LexikJWTAuthenticationBundle icon indicating copy to clipboard operation
LexikJWTAuthenticationBundle copied to clipboard

Using the bundle with token created somewhere else

Open greg0ire opened this issue 7 years ago • 22 comments

My company is intending to use http://www.keycloak.org/ to generate JWTs for applications that need it. I'm trying to use this bundle for the guard it provides, which I guess can check the signature against a public key. I skipped the configuration step where you provide the keys for the moment, and created the following security config:

security:
    # https://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
    providers:
        in_memory: { memory: ~ }
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        api:
            stateless: true
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator

    access_control:
        - { path: ^/, roles: IS_AUTHENTICATED_FULLY  }

The app crashes

Environment variable not found: "JWT_PRIVATE_KEY_PATH".

This sounds wrong, because I'm not trying to generate a token here, since I didn't provide any credentials.

I can see it trying to load a JWSProviderInterface implementation at some point. This interface looks like it should maybe be split into 2 interfaces, so that the private key would no longer be depended on:

https://github.com/lexik/LexikJWTAuthenticationBundle/blob/ddfd184c8dd2f56b0b01bc5731dc84c9c9396701/Services/JWSProvider/JWSProviderInterface.php#L10-L30

It would be great if the JWT generation was optional. Not sure if I'm making sense, these are my first steps with JWT.

What do you think? Does it make sense to use this bundle in my case, or is it not worth the hassle?

greg0ire avatar Nov 08 '17 15:11 greg0ire

Hmm nevermind, flex added that env in a config file for me, so although I thought I hadn't configured anything, I actually did. Will close if I manage to use the bundle without a private key.

greg0ire avatar Nov 08 '17 15:11 greg0ire

Or rather, I'll reopen if I can't use the bundle, but I just stopped getting 500s :)

greg0ire avatar Nov 08 '17 15:11 greg0ire

The use case seems legit to me. I'm not totally sure it's fully supported, but if it is not, supporting it should not require much work (at worst some validation to remove from JWSProviderInterface implems). Please keep me informed

chalasr avatar Nov 08 '17 15:11 chalasr

I'll keep you posted in both cases then, thanks a lot for caring!

greg0ire avatar Nov 08 '17 18:11 greg0ire

Second hurdle, but I believe it's not the fault of this bundle: https://github.com/symfony/symfony/issues/24877

greg0ire avatar Nov 09 '17 11:11 greg0ire

Third hurdle, I need to use the client_credentials. To achieve that I need to switch to https://github.com/FriendsOfSymfony/FOSOAuthServerBundle (I don't need the server part though). That's what I understand when reading https://github.com/api-platform/core/pull/982 It's a bit scary though, there does not seem to be much activity on this bundle...

greg0ire avatar Nov 09 '17 11:11 greg0ire

I think I'm going to stick with Lexik and try to make it work anyway, not quite sure how yet.

greg0ire avatar Nov 09 '17 14:11 greg0ire

I finally managed to authenticate!

I had to pretend to be in username password mode and use clientid as user_identity_field.

Here is my user class:

<?php

namespace App\Application\Security;

use Lexik\Bundle\JWTAuthenticationBundle\Security\User\JWTUser;
use Lexik\Bundle\JWTAuthenticationBundle\Security\User\JWTUserInterface;

final class User extends JWTUser implements JWTUserInterface
{
    public static function createFromPayload($username, array $payload): self
    {
        return new self(
            $username,
            $payload['resource_access'][$username]['roles']
        );
    }
}

greg0ire avatar Nov 09 '17 15:11 greg0ire

Nice use case, well done!

chalasr avatar Nov 09 '17 15:11 chalasr

Thanks!

greg0ire avatar Nov 09 '17 15:11 greg0ire

This use case is very interesting. It will be great if the bundle could support that. For example through a list of trusted issuers (iss claim + associated public/shared keys for token verification).

@ WDYT?

Spomky avatar Nov 10 '17 07:11 Spomky

I assumed the token was verified with the public key I provided but didn't actually check that. And you are proposing to have several of them 🤔 ? That's interesting indeed!

greg0ire avatar Nov 10 '17 08:11 greg0ire

That's it. At the moment the bundle uses the key defined in the configuration to verify the token signatures. If the bundle is able to support trusted third parties, this means it has to manage several public/shared keys (maybe other parameters) to be able to verify tokens issued by those parties.

I see several use cases:

  • The bundle issues and verifies its own tokens (current behavior)
  • The bundle verifies its own tokens and tokens from third parties
  • The bundle only checks tokens from third parties.
  • The bundle only issues tokens that are checked by another application.

These use cases show that the bundle can be decoupled and can either act as an ID provider, a security firewall or both. A kind of lightweight OAuth2/OIDC server.

Spomky avatar Nov 10 '17 09:11 Spomky

Should I reopen this issue? Or should you create a new one for clarity?

greg0ire avatar Nov 10 '17 09:11 greg0ire

Sounds good to me, let's discuss it here

chalasr avatar Nov 10 '17 09:11 chalasr

From what @Spomky says, I think what is needed is :

  • the ability to have several public keys
  • the ability to have 2 parts of the bundle that work without waking up the other, which means some interfaces like JWSProviderInterface probably need to be split into 2 interfaces, and same should probably go for the implementations. Corresponding services should be loaded only when needed

greg0ire avatar Nov 10 '17 10:11 greg0ire

Hi everyone, I am also very interested in this, especially with @Spomky proposal:

For example through a list of trusted issuers (iss claim + associated public/shared keys for token verification).

Is anyone currently working on this? I would love to help in any way if possible. Please let me know. Great bundle by the way.

ermalmino avatar Jan 11 '18 19:01 ermalmino

Hi @ermalmino,

Yes I am working on it and I will add this feature on my custom encoder. But the feature is not yet ready. Hoping I can merge it by the end of the month.

Spomky avatar Jan 12 '18 08:01 Spomky

I've created this demo project that allow to verify a user logged in on firebase https://github.com/hpatoio/api-platform-jwt-firebase

hpatoio avatar Jul 13 '18 08:07 hpatoio

any news about this use case ?

karousn avatar Dec 24 '19 08:12 karousn

This would be nice!

tomme87 avatar Feb 01 '22 15:02 tomme87

I finally managed to authenticate!

I had to pretend to be in username password mode and use clientid as user_identity_field.

Here is my user class:

<?php

namespace App\Application\Security;

use Lexik\Bundle\JWTAuthenticationBundle\Security\User\JWTUser;
use Lexik\Bundle\JWTAuthenticationBundle\Security\User\JWTUserInterface;

final class User extends JWTUser implements JWTUserInterface
{
    public static function createFromPayload($username, array $payload): self
    {
        return new self(
            $username,
            $payload['resource_access'][$username]['roles']
        );
    }
}

Please, I would like to see how you implement this. I am having the same issue.

Error: Class App\Security\JWTUser contains 5 abstract methods and must therefore be declared abstract or implement the remaining methods (Symfony\Component\Security\Core\User\UserInterface::getRoles, Symfony\Component\Security\Core\User\UserInterface::getPassword, Symfony\Component\Security\Core\User\UserInterface::getSalt, ...)

and when I added the abstract methods, I encountered Cannot autowire service "App\Security\JWTUser": argument "$username" of method "__construct()" is type-hinted "string", you should configure its value explicitly.

abdojulari avatar Mar 28 '22 00:03 abdojulari