2amigos / yii2-usuario

Highly customizable and extensible user management, authentication, and authorization Yii2 extension
https://github.com/2amigos/yii2-usuario
Other
294 stars 141 forks source link

Problem with authorization through social network new user. Return 404 #383

Open grozzzny opened 4 years ago

grozzzny commented 4 years ago

https://github.com/2amigos/yii2-usuario/blob/8adbffeb3f2da4e2bd77be8d7e06a1629904734f/src/User/Controller/RegistrationController.php#L158

Pls, help me. The problem with authorization through social network new user. Return 404 /user/registration/connect?code=FMwHPLCI*****DJC

    // Da\User\Controller\RegistrationController
    public function actionConnect($code)
    {
        /** @var SocialNetworkAccount $account */
        $account = $this->socialNetworkAccountQuery->whereCode($code)->one();
        if ($account === null || $account->getIsConnected()) {
            throw new NotFoundHttpException(); // !!!!!!!!!!  social_account not saved
        }
        ...
}
// Da\User\Service\SocialNetworkAuthenticateService
protected function createAccount()
    {
        $data = $this->client->getUserAttributes();

        /** @var SocialNetworkAccount $account */
        $account = $this->controller->make(
            SocialNetworkAccount::class,
            [],
            [
                'provider' => $this->client->getId(),
                'client_id' => $data['id'],
                'data' => json_encode($data),
                'username' => $this->client->getUserName(),
                'email' => $this->client->getEmail(),
            ]
        );
        // !!!!!!!!!!!!!!!!! User not found => social_account not save
        if (($user = $this->getUser($account)) instanceof User) {
            $account->user_id = $user->id;
            $account->save(false);
        }

        return $account;
    }
grozzzny commented 4 years ago

I tried all events. The problem is solved only this way, otherwise redirecting to 404

Config. Event beforeAuthenticate handler

 'user' => [
            'class' => Da\User\Module::class,
            'controllerMap' => [
 'security' => [
                    'class' => 'Da\User\Controller\SecurityController',
                    'on beforeAuthenticate' => ['app\components\SocialNetworkHandler', 'beforeAuthenticate']
                ],
     ],
],

file components/SocialNetworkHandler.php

<?php

namespace app\components;

use Da\User\Event\SocialNetworkAuthEvent;
use Da\User\Factory\MailFactory;
use Da\User\Model\User;
use Da\User\Service\UserCreateService;
use Yii;
use yii\base\BaseObject;

class SocialNetworkHandler extends BaseObject
{
    /**
     * @param SocialNetworkAuthEvent $event
     */
    public static function beforeAuthenticate($event)
    {
        /** @var User $model */
        $user = Yii::$container->get(User::class);

        if($user::find()->where(['email' => $event->client->email])->exists()) return;

        $user = new $user([
            'scenario' => 'create',
            'email' => $event->client->email,
            'username' => $event->client->username,
            'password' => null
        ]);

        /** @var MailFactory $mailFactory */
        $mailFactory = Yii::$container->get(MailFactory::class);
        $mailService = $mailFactory::makeWelcomeMailerService($user);

        /** @var UserCreateService $userCreateService */
        $userCreateService = Yii::$container->get(UserCreateService::class, [$user, $mailService]);
        $userCreateService->run();

        $event->account->user_id = $user->id;
        $event->account->save();

        Yii::$app->user->login($user);
    }
}
mkodummy commented 4 years ago

I also noticed this bug. i fixed it moving save(false) outside the IF statement. ugly fix though.

BEFORE

        if (($user = $this->getUser($account)) instanceof User) {
            $account->user_id = $user->id;
            $account->save(false);
        }

AFTER

        if (($user = $this->getUser($account)) instanceof User) {
            $account->user_id = $user->id;
       }
       $account->save(false);
mkodummy commented 4 years ago

@grozzzny from your code I made an even simpler fix. Thanks for the insight on the beforeAuthenticate hook. My final SocialNetworkHandler.php file is only the code below. Rest is exactly the same as you pointed out.

class SocialNetworkHandler extends BaseObject
{
    /**
     * @param SocialNetworkAuthEvent $event
     */
    public static function beforeAuthenticate($event)
    {
        return $event->account->save(false);
    }
}

Saving the social account like above already unlocks the entire user registration/welcome email etc. stuff existing, in my project the code below is redundant ( of course, unless you are customizing it)

 $user = new $user([
            'scenario' => 'create',
            'email' => $event->client->email,
            'username' => $event->client->username,
            'password' => null
        ]);

        /** @var MailFactory $mailFactory */
        $mailFactory = Yii::$container->get(MailFactory::class);
        $mailService = $mailFactory::makeWelcomeMailerService($user);

        /** @var UserCreateService $userCreateService */
        $userCreateService = Yii::$container->get(UserCreateService::class, [$user, $mailService]);
        $userCreateService->run();
grozzzny commented 4 years ago

Thanks, @mkodummy. I will test later.

whisere commented 4 years ago

@mkodummy and @grozzzny Thanks, the last change works!

