API Platform - Swagger UI with JWT Authentication - symfony

I'd like to add the "Authorize" button on Swagger, like described here : https://api-platform.com/docs/core/jwt#documenting-the-authentication-mechanism-with-swaggeropen-api
I installed LexikJWTAuthenticationBundle, it works fine with Curl. But when I browse to http://localhost:8000/api, I only see {"code":401,"message":"JWT Token not found"}.
Am I missing something?
Here's my security.yaml:
security:
encoders:
App\Entity\User:
algorithm: bcrypt
providers:
db_provider:
entity:
class: App\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_login:
pattern: ^/api/login
stateless: true
anonymous: true
form_login:
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
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
pattern: ^/
anonymous: ~
provider: db_provider
form_login:
login_path: app_security_login
check_path: app_security_login
csrf_token_generator: security.csrf.token_manager
logout:
path: /logout
target: /
remember_me:
secret: '%kernel.secret%'
lifetime: 604800
path: /
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
access_decision_manager:
strategy: unanimous
And my api_platform.yaml:
api_platform:
title: 'My project'
version: '0.0.1'
mapping:
paths: ['%kernel.project_dir%/src/Entity']
swagger:
api_keys:
apiKey:
name: Authorization
type: header

Bit late to this, but I faced this same issue. Your security configuration is stating that any route beginning with /api requires authentication, which includes /api itself. If you want to keep the documentation on the /api route, add a trailing slash to the security configuration;
firewalls:
...
api:
pattern: ^/api/
and
access_control:
- { path: ^/api/, roles: IS_AUTHENTICATED_FULLY }
This will leave /api as publicly accessible, whilst requiring a valid token to be provided for /api/*.
Alternatively, you can leave the security configuration as it is and move the documentation to a different URL (e.g. /docs). For this, you may need to add /docs as an IS_AUTHENTICATED_ANONYMOUSLY path under access_control depending on your other rules.
Then when the documentation page is accessible, click the Authorize button at the top of the page and enter Bearer <valid JWT token>.

It's old but here is the solution in 2021
You have to decorate open api factory
Edit :
Don't put hyphen (-) in the name of the authorization and in the scheme key otherwise it won't work (probably other special characters)
In config/packages/api_platform.yaml :
swagger:
versions: [3]
api_keys:
JWT: // The name of the authorization to display on swagger UI
name: Authorization
type: header
In config/services.yaml :
App\OpenApi\JwtDecorator:
decorates: 'api_platform.openapi.factory'
arguments: [ '#App\OpenApi\JwtDecorator.inner' ]
autoconfigure: false
enter code here
The decorating service : in OpenApi\JwtDecorator
class JwtDecorator implements OpenApiFactoryInterface
{
public function __construct(
private OpenApiFactoryInterface $decorated
) {}
public function __invoke(array $context = []): \ApiPlatform\Core\OpenApi\OpenApi
{
$openApi = ($this->decorated)($context);
$schemas = $openApi->getComponents()->getSecuritySchemes();
$schemas['JWT'] = new \ArrayObject([
'type' => 'http',
'scheme' => 'bearer',
'bearerFormat' => 'JWT'
]);
return $openApi;
}
}

I could fix my problem. In my case problem was, in public and private openssl keys. First I fixed it, the second, problem was in encoding. I used the source below to achieve result. Just take a look to link below
LexikJWTAuthenticationBundleSandbox

Related

Symfony security: Access control rules break after Symfony 6.1 update

I recently upgraded a Symfony 5.4 project to 6.1. After the upgrade I can't open the start page anymore. The /login page and logging in with a user still works.
The error shown on any other page is pretty cryptic:
ErrorException: Warning: preg_match(): Compilation failed: unmatched
closing parenthesis at offset 2
at vendor/symfony/http-foundation/RequestMatcher.php:165
at Symfony\Component\HttpFoundation\RequestMatcher->matches(object(Request))
(vendor/symfony/security-http/AccessMap.php:42)
If I comment out all access_control in security.yaml I can navigate around the page again, so I assume the error is somewhere in those rules. I have looked at https://symfony.com/doc/current/security/access_control.html but can't figure out why my config is broken. Is this the source of the error message?
This is my security.yaml:
security:
enable_authenticator_manager: true
password_hashers:
App\Entity\User:
algorithm: auto
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: app_user_provider
custom_authenticator: App\Security\LoginFormAuthenticator
logout:
path: security_logout
target: security_login
form_login:
check_path: security_login
login_path: security_login
enable_csrf: true
default_target_path: default_index
entry_point: form_login
access_control:
- { path: ^/login$, roles: PUBLIC_ACCESS }
- { path: ^/registration, roles: PUBLIC_ACCESS }
- { path: ^/resetting, roles: PUBLIC_ACCESS }
- { path: ^/locale, roles: PUBLIC_ACCESS }
- { path: ^/_error, roles: PUBLIC_ACCESS }
- { path: ^/), roles: ROLE_USER }
The error message basically tells you what's wrong.
Your last access_control rule uses a closing parenthesis ")". In Regex this is a reserved character. If this is not a mistake and you actually have routes starting with a closing parenthesis, then you will have to escape the character like so:
- { path: ^/\), roles: ROLE_USER }

symfony: granted access to everyone only to GET method in access_control on API but still get 401 unauthorised if user do not have a token

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.

Using symfony LoginFormAuthenticator with lexik_jwt_authentication to handle admin user and api token

I want to create an admin user to handle back-end and to use jwt token to protect my API routes.
In my security.yaml file i add a main firewall to handle login form authentication :
main:
anonymous: lazy
provider: app_user_provider
guard:
authenticators:
- App\Security\LoginFormAuthenticator
logout:
path: app_logout
and i config access control :
- { path: '^/admin', roles: ROLE_ADMIN }
- { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Now, if i use login form, i can sign in as an admin :
After sign in i can access to the admin controller.
I added jwt lexik bundle and i added config to security.yaml :
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
anonymous: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
And i added config to api_platform.yaml:
api_platform:
mapping:
paths: ['%kernel.project_dir%/src/Entity']
patch_formats:
json: ['application/merge-patch+json']
swagger:
versions: [3]
api_keys:
apiKey:
name: Authorization
type: header
title: API partage de mandats
version: 1.0.0
collection:
pagination:
client_enabled: true
enabled_parameter_name: pagination # optional
eager_loading:
enabled: false
I want to make public access to my api, and prevent testing api without token :
My api look like this :
The problem is : i can got ressource event if i'm not authenticated :
My current acces control is :
How can i add config to security.yaml to prevent public access to api ressources ?
Could, you just add this line to your access_control:
security:
# ...
access_control:
- { path: '^/api/mandates', roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: '^/api', roles: ROLE_USER }
I fix the problem.
All i need to do is to add a slash to my route in the firewall :
api:
pattern: ^/api/
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
Now everything work like i want .

Symfony3 FOSOAuthServerBundle: Invalid grant_type parameter or parameter missing

I am trying build a rest api on Symfony3 and using a combination of FOSOAuthServerBundle , JMSerializer and FOSUserBundle.
I have the configurations set up to the best of my knowledge i however cannot get the api to return a token.
it in fact always returns
{"error":"invalid_request","error_description":"Invalid grant_type parameter or parameter missing"}
despite providing the grant_type key and value
so i did a bunch of digging and tried suggestions on this FOSOAuthServerBundle: Invalid grant_type parameter or parameter missing and also this FOSOAuthServerBundle + FOSRestBundle + CamelCase = Not authenticating i still could not get it to work.
from my findings i probably need to to disable the body listener which converts underscored keys to camel in either FOSRestBundle or the JMSerializer Bundle
i however have no clue on how to do either
I would appreciate pointers in the right direction on how to resolve the issue
my config.yml foss section looks like this
# FOSRest Configuration
fos_rest:
body_listener: true
zone:
- { path: ^/api }
allowed_methods_listener: true
format_listener:
rules:
- { path: '^/api', priorities: ['json'], fallback_format: json, prefer_extension: false }
- { path: '^/', priorities: [ 'text/html', '*/*'], fallback_format: html, prefer_extension: true }
param_fetcher_listener: true
view:
view_response_listener: 'force'
formats:
json: true
security.yml look like this the
security:
encoders:
UserBundle\Entity\User: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
in_memory:
memory: ~
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
authenticate
pattern: ^/
form_login:
provider: fos_userbundle
#csrf_provider: security.csrf.token_manager
logout: true
anonymous: true
#auth api stuff
oauth_token:
pattern: ^/oauth/v2/token
security: false
api_doc:
pattern: ^/api/doc
security: false
api:
pattern: ^/api
fos_oauth: true
stateless: true
#end
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 }
#api
- { path: ^/api, roles: [ IS_AUTHENTICATED_FULLY ] }

