Symfony: Can I use both JWT + oAuth to authenticate different users? - symfony

Before explain why what I need your help, just keep in mind that I am totally new on Symfony 3.4, so please be lenient with me.
What I try to create is a RESTful API.
For the admin section, I'd like my users to log in every day in order to access the application dashboard. I already do that, by using the LexikJWTAuthenticationBundle with a token that expires every 24 hours.
Then, my API will have several client applications that should access my data using an access token, but not using JWT. Those client applications will be mobile apps ( implemented on React Native ) and desktop apps ( implemented on Electron).
The way I thought I should authorize those applications to access my API is by providing them with an Authentication token using the oAuth protocol.
So, the basic question is, if I could config both authorization methods ( JWT + oAuth ) at the same time for the same resources.
So, for example, let's say I have the following endpoint: /api/v1/repository/chocolates.
With the jms_serializer I am able to choose what data I want to display on each group, so for a user has the ROLE_ADMIN I can display all the product information, while for the user has the ROLE_SOME_GRANT will only be possible to see the title and the description.
So is it possible to access the /api/v1/repository/chocolates either by using the JWT token my admins have or by using the oAuth token my client apps will have?
For the moment my app/config/security.yml has the following setup:
# To get started with security, check out the documentation:
# https://symfony.com/doc/current/security.html
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_NUTRITIONIST: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
# https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
oauth_token:
pattern: ^/oauth/v2/token
security: false
oauth_authorize:
pattern: ^/oauth/v2/auth
security: false
api_user_management:
pattern: ^/api/v1/user/(login|request\/reset|password\/reset)
anonymous: true
stateless: true
api_user_management_safe:
pattern: ^/api/v1/user/(login|request\/reset|password\/reset)
anonymous: true
stateless: true
api_user_login:
pattern: ^/api/v1/login
stateless: true
anonymous: true
form_login:
check_path: /api/v1/login
require_previous_session: false
username_parameter: username
password_parameter: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
logout: true
#############################################
## HERE IF I CHANGE THE fos_oauth to false
## I CAN ACCESS MY ENDPOINT : /api/v1/register/new/user
## WHILE IF THAT IS TRUE I GET THE ERROR MESSAGE:
##
## {
## "error": "invalid_grant",
## "error_description": "The access token provided is invalid."
## }
#############################################
api_access:
pattern: ^/api/v1
stateless: true
#lexik_jwt: ~
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
fos_oauth: true
access_control:
- { path: ^/api/v1/user/request/reset, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/v1/user/password/reset, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/v1/user/update, role: IS_AUTHENTICATED_FULLY }
- { path: ^/api/v1/user/((?!request)|(?!password)|(?!update)), role: IS_AUTHENTICATED_FULLY }
- { path: ^/api/v1/register/new/user, role: IS_AUTHENTICATED_FULLY }
- { path: ^/api/v1/, role: IS_AUTHENTICATED_FULLY }
So, based on that security setup do you think there's a way to have multiple authorization services in my application?

Related

redirection firewalls symfony 4

Hello I have an issue on login operation when for more explain , have 2 button :
sign in (with email and password)
login with gmail accout
when i click write email and password , click on sign in is redirect me to chose an gmail account but i want to login with email and password normaly
and this is security.yaml file
security.yaml
encoders:
App\Entity\User:
algorithm: argon2i
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
guard:
authenticators:
- App\Security\GoogleAuthenticator
- App\Security\LoginFormAuthenticator
entry_point: App\Security\LoginFormAuthenticator
logout:
path: logout
# activate different ways to authenticate
# http_basic: true
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
# form_login: true
# https://symfony.com/doc/current/security/form_login_setup.html
# 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: ^/admin, roles: ROLE_ADMIN }
- { path: ^/profile, roles: ROLE_USER }
- { path: ^/car, roles: ROLE_USER }
how resolve this problem

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.

Symfony access control with FOSRestBundle

