Symfony 6 - lexik JWT - Authenticator does not support the request - symfony

I'm having some problems with lexik JWT bundle and Symfony 6.0, for swagger I use NelmioApiDocBundle.
The thing is, that every in swagger works before I decide to apply my Authorization Token (Bearer token), which is generated from lexik JWT. But once I get my token generated through /api/sign/in endpoint, and put it into the field, suddenly all endpoints stop working. Like the swagger has this loading animation, but no request comes (tested with xDebug, but also symfony profiler).
Funny thing is when I use Postman and apply the token there, I immediately get a correct response. So I'm not sure where or what's the problem, but when calling from Swagger, I can see docker debug messages saying: PHP message: [debug] Authenticator does not support the request.
I will put my configuration below. Thanks in advance.
lexik_jwt_authentication.yaml:
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 8640000
user_id_claim: id
user_identity_field: email
security.yaml
security:
enable_authenticator_manager: true
password_hashers:
App\Entity\User:
algorithm: bcrypt
cost: 10
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/sign
stateless: true
provider: app_user_provider
json_login:
check_path: /api/sign/in
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
provider: app_user_provider
pattern: ^/api
stateless: true
jwt: ~
access_control:
- { path: ^/api/sign/, roles: PUBLIC_ACCESS }
- { path: ^/api/(doc|doc.json), roles: PUBLIC_ACCESS }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
nelmio_api_doc.yaml
nelmio_api_doc:
documentation:
servers:
- url: http://bp.project
info:
title: BP PROJECT
description: This is an awesome app!
version: 1.0.0
components:
securitySchemes:
Bearer:
type: http
scheme: bearer
bearerFormat: JWT
security:
Bearer: [ ]
areas: # to filter documented areas
path_patterns: # an array of regexps
- ^/api(?!/(doc|doc.json|docs.{_format}|{index}.{_format}|contexts/{shortName}.{_format})$) # Accepts routes under /api except ...
models: { use_jms: false }

Found out api_platform swagger and nelmio are both interacting somehow, since I added this into api_platform.yaml and the header was available in nelmio too, which now works.
swagger:
versions: [3]
api_keys:
apiKey:
name: Authorization
type: header

Related

AuthenticationException A Token was not found in the TokenStorage?

I've upgraded to Symfony 5, I faced an issue with security I am not able to open the login page. when I access the public folder from localhost it's redirecting me to
.../public/login with 404 header
I am using PHP 8.0.8 on MAMP pro mac version.
Did I miss something in security.yaml?
framework.yaml
# see https://symfony.com/doc/current/reference/configuration/framework.html
framework:
secret: '%env(APP_SECRET)%'
#csrf_protection: true
#http_method_override: true
# Enables session support. Note that the session will ONLY be started if you read or write from it.
# Remove or comment this section to explicitly disable session support.
session:
handler_id: 'session.handler.native_file'
save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'
cookie_secure: true
cookie_samesite: 'none'
# cookie_secure: auto
# cookie_samesite: lax
#esi: true
#fragments: true
php_errors:
log: true
security.yaml
security:
encoders:
App\Entity\User:
algorithm: auto
# 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: false
lazy: false
provider: app_user_provider
guard:
authenticators:
- App\Security\LoginFormAuthenticator
logout:
path: app_logout
# where to redirect after logout
# target: app_any_route
# 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: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
- { path: ^/efconnect, role: ROLE_USER }
- { path: ^/elfinder, role: ROLE_USER }
The error log :
[2021-08-21T09:44:38.264832+00:00] request.INFO: Matched route "app_process_process_show". {"route":"app_process_process_show","route_parameters":{"_route":"app_process_process_show","_controller":"App\\Controller\\ProcessBundle\\ProcessController::show"},"request_uri":"http://localhost:8888/site/public/","method":"GET"} []
[2021-08-21T09:44:38.270837+00:00] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"main","authenticators":1} []
[2021-08-21T09:44:38.270998+00:00] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"main","authenticator":"App\\Security\\LoginFormAuthenticator"} []
[2021-08-21T09:44:38.271146+00:00] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"main","authenticator":"App\\Security\\LoginFormAuthenticator"} []
[2021-08-21T09:44:38.289628+00:00] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationCredentialsNotFoundException(code: 0): A Token was not found in the TokenStorage. at /Users/xx/Documents/Sites/site/vendor/symfony/security-http/Firewall/AccessListener.php:70)"} []
[2021-08-21T09:44:38.289822+00:00] security.DEBUG: Calling Authentication entry point. [] []
Your config says that you don't accept anonymous users. Your only Guard does not support the current request. So since unauthorised users are not allowed and we cannot authorise the request, this exception is thrown.
Solve it by doing anonymous: true.

