HWIOAuthBundle icon indicating copy to clipboard operation
HWIOAuthBundle copied to clipboard

Symfony 6 login issue: loadUserByIdentifier(): Argument #1 ($identifier) must be of type string, null given

Open jasperpoppe opened this issue 3 years ago • 15 comments

Q A
Bug? no
New Feature? no
Support question? yes
Version 2.0.0-BETA2

Actual Behavior

What is the actual behavior? When you call the Application Login URI (which is /connect/auth0) and correctly filled in your credentials, I got redirected to the callback url (/login/check-auth0?code=123456) the following error messages:

Uncaught PHP Exception TypeError: "HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUserProvider::loadUserByIdentifier(): Argument #1 ($identifier) must be of type string, null given, called in /vendor/hwi/oauth-bundle/src/Security/Core/User/OAuthUserProvider.php on line 43" at /vendor/hwi/oauth-bundle/src/Security/Core/User/OAuthUserProvider.php line 24
HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUserProvider::loadUserByIdentifier(): Argument #1 ($identifier) must be of type string, null given, called in /vendor/hwi/oauth-bundle/src/Security/Core/User/OAuthUserProvider.php on line 43

Like the error is displaying, my UserResponseInterface is empty. Even when the Auth0 logs contain the Success Login and Success Exchange.

Expected Behavior

When you call the Application Login URI (which is /connect/auth0) and correctly filled in your credentials, a new OAuthUser should be created and stored by the Symfony application.

Steps to Reproduce

I'm on Symfony version 6.0 and PHP version 8.1, using hwi/oauth-bundle version 2.0.0-BETA2.

But the same error was visible in Symfony 5.2 and PHP 7.4.

This is my config/packages/security.yaml:

security:
    providers:
        hwi:
            id: hwi_oauth.user.provider
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            pattern: ^/
            oauth:
                resource_owners:
                    auth0: "/login/check-auth0"
                login_path: /login
                use_forward: false
                failure_path: /

                oauth_user_provider:
                    service: hwi_oauth.user.provider
            logout:
                path: /auth0/logout
                target: /

    # 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: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/tool, roles: ROLE_OAUTH_USER }
        - { path: ^/admin, roles: ROLE_OAUTH_USER }

This is my config/packages/hwi_oauth.yaml:

hwi_oauth:
    firewall_names: [main]

    resource_owners:
        auth0:
            type: oauth2
            class: 'App\Client\Auth0ResourceOwner'
            client_id: "%env(AUTH0_CLIENT_ID)%"
            client_secret: "%env(AUTH0_CLIENT_SECRET)%"
            base_url: "https://%env(AUTH0_DOMAIN)%"
            scope: "%env(AUTH0_SCOPES)%"

This is my \App\Client\Auth0ResourceOwner:

<?php

namespace App\Client;

use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\GenericOAuth2ResourceOwner;

class Auth0ResourceOwner extends GenericOAuth2ResourceOwner
{
    /**
     * {@inheritdoc}
     */
    protected array $paths = [
        'identifier'     => 'user_id',
        'nickname'       => 'nickname',
        'realname'       => 'name',
        'email'          => 'email',
        'profilepicture' => 'picture',
    ];

    /**
     * {@inheritdoc}
     */
    public function getAuthorizationUrl($redirectUri, array $extraParameters = []): string
    {
        return parent::getAuthorizationUrl($redirectUri, array_merge(array(
            'authorization_url' => $this->options['authorization_url'],
            'audience' => $this->options['audience'],
        ), $extraParameters));
    }

    /**
     * {@inheritdoc}
     */
    protected function configureOptions(OptionsResolver $resolver)
    {
        parent::configureOptions($resolver);

        $resolver->setDefaults([
            'authorization_url' => '{base_url}/authorize',
            'access_token_url'  => '{base_url}/oauth/token',
            'infos_url'         => '{base_url}/userinfo',
            'audience'          => '{base_url}/userinfo',
            'scope'             => 'openid user profile'
        ]);

        $resolver->setRequired([
            'base_url',
        ]);

        $normalizer = function (Options $options, $value) {
            return str_replace('{base_url}', $options['base_url'], $value);
        };

        $resolver->setNormalizer('authorization_url', $normalizer);
        $resolver->setNormalizer('access_token_url', $normalizer);
        $resolver->setNormalizer('infos_url', $normalizer);
        $resolver->setNormalizer('audience', $normalizer);
    }
}