But I get this warning when login with github:

Please use the Authorization HTTP header instead as using the access_token query parameter is deprecated.

Depending on your API usage, we'll be sending you this email reminder on a monthly basis.

Visit https://developer.github.com/changes/2020-02-10-deprecating-auth-through-query-param for more information about suggested workarounds and removal dates.

vinhhung1108 commented 4 years ago

Thanks so much @mkodummy and @grozzzny, it worked for me. I searched for a few hours to find this topic.

dantart commented 3 years ago

this happened to me also ... and I reached this page ... why is not fixed in the official repo ?

mj4444ru commented 3 years ago

For PHP 7.4+

    'user' => [
        'class' => Da\User\Module::class,
        'controllerMap' => [
            'security' => [
                'class' => Da\User\Controller\SecurityController::class,
                'on beforeAuthenticate' =>
                    static fn (Da\User\Event\SocialNetworkAuthEvent $event) => $event->account->save(false),
            ],
        ],
        // ...
    ],
Jonyx4 commented 3 years ago

This solution worked like a charm!

For PHP 7.4+

    'user' => [
        'class' => Da\User\Module::class,
        'controllerMap' => [
            'security' => [
                'class' => Da\User\Controller\SecurityController::class,
                'on beforeAuthenticate' =>
                    static fn (Da\User\Event\SocialNetworkAuthEvent $event) => $event->account->save(false),
            ],
        ],
        // ...
    ],
srakl commented 3 years ago

For PHP 7.4+

    'user' => [
        'class' => Da\User\Module::class,
        'controllerMap' => [
            'security' => [
                'class' => Da\User\Controller\SecurityController::class,
                'on beforeAuthenticate' =>
                    static fn (Da\User\Event\SocialNetworkAuthEvent $event) => $event->account->save(false),
            ],
        ],
        // ...
    ],

@jonyx4 @mj4444ru

I added this in my main.php anything else to add? doesm't seem to work for me. new accounts not getting registered with social networks.

srakl commented 3 years ago

@grozzzny i tried your steps, after i login with google, it redirects me to user/registration/connect&code=adssad how do i get it to skip that? and get fill in the firtsname lastname email etc from google

Jonyx4 commented 3 years ago

For PHP 7.4+

    'user' => [
        'class' => Da\User\Module::class,
        'controllerMap' => [
            'security' => [
                'class' => Da\User\Controller\SecurityController::class,
                'on beforeAuthenticate' =>
                    static fn (Da\User\Event\SocialNetworkAuthEvent $event) => $event->account->save(false),
            ],
        ],
        // ...
    ],

@Jonyx4 @mj4444ru

I added this in my main.php anything else to add? doesm't seem to work for me. new accounts not getting registered with social networks.

I don't know if this is related to your issue, but I also had to apply the fix described in this issue https://github.com/2amigos/yii2-usuario/issues/397

srakl commented 3 years ago

@Jonyx4 unfortunately adding the return $account; still redirects the user to user/registration/connect&code=adssad to confirm registration. it should auto fill in firstname lastname etc into db :(

whisere commented 3 years ago

This works for me:

 'security' => [
                    'class' => 'Da\User\Controller\SecurityController',
                    'on beforeAuthenticate' => ['frontend\controllers\SocialNetworkHandler', 'beforeAuthenticate']
                ],

frontend\controllers\SocialNetworkHandler:

namespace frontend\controllers;

use Da\User\Event\SocialNetworkAuthEvent;
use Da\User\Factory\MailFactory;
use Da\User\Model\User;
use Da\User\Service\UserCreateService;
use Yii;
use yii\base\BaseObject;

class SocialNetworkHandler extends BaseObject
{
    /**
     * @param SocialNetworkAuthEvent $event
     */
    public static function beforeAuthenticate($event)
    {
        return $event->account->save(false);
    }
}
srakl commented 3 years ago

@whisere does yours redirect newly registered users to the user/registration/connect page to input their email etc after they click on the social connect button? how do i bypass that step?

whisere commented 3 years ago

It is normal that's just a step for user to confirm/edit their user name and email in the system (they may not want to use the same user name and email used in the social media account), it should automatically fill in the email address from social account.

srakl commented 3 years ago

@whisere cool, thanks. if i was to skip that step, and auto fill firstname and last name from social account, know which file i can edit or extend by any chance?

whisere commented 3 years ago

There may not be first and last name from social account, you can see: https://github.com/2amigos/yii2-usuario/tree/master/src/User/AuthClient (better to fork your own repo/copy if to make change to code)

overwriting controllers in the documentation: https://yii2-usuario.readthedocs.io/en/latest/enhancing-and-overriding/overriding-controllers/# and social events and other events under the events menu: https://yii2-usuario.readthedocs.io/en/latest/events/social-network-connect-events/

srakl commented 3 years ago

@whisere thanks. for the most part i got it working. It just doesn't seem to save the firstname and lastname in the user table. any idea what im missing here? here are the changes i made to the controller. https://github.com/2amigos/yii2-usuario/issues/418