JWT authentification with Symfony 2.7

I'm trying to use LexikJWTAuthenticationBundle with FOSUserBundle on my Symfony 2.7 application.
Basically, the FOSUserBundle config works fine: I properly load my fixtures and if I try to login via the login form it succeeds.
Getting the token from the login_check also works. Then I put the Authorization header with "Bearer " in the header and try to access to another page. It always result with a 401 response.
Has anyone any leads about what could be the problem or how to proceed to debug?
Here's my config:
# app/config/config.yml
# DunglasJsonLdApi
dunglas_json_ld_api:
title: %api_name%
description: %api_description%
default:
order: DESC
# FOSUserBundle
fos_user:
db_driver: orm
firewall_name: api
user_class: ApiBundle\Bundles\UserBundle\Entity\User
# LewikJWTAuthentificationBundle
lexik_jwt_authentication:
private_key_path: %kernel.root_dir%/config/jwt/private.pem
public_key_path: %kernel.root_dir%/config/jwt/public.pem
pass_phrase: %jwt_pass_phrase%
token_ttl: 86400
# app/config/routing.yml
# DunglasJsonLdBundle
api_doc:
resource: #DunglasJsonLdApiBundle/Resources/config/routing.xml
prefix: /api
api:
resource: .
type: json-ld
prefix: /api
# FOSUserBundle
fos_user_security_login:
path: /login
defaults: { _controller: FOSUserBundle:Security:login }
fos_user_security_check:
path: /api/login_check
defaults: { _controller: FOSUserBundle:Security:check }
fos_user_security_logout:
path: /logout
defaults: { _controller: FOSUserBundle:Security:logout }
# app/config/security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_CA: ROLE_USER
ROLE_SUPER_ADMIN: [ ROLE_CA, ROLE_ALLOWED_TO_SWITCH ]
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
dev:
pattern: ^/(_(profiler|wdt|error)|css|images|js)/
security: false
login:
pattern: ^/login|^/api/login
provider: fos_userbundle
stateless: true
anonymous: true
form_login:
login_path: fos_user_security_login
check_path: fos_user_security_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
provider: fos_userbundle
stateless: true
anonymous: true
lexik_jwt:
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: IS_AUTHENTICATED_FULLY }
Complete application available here.
It seems it works fine now. I did not change anything since the time I posted my question and before posting I rebooted my machine, restarted MySQL, nginx, PHP5-FPM and deleted Symfony cache, so I really fail to understand what happen...

Resources