Possible Solutions

I have absolutely no idea what I'm doing wrong or which setting I forgot. This is a project that was working in August 2021, but now (April 2022) I'm dusting off. Can someone point me in the right direction to solve this login issue?

jasperpoppe avatar Apr 12 '22 22:04 jasperpoppe

@jasperpoppe I had the same error you have until I set the scope option to openid profile email. Also, I'm using the Auth0ResourceOwner shipped with the library.

alsciende avatar May 22 '22 21:05 alsciende

Hi @alsciende! Thanks for your feedback. I'm trying to use the Auth0ResourceOwner, but I got the following error message: Class "\OAuth\ResourceOwner\Auth0ResourceOwner" must implement interface "HWI\Bundle\OAuthBundle\OAuth\ResourceOwnerInterface".

Is your hwi_oauth.yaml file looking like this?

hwi_oauth:
    firewall_names: [main]

    resource_owners:
        auth0:
            type: oauth2
            class: '\OAuth\ResourceOwner\Auth0ResourceOwner'
            client_id: "%env(AUTH0_CLIENT_ID)%"
            client_secret: "%env(AUTH0_CLIENT_SECRET)%"
            base_url: "https://%env(AUTH0_DOMAIN)%"
            scope: "%env(AUTH0_SCOPES)%"

jasperpoppe avatar May 27 '22 12:05 jasperpoppe

@jasperpoppe Hi! Here is my hwi_oauth.yml file: https://github.com/alsciende/effusion/blob/main/config/packages/hwi_oauth.yaml.

Its content is:

