thephpleague / oauth2-server-bundle

Symfony bundle for the OAuth2 Server.
MIT License
187 stars 85 forks source link

Argument 3 of service "league.oauth2_server.authenticator.oauth2" is abstract user provider #77

Open jasonpeltzer opened 2 years ago

jasonpeltzer commented 2 years ago

Just recently tried to make the move from trikoder over to these packages, have "successfully" gotten v0.1.2 working, but when I try to use v0.2.0 or v0.3.0 I get recipe issues and then when I try to run the application I get the error: "Argument 3 of service "league.oauth2_server.authenticator.oauth2" is abstract user provider".

This seems to be an issue with Symfony 5.3/5.4, and is definitely a blocker to getting Symfony 6 working!

Thanks

pchergi commented 2 years ago

Hi! Just playing with a fresh install of Symfony 5.4 and Oauth2-server-bundle 0.3.0 (php v, 7.4). It works well up to the stage of using custom authenticators. For now i can issue tokens via api. But when i add to to security.yaml something like that:

    main:
        ...
        custom_authenticators:
                - League\Bundle\OAuth2ServerBundle\Security\Authenticator\OAuth2Authenticator
        entry_point: form_login

    test:
        pattern:    ^/test
        oauth2:     true
        stateless:  true

framework crashes with the "title" error.

In vendor/league/oauth2-server-bundle/src/Resources/config/services.php we have interface definition like that:

    ->set('league.oauth2_server.authenticator.oauth2', OAuth2Authenticator::class)
        ->args([
            service('league.oauth2_server.factory.psr_http'),
            service(ResourceServer::class),
            abstract_arg('User Provider'),
            abstract_arg('Role prefix'),
        ])

As a workaround i declared service('Symfony\Component\Security\Core\User\UserProviderInterface') instead of abstract 'User Provider'. So now i can access resource servers via token authentification with no errors. Alas i'm not sure that it's a "right" way.

Some points are still not clear for me:

  1. Do we really need abstract argument here (As for me, it's pretty much "endpoint" service)?
  2. May be i loose something important with the bundle setup?
  3. Why auto-wiring failed?

I tried to track down the error, but it's way TOO much information for novice like me (i'm not a Symfony pro nor php coder).

Any advise will be very welcome.

Thanks!

chalasr commented 2 years ago

Why are you using custom_authenticators over oauth2: ~ in the main firewall?

jasonpeltzer commented 2 years ago

It worked with Guard Security and the Trikoder libraries. I'm still using a custom authenticator as I need to intercept certain requests that contain an access_token in the url and convert it to a bearer token so it was an artifact of that. After reviewing the documentation I did find it tucked deep in the Basic Setup, so it's my fault for not finding it originally.

jasonpeltzer commented 2 years ago

Thank you, this was not an obvious fix!

johnvanham commented 2 years ago

Below is a working example of using custom authenticators alongside the oauth2 bundle.

Setting oauth2: true is enough to activate oauth2 from the bundle and can be used at the same time as custom_authenticators. The order of the firewalls is important. I had to make sure the 'main' firewall came after the 'api' firewall in my set up for it to all work properly.

I am using the oauth2 authorization code grant for the API as well as a custom API key authenticator as an alternative authentication method for the API. The login form used by the authorization grant uses a custom authenticator that works with a WordPress user database.

Hope that helps!

security:
    enable_authenticator_manager: true
    # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
    # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
    providers:
        users:
            entity:
                # the class of the entity that represents users
                class: 'App\Entity\User'
                # the property to query by - e.g. email, username, etc
                property: 'email'
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        api_token:
            pattern: ^/token$
            security: false
        api:
            pattern: ^/api
            security: true
            stateless: true
            oauth2: true
            provider: users
            custom_authenticators:
                - App\Security\ApiKeyAuthenticator
        main:
            security: true
            lazy: true
            provider: users

            custom_authenticators:
                - App\Security\LoginFormAuthenticator

            logout:
                path: logout
                # where to redirect after logout
                target: login

            # 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

    # 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: ^/authorize, roles: IS_AUTHENTICATED_REMEMBERED }
        - { path: ^/api/docs, roles: PUBLIC_ACCESS }
        - { path: ^/api, roles: ROLE_USER }
jojocague commented 2 years ago

