Lexikjwtauthenticationbundle: Authenticate users only by username

Created on 6 Mar 2018  路  5Comments  路  Source: lexik/LexikJWTAuthenticationBundle

Hi,

I would like to know if it is possible to authenticate users without any password sent ?
In fact, my front end application calls an other webservice that send username to identify the computer.
And then I need to authenticate this username in my API.
The website doesn't plan to be public I just want a JWT token to track users.

Support

Most helpful comment

@scargoll while it might be a little bit late, here is what I ended up with.
Replace the form_login or json_login by a guard:

# security.yml

security:
# [...]
    firewalls:
        login:
            pattern: ^/give-me-a-jwt
            stateless: true
            anonymous: true
            guard:
                authenticators:
                    - App\Security\UsernameAuthenticator
        api:
            pattern: ^/
            stateless: true
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator
# [...]

Authentication handlers can be injected in a new dedicated authenticator:

# App\Security\UsernameAuthenticator

# [...]
use Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationFailureHandler;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationSuccessHandler;

class UsernameAuthenticator extends AbstractGuardAuthenticator
{
    private $successHandler;
    private $failureHandler;

    public function __construct(
        AuthenticationSuccessHandler $successHandler,
        AuthenticationFailureHandler $failureHandler
    ) {
        $this->successHandler = $successHandler;
        $this->failureHandler = $failureHandler;
    }

    public function supports(Request $request)
    {
        return true;
    }

    public function getCredentials(Request $request)
    {
        $data = json_decode($request->getContent(), true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new HttpException(400, 'Invalid json');
        }
        if (!isset($data['username'])) {
            throw new HttpException(400, 'Username is required');
        }

        return $data['username'];
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        if (null === $credentials) return null;
        return $userProvider->loadUserByUsername($credentials);
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        return true;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        return $this->failureHandler->onAuthenticationFailure($request, $exception);
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        return $this->successHandler->onAuthenticationSuccess($request, $token);
    }

    public function supportsRememberMe()
    {
        return false;
    }

    public function start(Request $request, AuthenticationException $authException = null)
    {
        return new Response('Auth header required', 401);
    }
}

The interface can be implemented any way needed, but the interesting part here is the failure/success wiring

All 5 comments

Hi,

Yes, nothing should prevent that.
In fact, the part that requires the password is the form_login (or json_login or whichever symfony鈥檚 security listener), this bundle just hooks into the result of this authentication listener for delivering a token or not (via success_handler and failure_handler), username/password validation is pure symfony logic.

For your use case, I would replace the form_login by a guard authenticator, the authenticator would take the AuthenticationSuccessHandler and AuthenticationFailureHandler provided by this bundle as dependencies and use them in its onAuthenticationSuccess() and onAuthenticationFailure() methods.

I'm closing as this is not a bug nor a feature request, feel free to reopen.

Hello,

I need to make the same thing but i've no idea how do this part : "to the authenticator would take the AuthenticationSuccessHandler and AuthenticationFailureHandler provided by this bundle as dependencies and use them in its onAuthenticationSuccess() and onAuthenticationFailure() methods."

I've already made my custom guard but i don't know how to inject and use the AuthenticationSuccessHandler and AuthenticationFailureHandler from Lexik, is it possible to have a example or any help to do this ?

Thanks.

@scargoll while it might be a little bit late, here is what I ended up with.
Replace the form_login or json_login by a guard:

# security.yml

security:
# [...]
    firewalls:
        login:
            pattern: ^/give-me-a-jwt
            stateless: true
            anonymous: true
            guard:
                authenticators:
                    - App\Security\UsernameAuthenticator
        api:
            pattern: ^/
            stateless: true
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator
# [...]

Authentication handlers can be injected in a new dedicated authenticator:

# App\Security\UsernameAuthenticator

# [...]
use Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationFailureHandler;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationSuccessHandler;

class UsernameAuthenticator extends AbstractGuardAuthenticator
{
    private $successHandler;
    private $failureHandler;

    public function __construct(
        AuthenticationSuccessHandler $successHandler,
        AuthenticationFailureHandler $failureHandler
    ) {
        $this->successHandler = $successHandler;
        $this->failureHandler = $failureHandler;
    }

    public function supports(Request $request)
    {
        return true;
    }

    public function getCredentials(Request $request)
    {
        $data = json_decode($request->getContent(), true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new HttpException(400, 'Invalid json');
        }
        if (!isset($data['username'])) {
            throw new HttpException(400, 'Username is required');
        }

        return $data['username'];
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        if (null === $credentials) return null;
        return $userProvider->loadUserByUsername($credentials);
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        return true;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        return $this->failureHandler->onAuthenticationFailure($request, $exception);
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        return $this->successHandler->onAuthenticationSuccess($request, $token);
    }

    public function supportsRememberMe()
    {
        return false;
    }

    public function start(Request $request, AuthenticationException $authException = null)
    {
        return new Response('Auth header required', 401);
    }
}

The interface can be implemented any way needed, but the interesting part here is the failure/success wiring

@Glideh thank you for sharing your solution ! Works beautifully :+1:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mflasquin picture mflasquin  路  5Comments

christophe-mailfert picture christophe-mailfert  路  5Comments

rolandrajko picture rolandrajko  路  3Comments

JuliusKoronci picture JuliusKoronci  路  4Comments

hiromik picture hiromik  路  3Comments