Symfony 6.3 Login Programmatically - using lexik_jwt_authentication

244 views Asked by At

I succeed to fetch the Linkedin users Data, Create a User object and inject User details in it, Persist it into the DB, and even Generate a JWT token: $jwtToken = $this->jwtManager->create($userEntity);

But it seems that the token is no "active"

{
    "code": 401,
    "message": "Invalid credentials."
}

When I use directly the /api/login_check from Security it works:

{
    "message": "Welcome to your new controller!",
    "path": "src/Controller/DashboardController.php"
} 

I also tried this: return $security->login($userEntity, 'json_login', 'login'); Where "login" is the firewall used by JWT and 'json_login' the authenticator(See security). https://symfony.com/doc/current/security.html#login-programmatically

I also tried this with no success: return $authenticationSuccessHandler->handleAuthenticationSuccess($user); https://github.com/lexik/LexikJWTAuthenticationBundle/issues/1113

$userEntity is containing the right User and the JWT Token is containing all the details.

Now the JSon answer is containing the JWT Token, but is invalid when I put it in the Bearer Token.

Can you please help me? Thank you!

** Bundles Installed:**

"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"doctrine/doctrine-bundle": "^2.10",
"doctrine/doctrine-migrations-bundle": "^3.2",
"doctrine/orm": "^2.16",
"friendsofsymfony/rest-bundle": "^3.6",
"jms/serializer-bundle": "^5.3",
"league/oauth2-linkedin": "^5.1",
"lexik/jwt-authentication-bundle": "*",
"symfony/console": "6.3.*",
"symfony/dotenv": "6.3.*",
"symfony/flex": "^2",
"symfony/framework-bundle": "6.3.*",
"symfony/runtime": "6.3.*",
"symfony/yaml": "6.3.*"

Security:

security:
    enable_authenticator_manager: true
    # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
    password_hashers:
        App\Entity\User: 'auto'
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
            algorithm: 'auto'
            cost:      15
    # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    firewalls:
        login:
            pattern: ^/api/login
            stateless: 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
            jwt: ~
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            lazy: true
            provider: app_user_provider

    # 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: ^/api/register, roles: PUBLIC_ACCESS  }
        - { path: ^/api/login, roles: PUBLIC_ACCESS  }
        - { path: ^/api/auth/linkedin, roles: PUBLIC_ACCESS  }
        - { path: ^/api/li-callback, roles: PUBLIC_ACCESS  }
        - { path: ^/api,       roles: IS_AUTHENTICATED_FULLY }


            # activate different ways to authenticate
            # https://symfony.com/doc/current/security.html#the-firewall

            # https://symfony.com/doc/current/security/impersonating_user.html
            # switch_user: true

when@test:
    security:
        password_hashers:
            # By default, password hashers are resource intensive and take time. This is
            # important to generate secure password hashes. In tests however, secure hashes
            # are not important, waste resources and increase test times. The following
            # reduces the work factor to the lowest possible values.
            Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
                algorithm: auto
                cost: 4 # Lowest possible value for bcrypt
                time_cost: 3 # Lowest possible value for argon
                memory_cost: 10 # Lowest possible value for argon

Controler:

<?php

// src/Controller/LinkedinAuthController.php

namespace App\Controller;

use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use App\Service\LinkedinAuthService;
use App\Service\UserManager;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use League\OAuth2\Client\Provider\Linkedin;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Bundle\SecurityBundle\Security;

class LinkedinAuthController extends AbstractController
{
    private $linkedinAuthService;
    private $entityManager;
    private $jwtManager;
    private $authenticationSuccessHandler;

    public function __construct(LinkedinAuthService $linkedinAuthService, EntityManagerInterface $entityManager, JWTTokenManagerInterface $jwtManager)
    {
        $this->linkedinAuthService = $linkedinAuthService;
        $this->entityManager = $entityManager;
        $this->jwtManager = $jwtManager;
    }

    /**
     * @Route("/api/auth/linkedin", name="api_auth_linkedin")
     */
    public function redirectToLinkedin(): Response
    // public function redirectToLinkedin(): JsonResponse
    {...}

    /**
     * @Route("/api/li-callback", name="api_linkedin_callback")
     */
    public function linkedinCallback(Request $request, Security $security): JsonResponse
    {
        session_start();

        if (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) {
            unset($_SESSION['oauth2state']);
            return new JsonResponse(['error' => 'Invalid state'], 401);
        }
       
        try {
            $linkedinUser = $this->linkedinAuthService->getUserData($_GET['code']);

            $userEntity = $this->findOrCreateUser($linkedinUser);
            
            // Generate JWT Token
            // $jwtToken = $this->jwtManager->create($userEntity);            
        
            // you can also log in on a different firewall
            return $security->login($userEntity, 'json_login', 'login');
            
        } catch (IdentityProviderException $e) {
            return new JsonResponse(['error' => 'Authentication failed: '.$e->getMessage()], 401);
        }
    }
0

There are 0 answers