I have the problem with Symfony 6.1 and bundle 0.4 right now. Everything seems to be correct but firewall won't trigger... I can generate new client token but it has never been "requested" for example for following path: /api/v1/users. I dont know where is the problem. Make everything that written in the docs but without success. Can somebody help me with it or how i can debbug it?

This is Firewall:

security:
    # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
#    password_hashers:
#        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
    # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
    enable_authenticator_manager: true
    providers:
        users_in_memory: { memory: null }
    firewalls:
        api_token:
            pattern: ^/token$
            security: false
        api:
            pattern: ^/api
            security: true
            stateless: true
            oauth2: true
        main:
            security: true
            lazy: true
            provider: users_in_memory
            #        dev:
#            pattern: ^/(_(profiler|wdt)|css|images|js)/
#            security: false
#        main:
#            lazy: true
#            provider: users_in_memory

            # 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

    # 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: ^/admin, roles: ROLE_ADMIN }
        # - { path: ^/profile, roles: ROLE_USER }
#
#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

Bundle Config:


league_oauth2_server:
  authorization_server: # Required

    # Full path to the private key file.
    # How to generate a private key: https://oauth2.thephpleague.com/installation/#generating-public-and-private-keys
    private_key: '%kernel.project_dir%/var/oauth/private.key'

    # Passphrase of the private key, if any
    private_key_passphrase: null

    # The plain string or the ascii safe string used to create a Defuse\Crypto\Key to be used as an encryption key.
    # How to generate an encryption key: https://oauth2.thephpleague.com/installation/#string-password
    encryption_key: 'YXqniAl0r3BierGCkJh55IQ3CF3ddELC+0DdOgdEzPQ='

    # The type of value of 'encryption_key'
    encryption_key_type: plain # One of "plain"; "defuse"

    # How long the issued access token should be valid for.
    # The value should be a valid interval: http://php.net/manual/en/dateinterval.construct.php#refsect1-dateinterval.construct-parameters
    access_token_ttl: PT1H

    # How long the issued refresh token should be valid for.
    # The value should be a valid interval: http://php.net/manual/en/dateinterval.construct.php#refsect1-dateinterval.construct-parameters
    refresh_token_ttl: P1M

    # How long the issued auth code should be valid for.
    # The value should be a valid interval: http://php.net/manual/en/dateinterval.construct.php#refsect1-dateinterval.construct-parameters
    auth_code_ttl: PT10M

    # Whether to enable the client credentials grant
    enable_client_credentials_grant: true

    # Whether to enable the password grant
    enable_password_grant: true

    # Whether to enable the refresh token grant
    enable_refresh_token_grant: true

    # Whether to enable the authorization code grant
    enable_auth_code_grant: true

    # Whether to require code challenge for public clients for the auth code grant
    require_code_challenge_for_public_clients: true

    # Whether to enable access token saving to persistence layer (default to true)
    persist_access_token: true

  resource_server: # Required

    # Full path to the public key file
    # How to generate a public key: https://oauth2.thephpleague.com/installation/#generating-public-and-private-keys
    public_key: '%kernel.project_dir%/var/oauth/public.key'

  scopes:
    # Scopes that you wish to utilize in your application.
    # This should be a simple array of strings.
    available: [ default ]

    # Scopes that will be assigned when no scope given.
    # This should be a simple array of strings.
    default: [ default ]

  # Configures different persistence methods that can be used by the bundle for saving client and token data.
  # Only one persistence method can be configured at a time.
  persistence: # Required
    doctrine:

      # Name of the entity manager that you wish to use for managing clients and tokens.
      entity_manager: default

  # Set a custom prefix that replaces the default 'ROLE_OAUTH2_' role prefix
  role_prefix: ROLE_OAUTH2_

  client:
    # Set a custom client class. Must be a League\Bundle\OAuth2ServerBundle\Model\Client
    classname: League\Bundle\OAuth2ServerBundle\Model\Client

Routes & Bundles same as in docs.

Sylvannes commented 2 years ago

I also have this problem. "oauth2: true" is active on the firewall. When accessing the route, I can see in the debugger that the firewall context is loaded, but the authenticator for the bearer token is never run.

Trying to specify League\Bundle\OAuth2ServerBundle\Security\Authenticator\OAuth2Authenticator under custom_authenticators gives the title error.

edit: This is on Symfony 6.1. I would assume something changed in the framework with regards to running authenticators in the way that was originally set up for this library.

fbxcelsior commented 3 months ago

Same issue here, migrating to SF6.4 from SF5.4