hwi_oauth:
    resource_owners:
        auth0:
            type:                auth0
            client_id:           '%env(AUTH0_CLIENT_ID)%'
            client_secret:       '%env(AUTH0_CLIENT_SECRET)%'
            base_url:            'https://%env(AUTH0_DOMAIN)%'
            scope:               'openid profile email'```

alsciende avatar May 27 '22 14:05 alsciende

Hey @jasperpoppe @alsciende , can you check if applying: https://github.com/hwi/HWIOAuthBundle/pull/1913 fixes your problem? Thanks!

stloyd avatar Jul 22 '22 08:07 stloyd

Hi @stloyd ! Thanks for your time!

When I apply your bugfix to the 2.0.0-BETA2 branch, I got following error message:

Attempted to call an undefined method named "getUserIdentifier" of class "HWI\Bundle\OAuthBundle\OAuth\Response\PathUserResponse".

jasperpoppe avatar Jul 22 '22 09:07 jasperpoppe

@jasperpoppe You should try dev-master as beta3 that contains a fix for this was not yet released.

stloyd avatar Jul 22 '22 09:07 stloyd

When on dev-master and Symfony 6.1, I got following error message:

User identifier was not found in response.

jasperpoppe avatar Jul 22 '22 09:07 jasperpoppe

@jasperpoppe According to Auth0 docs and your code above:

    protected array $paths = [
        'identifier'     => 'user_id',
        'nickname'       => 'nickname',
        'realname'       => 'name',
        'email'          => 'email',
        'profilepicture' => 'picture',
    ];

This: 'identifier' => 'user_id', should be: 'identifier' => 'email', (or preferred_username or other unique value).

stloyd avatar Jul 22 '22 10:07 stloyd

In built-in we use use sub for identifier: https://github.com/hwi/HWIOAuthBundle/blob/master/src/OAuth/ResourceOwner/Auth0ResourceOwner.php#L28

stloyd avatar Jul 22 '22 10:07 stloyd

Ok, now I got this weird error which isn't even loading my app.

hwi_oauth.yaml:

hwi_oauth:
    firewall_names: [main]

    resource_owners:
        auth0:
            type: oauth2
            class: 'App\Client\Auth0ResourceOwner'
            client_id: "%env(AUTH0_CLIENT_ID)%"
            client_secret: "%env(AUTH0_CLIENT_SECRET)%"
            base_url: "https://%env(AUTH0_DOMAIN)%"
            scope: "%env(AUTH0_SCOPES)%"

src/Client/Auth0ResourceOwner.php:

<?php

namespace App\Client;

use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use HWI\Bundle\OAuthBundle\OAuth\ResourceOwner\GenericOAuth2ResourceOwner;

class Auth0ResourceOwner extends GenericOAuth2ResourceOwner
{
    /**
     * {@inheritdoc}
     */
    protected array $paths = [
        'identifier' => 'sub',
        'nickname' => 'nickname',
        'realname' => 'name',
        'email' => 'email',
        'profilepicture' => 'picture',
    ];

    /**
     * {@inheritdoc}
     */
    public function getAuthorizationUrl($redirectUri, array $extraParameters = []): string
    {
        return parent::getAuthorizationUrl($redirectUri, array_merge(array(
            'authorization_url' => $this->options['authorization_url'],
            'audience' => $this->options['audience'],
        ), $extraParameters));
    }

    /**
     * {@inheritdoc}
     */
    protected function configureOptions(OptionsResolver $resolver)
    {
        parent::configureOptions($resolver);

        $resolver->setDefaults([
            'authorization_url' => '{base_url}/authorize',
            'access_token_url'  => '{base_url}/oauth/token',
            'infos_url'         => '{base_url}/userinfo',
            'audience'          => '{base_url}/userinfo',
            'scope'             => 'openid user profile email'
        ]);

        $resolver->setRequired([
            'base_url',
        ]);

        $normalizer = function (Options $options, $value) {
            return str_replace('{base_url}', $options['base_url'], $value);
        };

        $resolver->setNormalizer('authorization_url', $normalizer);
        $resolver->setNormalizer('access_token_url', $normalizer);
        $resolver->setNormalizer('infos_url', $normalizer);
        $resolver->setNormalizer('audience', $normalizer);
    }
}

The error I got:

Warning: Undefined array key "type"

No clue where I can set that missing type key.

When I don't set a custom resource owner class in my hwi_oauth.yaml file, I got following:

Invalid configuration for path "hwi_oauth.resource_owners.auth0": All parameters are mandatory for types 'oauth2' and 'oauth1'. Check if you're missing one of: 'access_token_url', 'authorization_url', 'infos_url' and 'request_token_url' for 'oauth1'.

jasperpoppe avatar Jul 22 '22 10:07 jasperpoppe

@jasperpoppe This should be fixed after #1915.

stloyd avatar Jul 22 '22 11:07 stloyd

After updating the dev-master package and using your #1913 pull request, I still got the error message like one of my previous comments: https://github.com/hwi/HWIOAuthBundle/issues/1901#issuecomment-1192393680

jasperpoppe avatar Jul 23 '22 11:07 jasperpoppe

@jasperpoppe Can you paste response after which you get that error? Cause according to linked docs it should work with sub.

stloyd avatar Jul 23 '22 13:07 stloyd

So, this is the steps I take to reproduce this error:

  • I go to/connect/auth0 (to login)
  • I got redirected to /login/check-auth0?code={code_id}
  • Then I see the error

When I dump the $response data from src/Security/Core/User/OAuthUserProvider.php#L43, I see the three roles I defined in auth0:

[OAuthUserProvider.php] on line 43:
array:1 [▼
  "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" => array:3 [▼
    0 => "administrator"
    1 => "group-responsible"
    2 => "wristband-responsible"
  ]
]

So there is no sub or other $paths I defined in src/Client/Auth0ResourceOwner.php.

jasperpoppe avatar Jul 23 '22 13:07 jasperpoppe

For me it seems that there is wrong url used to get user info or due to usage of audience you got different response (my guess)

stloyd avatar Jul 23 '22 13:07 stloyd