Limit access to routes with the FOSOAuthServerBundle - symfony

I have created a REST API, i'm actually trying to secure it with the FOSOAuthServerBundle.
I created the route newclient to generate new client, now i would like to allow only the 'admin' to access to this url.
I guess it's possible to do this with scopes but i can't figure out how.
Here is my security.yml:
security:
providers:
user_provider:
id: user_provider
firewalls:
doc:
pattern: ^/doc
security: false
oauth_token:
pattern: ^/oauth/v2/token
security: false
oauth_authorize:
pattern: ^/oauth/v2/auth
provider: user_provider
anonymous: true
api:
pattern: ^/
fos_oauth: true
stateless: true
anonymous: false
access_control:
- { path: ^/, roles: ROLE_CLIENT }
- { path: ^/newclient, roles: ROLE_ADMIN }
My config.yml
fos_oauth_server:
db_driver: orm
client_class: WS\RESTBundle\Entity\Client
access_token_class: WS\RESTBundle\Entity\AccessToken
refresh_token_class: WS\RESTBundle\Entity\RefreshToken
auth_code_class: WS\RESTBundle\Entity\AuthCode
Any tips ?

Actually desling with scopes is not the best way to do it, plus the bundle does not support it : https://github.com/FriendsOfSymfony/FOSOAuthServerBundle/issues/231
I made use the User model, add a "role" method and check if the current user's role is enough to access the to the routes.
Here is the piece of code
//Get the current user, check if it's an admin
$token = $this->container->get('security.context')->getToken()->getToken();
$accessToken = $this->container
->get('fos_oauth_server.access_token_manager.default')
->findTokenBy(array('token' => $token));
$client = $accessToken->getClient();
if ($client->getRole() == 'admin') {
...
}
Not sure if it's the best way to do it, any advices welcome !

Related

Public Access with Symfony's AbstractAuthenticator