I'm trying to make an authenticated section called /admin but putting in access_control has broken my API (nothing is returned). I don't need authentication for the API so I've used IS_ANONYMOUS as role. Here is my security.yml, what am I doing wrong?
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
providers:
in_memory:
memory: ~
fos_userbundle:
id: fos_user.user_provider.username # fos_user.user_provider.username_email does not seem to work (OAuth-spec related ("username + password") ?)
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
default:
anonymous: ~
http_basic: ~
oauth_token: # Everyone can access the access token URL.
pattern: ^/api/oauth/v2/token
security: false
api:
pattern: ^/api # All URLs are protected
fos_oauth: true # OAuth2 protected resource
stateless: true # Do no set session cookies
anonymous: true # Anonymous access is not allowed
security: false
access_control:
# require ROLE_ADMIN for /admin*
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/api, roles: IS_ANONYMOUS }

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.

Lexik JWT returns 401 Unauthorized

I am using LexikJWTBundle for a RESTful API.
Login works just fine and I get my token. But when I make a GET request I get a 401 with no content.
The Authorization header seems ok since I get this in the profiler:
Request Headers: authorization: Bearer {token}
Request Server Parameters: HTTP_AUTHORIZATION: Bearer {token}
The 401 I get is from: https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Security/Firewall/JWTListener.php#L80
I've tried different solutions but still it's not working.
Would you have any idea on how to resolve/debug this?
My config:
# config.yml
...
lexik_jwt_authentication:
private_key_path: %kernel.root_dir%/var/jwt/private.pem # ssh private key path
public_key_path: %kernel.root_dir%/var/jwt/public.pem # ssh public key path
pass_phrase: 'TEST' # ssh key pass phrase
token_ttl: 86400 # token ttl - defaults to 86400
And
# security.yml
security:
role_hierarchy:
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_SONATA_ADMIN, ROLE_ALLOWED_TO_SWITCH]
# http://sonata-project.org/bundles/admin/2-3/doc/reference/security.html
# set access_strategy to unanimous, else you may have unexpected behaviors
access_decision_manager:
strategy: unanimous
encoders:
FOS\UserBundle\Model\UserInterface: sha512
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_login:
pattern: ^/api/login # Default: .*
provider: fos_userbundle
# form login
form_login:
login_path: fos_user_security_login
# csrf_provider: form.csrf_provider # Default: my.csrf_provider.id
# LexikJWT # 09/01/15 - Note: provient de la configuration officielle.
check_path: api_login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
anonymous: true # Default: ~
api:
pattern: ^/api
stateless: true
lexik_jwt:
authorization_header: # check token in Authorization Header
enabled: true
prefix: Bearer
anonymous: true
access_control:
# Secured part of the site
# This config requires being logged for the whole site and having the admin role for the admin part.
# Change these rules to adapt them to your needs
- { path: "^/api/contacts$", roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [POST] }
- { path: "^/api/users/dt$", roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [GET] }
- { path: "^/api/users$", roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [POST] }
- { path: "^/api", roles: [IS_AUTHENTICATED_FULLY, ROLE_API] }
I just found a solution on the same problem
Here my security.yml
firewalls:
dev:
pattern: ^/(_(profiler|wdt|error)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
provider: user_db
form_login:
check_path: /api/login_check
username_parameter: _username
password_parameter: _password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
api:
pattern: ^/api
stateless: true
lexik_jwt:
authorization_header: # check token in Authorization Header
enabled: true
prefix: Bearer
throw_exceptions: false # When an authentication failure occurs, return a 401 response immediately
create_entry_point: true # When no authentication details are provided, create a default entry point that returns a 401 response
authentication_provider: lexik_jwt_authentication.security.authentication.provider
My problem was the username and the password parameters. I change "username" by "_username" and "password" by "_password"
You should add the ROLE_API in the role_hierarchy of your security.yml :
role_hierarchy:
# ...
ROLE_API: [ROLE_USER]
Then, users ranked with ROLE_API can access routes restricted to IS_AUTHENTICATED_FULLY.
Also, if you are using a web server, try to use your application using the built-in server (i.e. app/console server:run).
Apache seems to modify the token in headers.
LexikJWTBundle generates token so user's credentials are valid.
Problem occurs when you try to access secured routes (behind "^/api" path).
You should definitely check roles assigned to user. Maybe ROLE_API is missing and user is not fully authenticated.

Resources