I have two entities User and Merch.
A User uses a form and authenticates with his username and password.
A Merch uses an iPad app and authenticates with their merchCode (integer eg:11) and password.
With JWT Authentification I can generate a token for User.
The problem is I don't know what do to generate a token for Merch, too.
I want to use two different paths:
/api/login_check_user
/api/login_check_merch
for Merch I want to return a Response contain token + marchId
my security.yaml
security:
encoders:
App\Entity\User:
algorithm: argon2i
App\Entity\Merch:
algorithm: auto
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
# used to reload user from session & other features (e.g. switch_user)
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
check_path: /api/login_check_user
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
refresh:
pattern: ^/api/token/refresh
stateless: true
anonymous: true
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
anonymous: true
# access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/validator, roles: ROLE_VALIDATOR }
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
How can I solve this?
With Lexik, we can override the response with events:
https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Resources/doc/2-data-customization.md#eventsjwt_created---adding-custom-data-or-headers-to-the-jwt
So you sould have only one firewall
Related
I have a project with Symfony 5.1 using Lexik JWT v2.8.0, gesdinet jwt refresh token v0.9.1 and my own entity user. I can log in with JWT and get the token, save it in a HttpOnly cookie and use it with the protected APIs successfully.
I have implemented CRUD API with the route /api/user/ and I don't want non logged in users to be able to update or delete users, so I need to grant access to non logged in and logged in users to create and read users.
I used a different route to create users /api/register and added it to the access_control with IS_AUTHENTICATED_ANONYMOUSLY, so it's working fine with non logged in users.
But to get a users list I use the /api/user/ route with GET method as the route is shared with PUT and DELETE methods, I only want the GET method to be accesible.
my access_control in security.yaml is:
access_control:
- { path: ^/api/user, roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [GET] }
- { path: ^/api/linkpage, roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [GET] }
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
But when I try to get the users without a token, I get a 401 unauthorised message, I can only have the list of users if I am logged in and have a token.
PD: I have the same problem with the /api/linkpage route with GET method if I need a list of linkpages and I don't have a token.
edit: this is the full security.yaml:
security:
encoders:
UserBundle\Domain\Entity\User:
algorithm: auto
providers:
app_user_provider:
entity:
class: UserBundle\Domain\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
provider: app_user_provider
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
refresh:
pattern: ^/api/token/refresh
stateless: true
anonymous: true
register:
pattern: ^/api/register
stateless: true
anonymous: true
api:
pattern: ^/api
stateless: true
provider: app_user_provider
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
anonymous: true
lazy: true
provider: app_user_provider
access_control:
- { path: ^/api/user, roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [GET] }
- { path: ^/api/linkpage, roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [GET] }
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
The issue is right here
api:
pattern: ^/api
stateless: true
provider: app_user_provider
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
You can add anonymous: true as you're protected by
access_control:
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
And you're explicitly listing the other routes accessible by non authenticated users.
As the title suggests, I'm going to use Facebook,Google and GitHub authentication alongside JWT authenticator (LexikJWT).
Before starting, I want to know how can I use them? is it possible to use both of them to protect APIs?
If yes, what sort of configurations should my security have? Assuming that I'm using the default configurations.
here is the current security.yml:
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
acl:
connection: default
access_decision_manager:
strategy: affirmative
role_hierarchy:
ROLE_SALES_NOTIFICATIONS: [ ROLE_SALES_NOTIFICATIONS ]
# FULL CONTROL
ROLE_ADMIN: [ROLE_USER, ROLE_SONATA_ADMIN]
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
# Disabling the security for the web debug toolbar, the profiler and Assetic.
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
# -> custom firewall for the admin area of the URL
admin:
pattern: /admin(.*)
context: user
form_login:
provider: fos_userbundle
login_path: /admin/login
use_forward: false
check_path: /admin/login_check
failure_path: null
success_handler: admin_success_handler
logout:
path: /admin/logout
anonymous: true
# Custom firewall for api area
api_login:
pattern: ^/api/auth
stateless: true
anonymous: true
provider: fos_userbundle
form_login:
check_path: /api/auth/check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
api:
pattern: ^/api/v\d+\.\d+/
methods: [ POST, PUT ]
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
api_doc:
pattern: ^/api/doc
stateless: true
anonymous: true
# -> end custom configuration
# default login area for standard users
# This firewall is used to handle the public login area
# This part is handled by the FOS User Bundle
main:
pattern: .*
context: user
form_login:
provider: fos_userbundle
login_path: /login
use_forward: false
check_path: /login_check
failure_path: null
logout: true
anonymous: true
access_control:
# URL of FOSUserBundle which need to be available to anonymous users
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
# Admin login page needs to be access without credential
- { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
# 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: ^/assets/, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/uploads/, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
- { path: ^/user/, role: [ROLE_USER] }
- { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/auth, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/auth/me, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/api/v\d+\.\d+/, roles: IS_AUTHENTICATED_FULLY }
I'm thinking, using both of them as a security provider will lead to errors. is it right?
I don't think using HWIOAuthBundle and LexikJWTBundle on the same project will lead to errors.
You api_login firewall will authenticate your api users while your admin firewall will authenticate your backoffice users.
Since your URL patterns are correctly congigured you should not encounter problems.
I want to create a oauth2 server for my websites (3 websites, 1 login). I created the server, I used FOSUserBundle and FOSOAuthServerBundle, I followed the instructions.
But I have 1 problem. I can create token through /oauth/v2/token this is fine.
I can go to /oauth/v2/oauth, but when I'm redirected to the website, I have examle.com?error=invalid_request&error_description=Invalid+response+type.
And if I login at /login, I will be logged in, but no token is created. This would be fine if I only had 1 website, but I would like to make something more like Google (if you are on maps, youtube... if you want to log in, you are redirected to accounts.google.com, login, then back to the website), but I can't see how to do that right now. I guess I need to do more work, but where exactly ?
My security.yml right now:
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
logout: true
anonymous: true
oauth_token:
pattern: ^/oauth/v2/token
security: false
oauth_authorize:
pattern: ^/oauth/v2/auth
anonymous: true
api:
pattern: ^/api
fos_oauth: true
stateless: true
anonymous: false # can be omitted as its default value
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
- { path: ^/api, roles: [ IS_AUTHENTICATED_FULLY ] }
Should I create a new login page, or user provider? Thanks :)
You didn't specify a response type. You should use this request :
PROVIDER_HOST/oauth/v2/auth?client_id=CLIENT_ID&response_type=code&redirect_uri=CLIENT_HOST
Then get access with code :
CLIENT_HOST/?code=Yjk2MWU5YjVhODBiN2I0ZDRkYmQ1OGM0NGY4MmUyOGM2NDQ2MmY2ZDg2YjUxYjRiMzAwZTY2MDQxZmUzODg2YQ
Then ask for the token :
PROVIDER_HOST/oauth/v2/token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fclinet.local%2F&code=CODE
More info here : http://blog.tankist.de/blog/2013/07/18/oauth2-explained-part-3-using-oauth2-with-your-bare-hands/
I'm trying to use FOSOAuthServerBundle.
From my ios application, I correctly get the token from /oauth/v2/token, I can see in my database the entry in AccessToken and RefreshToken with the correct user_id.
Opening the _profile, I can see I'm authenticated but I'm logged in as anonymous... why this is happening?
When trying to access /secured/api/me, I'm redirected to /login path...
Can somebody help me?
Here my security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_USER
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
oauth_token:
pattern: ^/oauth/v2/token
security: false
oauth_authorize:
pattern: ^/oauth/v2/auth
# form_login:
# provider: fos_userbundle
# check_path: /oauth/v2/auth_login_check
# login_path: /oauth/v2/auth_login
anonymous: true
api:
pattern: ^/api
fos_oauth: true
stateless: true
anonymous: true
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
login_path: /login
check_path: /login_check
oauth:
resource_owners:
facebook: "/login/check-facebook"
google: "/login/check-google"
login_path: /login
use_forward: false
failure_path: /login
oauth_user_provider:
#this is my custom user provider, created from FOSUBUserProvider - will manage the
#automatic user registration on your site, with data from the provider (facebook. google, etc.)
service: my_user_provider
logout: true
anonymous: true
login:
pattern: ^/login$
security: false
remember_me:
key: "%secret%"
lifetime: 31536000 # 365 days in seconds
path: /
domain: ~ # Defaults to the current domain from $_SERVER
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
- { path: ^/oauth/v2/auth, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/secured, role: [ IS_AUTHENTICATED_FULLY ] }
.
I think you have in your security.yml, under the firewall 'api ' :
//...
api :
// ...
stateless : true
// ...
You have to send the access_token on every request.
Furthermore, if you want to get an authenticated access_token, you have to get it by a request with de parameter "grant_type=password".
With this access_token, your server will recognize the user in each request.
Something like:
PROVIDER_HOST/oauth/v2/token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=password&username=USERNAME&password=PASSWORD
(source: OAuth2 Explained: Part 3 - Using OAuth2 With Your Bare Hands)
Im trying to create an oauth2 server based on FOSOauthServerBundle, FOSRestBundle and FOSUserBundle. I created a demo application to test the my oauth-server and it failed receiving the data via the GET reguest
(received 401 error ' error="access_denied", error_description="OAuth2
authentication required" '),
in spite the fact that the user was authenticated and the client received an access token properly.
How should I implement the api controllers so the oauth2 will execute the authentication process?
Also, i would like to take a look on real working oauth server example based on those bundles so i would be able to check my application on it.
my security.yml:
jms_security_extra:
secure_all_services: false
expressions: true
security:
acl:
connection: default
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
in_memory:
memory:
users:
user: { password: userpass, roles: [ 'ROLE_USER' ] }
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
fos_userbundle:
id: fos_user.user_provider.username
encoders:
FOS\UserBundle\Model\UserInterface: sha512
Symfony\Component\Security\Core\User\User: plaintext
firewalls:
api:
pattern: ^/api
fos_oauth: true
stateless: true
oauth_authorize:
pattern: ^/oauth/v2/auth
form_login:
provider: fos_userbundle
check_path: /oauth/v2/auth_login_check
login_path: /oauth/v2/auth_login
use_referer: true
anonymous: true
oauth_token:
pattern: ^/oauth/v2/token
security: false
secured_area:
pattern: ^/
anonymous: ~
form_login:
provider: fos_userbundle
check_path: /login_check
login_path: /login
always_use_default_target_path: true
default_target_path: /
access_control:
- { path: ^/oauth/v2/auth_login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/oauth/v2/auth, role: ROLE_USER }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY}
- { path: ^/, roles: ROLE_USER }
- { path: ^/api, roles: [ IS_AUTHENTICATED_FULLY ] }
Thanks.
Submitting the answer so this will close the open question.
Access denied caused because the request did not contain the access token. Refer to documentation with section titled Creating A Client and Usage.
https://github.com/FriendsOfSymfony/FOSOAuthServerBundle/blob/master/Resources/doc/index.md