thephpleague / oauth2-server-bundle

Symfony bundle for the OAuth2 Server.
MIT License
188 stars 86 forks source link

firewall requires a user provider but none was defined #191

Open ghost opened 4 months ago

ghost commented 4 months ago

Bundle setup docs https://github.com/thephpleague/oauth2-server-bundle/blob/58d4b11a5f75dd049d613871e6a3a3a367cbd976/docs/basic-setup.md does not say anything about setting a user provider for a firewall. I get exception Uncaught PHP Exception Symfony\Component\Config\Definition\Exception\InvalidConfigurationException: ""api" firewall requires a user provider but none was defined." at MissingUserProvider.php line 31

My security.yaml (same as in docs):

security:
    firewalls:
        api_token:
            pattern: ^/token$
            security: false
        api:
            pattern: ^/api
            security: true
            stateless: true
            oauth2: true
bartholdbos commented 2 months ago

Are you using client-credentials for authentication? I've had a similar issue due to changes that have been made in oauth2-server. Before oauth2-server version 9.0.0 the subject field in the JWT token was empty when using client credentials. This commit add a change that will fill the client id in the subject field. When the subject field is empty in the JWT this code will return a NullUser object and not call the user provider, but because this field is not empty anymore after version 9.0.0 the user provider is now being called and throws an error. I think the oauth2-server-bundle should provide a user provider for this or return the NullUser in some other way. The oauth2-server-bundle 0.9.0 upgrade broke our client-credentials flow without it being a mayor release :(

Ardenexal commented 2 months ago

Also experiencing the issue with client credentials that @bartholdbos is describing. Our tests started failing when updating to 0.9.0 because the UserInterface::createFromPayload was called, and no payload was passed with the JWT array and only the Client ID as the UserName.

This breaking change was not documented in the Release notes and there is no documentation on what should be done to resolve this issue. Any help would be appreciated. For now will stay it at 0.8.0 until there is a resolution

rela589n commented 1 month ago

It seems that previously NullUser (League\Bundle\OAuth2ServerBundle\Security\User\NullUser) was used, since $userIdentifier ($psr7Request->getAttribute('oauth_user_id', '') from OAuth2Authenticator) was empty string, - that's because 'sub' claim of jwt token was never set (at the point BearerTokenValidator::validateAuthorization it is empty string)

Hence, it looks like a fix for claim attribute, but it's definitely necessary to have some user provider.

rela589n commented 1 month ago

In my opinion, since we already have user identifier, it would be better if league library used some InMemoryUser (or even dedicated Oauth2User) containing that identifier

maciekstary commented 1 month ago

Same problem here, @bartholdbos perfectly explained it. I can't understand how the issue went unnoticed since the change is so breaking. As for resolution: Problem with own user provider is that all SF user providers has to implement Symfony\Component\Security\Core\User\UserProviderInterface which in turn forces you to return class implementing Symfony\Component\Security\Core\User\UserInterface in loadUserByIdentifier method. IMHO the way to go is to make Client class implement UserInterface and then just use standard EntityUserProvider. For now I got it working by creating my own client class and setting it in config to be used in place of original Client:

namespace App\Entity;

use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use League\Bundle\OAuth2ServerBundle\Model\AbstractClient;
use Symfony\Component\Security\Core\User\UserInterface;

#[ORM\Entity]
class Oauth2Client extends AbstractClient implements UserInterface
{
    #[ORM\Id]
    #[ORM\Column(type: Types::STRING, length: 32)]
    protected string $identifier;

    public function getRoles(): array
    {
        return [];
    }

    public function eraseCredentials()
    {

    }

    public function getUserIdentifier(): string
    {
        return $this->identifier;
    }
}
security:
    providers:
        ...
        oauth2_server_bundle:
            entity:
                class: App\Entity\Oauth2Client
                property: identifier
league_oauth2_server:
    client:
        classname: App\Entity\Oauth2Client

And then use oauth2_server_bundle provider in firewall that requires oauth2.

Would be nice if this was not needed!

chalasr commented 1 month ago

/cc @ajgarlag

rela589n commented 1 month ago

@maciekstary, I'm just wondering if it won't create the confusion that Oauth2Client::getRoles() method doesn't return any roles, since there are ROLE_OAUTH2_* roles created by the library. The point how oauth roles currently work under the hood is still a mystery to me

maciekstary commented 1 month ago

@rela589n Good point. What I can only say is that in previous implementation authenticator used League\Bundle\OAuth2ServerBundle\Security\User\NullUser class and it's getRoles method returns empty array, too.

ajgarlag commented 1 month ago

Sorry, but I'm pretty busy these days.

Can anyone provide a reproducer? I'd need a minimal Symfony app working with league/oauth2-server-bundle:0.8 without a user provider.

krkabol commented 1 month ago

Hi, I've tried to prepare a reproducer (based on https://github.com/dwgebler/OAuth2ServerDemo), available here https://github.com/krkabol/OAuth_reproducer. Includes a test client, but sorry as new in Symfony, from some weird reason the /authorize route does not works :) - hopefully you are able to fix it easily yourself.

I have a similar non-public and when upgrade to 0.9, it falls..

EDIT: SSH key included for instant usage, it is not an unwanted leak

ajgarlag commented 1 month ago

@krkabol thanks. I'll try it ASAP.

ajgarlag commented 1 month ago

The reproducer provided by @krkabol was not using client-credentials. It did not fail with the Uncaught PHP Exception Symfony\Component\Config\Definition\Exception\InvalidConfigurationException: ""api" firewall requires a user provider but none was defined." at MissingUserProvider.php line 31 message.

The problem with that repository was reported in #200. I've opened #201 to fix documentation.

I still need a reproducer for the original error reported in this issue.