How to secure app and be able to use Symfony Auth credentials in Symfony + VueJs SPA

I've implemented an api-platform app with Symfony4 + GraphQl + axios + vuejs; all this is working as a SPA which uses a single controller action to render a template and from there all the router is handled by vue-router. So far so good.
When I'm about to secure the app(regular symfony way) all is working fine, here is the config
security:
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]
encoders:
Symfony\Component\Security\Core\User\User: plaintext
App\Entity\User:
algorithm: bcrypt
providers:
in_memory:
memory:
users:
administrador: { password: p#ssw0rd, roles: [ 'ROLE_USER' ] }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
tempapi: #this should not be configured
pattern: ^/api
security: false
memory:
pattern: ^/
logout: true
anonymous: true
provider: in_memory
form_login:
login_path: /login
check_path: /login
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
I think everything is well configured but when I comment the tempapi firewall and make login I'm not able to access the api, all I get is a redirection to the login page. Notice that I'm accessig the api from a view behind the firewall, so I expected to behave as a regular ajax request(because it is).
When I remove the comment and left the tempapi firewall without security I'm able to access the api but I cannot have access to the logged user credentials, roles, etc.
apollo configuration to use graphql endpoint
const httpLink = new HttpLink({
uri: '/api/graphql'
})
const cache = new InMemoryCache()
export default new ApolloClient({
link: httpLink,
cache,
connectToDevTools: true
})
axios configuration to access rest endpoint
export const backend = axios.create({
baseURL: '/api',
headers: {
'Content-Type': 'application/json'
}
})
Why is symfony dealing with async requests made behind an authenticated firewall as they were externals?. I don't have problems with CORS
Thanks in advance!

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 FOSOAuthServerBundle: access token not detected

I'm using FOSOAuthServerBundle as my oauth endpoint. I succesfully generated a token using the Resource Owner Password Credentials grant method:
{
"access_token": "MY-FOO-TOKEN",
"expires_in": ​3600,
"token_type": "bearer",
"scope": "read",
"refresh_token": "MY-BAR-REFRESH-TOKEN"
}
Now I would like to use it to get some protected resources. So I did:
curl -X GET -H "Authorization: Bearer MY-FOO-TOKEN" "http://localhost:8000/api/a-bar-resource"
The Bearer do not seem to be detected.
INFOS:
echo $this->get('security.token_storage')->getToken(); gives:
AnonymousToken(user="anon.", authenticated=true, roles="")
In the headers there is:
["authorization"]=> /** <-- Is the lowercase OK? **/
array(1) {
[0]=>
string(93) "Bearer MY-FOO-TOKEN"
}
I also tried to pass access_token as a query parameter, without success.
Now I'm guessing something is wrong with the config.yml or the security.yml. Here are some selected parts:
config.yml:
fos_oauth_server:
[...]
service:
options:
supported_scopes: read
user_provider: fos_user.user_provider.username_email
security.yml:
security:
[...]
firewalls:
api:
pattern: ^/api
fos_oauth: true
stateless: true
anonymous: false
access_control:
- { path: ^/api, roles: [ IS_AUTHENTICATED_ANONYMOUSLY ] }
I finally found what was causing the problem. I had a firewall that was nullifying the other firewall, because of same patterns:
security:
[...]
firewalls:
other_firewall: #this one
pattern: ^/
anonymous: true
api:
pattern: ^/api
fos_oauth: true
stateless: true
anonymous: true
I also think that an authentication made in one firewall is not available in an other firewall, so diminishing the number of firewalls to the strict minimum (one not secured, one with auth etc.) is a good practice. Then using access_control for fine access.

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