I'm exploring the new system for User Authentication using the new AbstractAuthenticator class in my symfony 5 project.
My symfony app will contain a mix of routes, some will only be accessible to authenticated users and some for unauthenticated users (public access)
My security.yaml file looks something like this:
security:
enable_authenticator_manager: true
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
custom_authenticators:
- App\Security\JwtAuthenticator
access_control:
- { path: ^/auth/login, roles: PUBLIC_ACCESS }
- { path: ^/auth, roles: ROLE_USER }
I have also setup a route to auth/login
However, when I access the url https://myapp.test/auth/login I get the following message:
{"message":"Invalid credentials."}
If I remove the custom authenticator directive from security.yaml the url loads as expected.
Below is the authenticate function from the Authenticator class:
public function authenticate(Request $request): PassportInterface
{
return new SelfValidatingPassport(new UserBadge($request->get('email));
}
if I access /auth/login with a valid user matching the email address provided and with the ROLE_USER role, the page will load as expected. If I access it without providing an email address, the page will return the following (from the onAuthenticationFailure method):
{"message":"Invalid credentials."}
If I understand correctly, as stated in Symfony docs, the PUBLIC_ACCESS should skip authenticating the user and load the /auth/login route, while everything else under /auth/ should be protected. But I cannot get the PUBLIC_ACCESS directive to work.
I resolved it by changing the location of custom_authenticators like that:
security:
enable_authenticator_manager: true
firewalls:
main:
json_login: # that is my login for rest api
provider: user_provider
check_path: api_login
api:
pattern: ^/api
custom_authenticators: # here is the location for my custom authenticator
- App\Security\Authenticator
stateless: true
access_control:
- { path: ^/login, roles: PUBLIC_ACCESS }
I hope it helps!

How to create JWT token in Symfony5?

I'm trying to create REST API with JWT authentification in Symfony5. At first I've tried to do as written here https://h-benkachoud.medium.com/symfony-rest-api-without-fosrestbundle-using-jwt-authentication-part-2-be394d0924dd . Difficulties appeared when I've tried to make method getTokenUser:
/**
* #param JWTTokenManagerInterface $JWTManager
* #return JsonResponse
* #Route("/api/login_check", name="api_login_check", methods={"POST"})
*/
public function getTokenUser(UserInterface $user,JWTTokenManagerInterface $JWTManager)
{
return new JsonResponse(['token' => $JWTManager->create($user)]);
}
Symfony says that UserInterface is not service so it can't Autowire it.
Ok, then I've tried to find another articles about this problem. But surprisingly they just doesn't say how to write this method. For example, here https://digitalfortress.tech/php/jwt-authentication-with-symfony/ appears like route /api/login_check must work authomatically if it configured in security.yaml and in routes.yaml. But no, it doesn't work.
So how must I write controller?
My security.yaml is:
security:
encoders:
App\Entity\User:
algorithm: bcrypt
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
provider: app_user_provider
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
it's true you do not need this method. You should refer to the documentation of the bundle you are using:
https://github.com/lexik/LexikJWTAuthenticationBundle/blob/2.x/Resources/doc/index.rst
In fact the login URL is handled by the security component of Symfony and rely on the configuration you set under json_login key :) .
Once again, it's detailed in the documentation (section "Usage").

Can I mark a single URI endpoint as anonymous?

I have read the docs and followed this similar question:
Allow anonymous access to specific URL in symfony firewall protected bundle
Using Symfony 4.1.4 I have tried the following:
access_control:
- { path: ^/rpi/service/application/quote/approve, roles: IS_AUTHENTICATED_ANONYMOUSLY}
- { path: ^/rpi, roles: ROLE_USER }
- { path: ^/erp, roles: ROLE_USER }
However when I access the first URI as anonymous I am prompted by the http_basic_ldap login screen. Any ideas?
You need
anonymous: true
in your firewall, as in the default configuration config/packages/security.yml:
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
in_memory: { memory: ~ }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
Anonymous authentication means that the user is authenticated and has a token, but it is an anonymous token.
If you do not have anonymous: true, the AnonymousAuthenticationListener will never run for your firewall, and never create an anonymous token.

How to set provider to oauth token

I've set up OAuth [ using FOSOauthServerBudle ]. I'm working on setting up a password type flow.
I can get an access token with client type, but using password type I get an error.
Error: Call to a member function loadUserByUsername() on a non-object
More specifically
$user = $this->userProvider->loadUserByUsername($username); <-OAuthStorage.php at line 161
/oauth/v2/token?client_id=[CLIENTID]&client_secret=[CLIENTSECRET]&grant_type=password&username=test&password=test
So there's no user provider set for OAuthStorage. I can't find any good documentation on how to set a provider for this.
My security conf
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
user_provider:
id: fos_user.user_provider.username_email
firewalls:
dev:
pattern: ^/(_(profiler|wdt|error)|css|images|js)/
security: false
oauth_token:
pattern: ^/oauth/v2/token
security: false
oauth_authorize:
pattern: ^/oauth/v2/auth
form_login:
provider: user_provider
check_path: _security_check
login_path: _demo_login
anonymous: true
api:
pattern: ^/api
fos_oauth: true
stateless: true
access_control:
# You can omit this if /api can be accessed both authenticated and anonymously
- { path: ^/api, roles: [ IS_AUTHENTICATED_FULLY ] }
What do you think guys. Any ideas?
Eureka!
So apparently the provider is configured in config.yml. I had it set to a previous custom user provider. I set it to the same provider as specified in security.yml and everything works just fine.

Symfony2 how to invalidate all the session attributes

In my Symfony2 project i have a logout button which redirect to the index page but when i click the Login button it connects directly without asking me for user and password.
how can i validate all session attributes, so if i login again it should ask me for user and password
this is my logout Action:
public function logoutAction(Request $request)
{
$this->get('security.context')->setToken(null);
$this->get('request')->getSession()->invalidate();
$url = $this->generateUrl('my_route');
$response = new RedirectResponse($url);
$response->headers->clearCookie('PHPSESSID');
$response->send();
return $response;
}
here's the security.yml:
security:
encoders:
Envivio\UserBundle\Entity\User: sha512
role_hierarchy:
ROLE_USER: ROLE_USER
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]
providers:
mine:
id: ib_user.oauth_user_provider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
pattern: ^/
oauth:
resource_owners:
salesforce: "/login/check-salesforce"
login_path: /login
#use_forward: false
failure_path: /login
default_target_path: /upload
oauth_user_provider:
service: ib_user.oauth_user_provider
remember_me:
key: "%secret%"
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, role: ROLE_ADMIN }
You can try :
$this->get('session')->clear();
or
$session = $this->get('session');
$ses_vars = $session->all();
foreach ($var as $key => $value) {
$session->remove($key);
}
It may also be a cookie problem check your cookies in the response->headers then try clearing it with that function :
http://api.symfony.com/2.3/Symfony/Component/HttpFoundation/ResponseHeaderBag.html#method_clearCookie
Edit :
Since you have remember me enabledyou need to clear that cookie too otherwise the next request you do will reauthenticate the user, you can specify a name for the cookie :
#security.yml
remember_me:
name: "%session.remember_me.name%"
add in parameters.yml the name of the cookie
parameters:
session.remember_me.name: EXTSESS
then in your controller logout action :
$response = $this->redirectToRoute('homepageroute'); //redirect prepare the route to redirect
$this->get('security.context')->setToken(null);
$this->get('request')->getSession()->invalidate();
$remember_sess_name = $this->container->getParameter('session.remember_me.name');
$response->headers->clearCookie($remember_sess_name);
return $response;
it should work now. if this doesn't then ALT+F4 :D
You don't need to do it manually. The Symfony2 firewall can perform a logout like this:
# app/config/security.yml
security:
firewalls:
secured_area:
# ...
logout:
path: /logout
target: /
# ...
Next, you'll need to create a route for this URL (but not a
controller):
# app/config/routing.yml
logout:
path: /logout
And that's it! By sending a user to /logout (or whatever you configure
the path to be), Symfony will un-authenticate the current user.
Once the user has been logged out, they will be redirected to whatever
path is defined by the target parameter above (e.g. the homepage).
Link to the related doc: http://symfony.com/doc/current/book/security.html#logout

Resources