在 oauth2 中自动重新授权客户端


Automatically re-authorize client in oauth2?

我已经将oauth2服务器与Laravel集成在一起。但是我有点卡住的是使用通过 oauth2 保护的资源的网络应用程序,我们只通过授权服务器登录,就像有"使用 Google 登录"的网站一样,我们实际上在我们的网站上没有登录表单。所以我想知道我们如何在用户返回时识别用户?

我的意思是,我们通过仅在 webapp 上下文中相关的 web 应用程序提供个性化,因此我们在那里有一个数据库,它只是通过 user_id 将个性化链接到用户,我们还存储 user_id => access_token 对,在后续请求中我们使用该访问令牌。

这一切都在用户第一次登录时工作得很好,因为他们被要求授予访问权限等。但在后续请求中,授权服务器再次要求用户授予对 Web 应用程序的访问权限,而不仅仅是登录。

我在这里做错了什么,还是授权服务器应该 - 在识别预先存在的client_id并user_id自动授权客户端?并且如果Web应用程序在每个后续会话上都获得新access_token(user_id已退出用户会话/cookie,因此我们必须要求授权服务器再次告诉我们用户是谁)。

欧特控制器代码

<?php
use Illuminate'Support'MessageBag;
use 'League'OAuth2'Server;
use League'OAuth2'Server'Util'RedirectUri;
use Toddish'Verify'UserDeletedException;
use Toddish'Verify'UserDisabledException;
use Toddish'Verify'UserNotFoundException;
use Toddish'Verify'UserPasswordIncorrectException;
use Toddish'Verify'UserUnverifiedException;
class oAuthController extends BaseController {
    public function __construct() {
        // Initiate the request handler which deals with $_GET, $_POST, etc
        $request = new Server'Util'Request();
        // Initiate a new database connection
        $connectionString = '';
        $connectionString .= DB::Connection()->getConfig('driver') . '://';
        $connectionString .= DB::Connection()->getConfig('username') . ':';
        $connectionString .= DB::Connection()->getConfig('password') . '@';
        $connectionString .= DB::Connection()->getConfig('host') . '/';
        $connectionString .= DB::Connection()->getConfig('database');
        $db = new Server'Storage'PDO'Db( $connectionString );
        // Create the auth server, the three parameters passed are references
        //  to the storage models
        $this->authserver = new Server'Authorization(
            new Server'Storage'PDO'Client,
            new Server'Storage'PDO'Session,
            new Server'Storage'PDO'Scope
        );
        // Enable the authorisation code grant type
        $this->authserver->addGrantType(new Server'Grant'AuthCode( $this->authserver ));
        $this->authserver->setScopeDelimeter(',');
    }
    public function getIndex() {
        try {
            // Tell the auth server to check the required parameters are in the
            //  query string
            $grant  = $this->authserver->getGrantType('authorization_code');
            $params = $grant->checkAuthoriseParams();
            // Save the verified parameters to the user's session
            Session::put('client_id', $params['client_id']);
            Session::put('client_details', $params['client_details']);
            Session::put('redirect_uri', $params['redirect_uri']);
            Session::put('response_type', $params['response_type']);
            Session::put('scopes', $params['scopes']);
            // Redirect the user to the sign-in route
            return Redirect::action('OauthController@getSignin');
        } catch(Server'Exception'ClientException $e) {
            /**
             * Handle all the fatal errors we shouldn't send back to the client.
             */
            if(Server'Authorization::getExceptionType($e->getCode()) == 'invalid_client') {
                return View::make('oauth.client-error')
                       ->with(Lang::get('oauth.errors.fatal.invalid'))
                       ->with('apiMessage', $e->getMessage());
            } elseif(Str::contains($e->getMessage(), 'Check the "redirect_uri" parameter')) {
                return View::make('oauth.client-error')
                       ->with(Lang::get('oauth.errors.fatal.redirectUri'))
                       ->with('apiMessage', $e->getMessage());
            } elseif(Str::contains($e->getMessage(), 'Check the "client_id" parameter')) {
                return View::make('oauth.client-error')
                       ->with(Lang::get('oauth.errors.fatal.clientId'))
                       ->with('apiMessage', $e->getMessage());
            } else {
                /**
                 * if we got here we know that the client_id and redirect_uri are filled but we cannot assume they are
                 * valid we need to validate the credentials to decide whether to send the user back to the redirect_uri                    * with the error or handle it in house
                 */
                $client        = $this->authserver->getStorage('client');
                $clientDetails = $client->getClient(Input::get('client_id'),
                                                    null,
                                                    urldecode(Input::get('redirect_uri')),
                                                    $grant->getIdentifier());
                /**
                 * Invalid client details, let's handle this issue first.
                 */
                if($clientDetails === false) {
                    return View::make('oauth.client-error')
                           ->with(Lang::get('oauth.errors.fatal.invalid'))
                           ->with('apiMessage', $this->authserver->getExceptionMessage('invalid_client'));
                }
                /**
                 * Valid client credentials - let's redirect the resource owner back to the client with the error.
                 */
                $redirectResponse = [
                    'error'             => Server'Authorization::getExceptionType($e->getCode()),
                    'error_description' => $e->getMessage()
                ];
                return Redirect::to(RedirectUri::make(Input::get('redirect_uri'), $redirectResponse));
            }
        } catch(Exception $e) {
            /**
             * This is a general exception so we should pass this back off to the client with the server_error error
             */
            $redirectResponse = [ ];
        }
    }
    public function getSignin() {
        $params = $this->getParams(false);
        // User is signed in
        if($params['user_id'] !== null) {
            return Redirect::action('OauthController@getAuthorise');
        } else {
            return View::make('oauth.signin', $params);
        }
    }
    public function postSignin() {
        try {
            $validator = Validator::make(
                Input::only([ 'identifier', 'password' ]),
                [ 'identifier' => [ 'required' ], 'password' => [ 'required' ] ]
            );
            if($validator->fails()) {
                $errors = $validator->getMessageBag();
            } else {
                Auth::attempt(Input::only([ 'identifier', 'password' ]), Input::get('remember'));
                Session::put('user_id', Auth::user()->id);
                return Redirect::action('OauthController@getAuthorise');
            }
        } catch(UserNotFoundException $e) {
            $errors = new MessageBag( [ 'identifier' => Lang::get('oauth.errors.signIn.userNotFound') ] );
        } catch(UserUnverifiedException $e) {
            $errors = new MessageBag( [ 'identifier' => Lang::get('oauth.errors.signIn.userNotVerified') ] );
        } catch(UserDisabledException $e) {
            $errors = new MessageBag( [ 'identifier' => Lang::get('oauth.errors.signIn.userDisabled') ] );
        } catch(UserDeletedException $e) {
            $errors = new MessageBag( [ 'identifier' => Lang::get('oauth.errors.signIn.userDeleted') ] );
        } catch(UserPasswordIncorrectException $e) {
            $errors = new MessageBag( [ 'password' => Lang::get('oauth.errors.signIn.userWrongPassword') ] );
        } catch(Exception $e) {
            $errors = new MessageBag( [ 'generic' => $e->getMessage() ] );
        }
        return Redirect::action('OauthController@getSignin')
               ->withErrors($errors)
               ->withInput(Input::only([ 'identifier', 'remember' ]));
    }
    private function getParams($doUserIdCheck = true) {
        // Retrieve the auth params from the user's session
        $params['client_id']      = Session::get('client_id');
        $params['client_details'] = Session::get('client_details');
        $params['redirect_uri']   = Session::get('redirect_uri');
        $params['response_type']  = Session::get('response_type');
        $params['scopes']         = Session::get('scopes');
        // Check that the auth params are all present
        foreach($params as $key => $value) {
            if($value === null) {
                // Throw an error because an auth param is missing - don't
                //  continue any further
                $errors = new MessageBag( [ 'error' => 'The request is missing the ' . $key . ' key' ] );
                die();
            }
        }
        // Get the user ID
        $params['user_id'] = Session::get('user_id');
        // User is not signed in so redirect them to the sign-in route (/oauth/signin)
        if($doUserIdCheck && $params['user_id'] === null) {
            return Redirect::action('OauthController@getSignin');
        }
        return $params;
    }
    public function getAuthorise() {
        $params = $this->getParams();
        //if it's not an array we have got back a redirect request
        if(! is_array($params)) {
            return $params;
        }
        // Check if the client should be automatically approved
        $autoApprove = ( $params['client_details']['auto_approve'] === '1' ) ? true : false;
        // Process the authorise request if the user's has clicked 'approve' or the client
        if($autoApprove) {
            // Generate an authorisation code
            $code = $this->authserver->getGrantType('authorization_code')
                    ->newAuthoriseRequest('user', $params['user_id'], $params);
            // Redirect the user back to the client with an authorisation code
            return Redirect::to(
                RedirectUri::make(
                    $params['redirect_uri'],
                    [ 'code' => $code, 'state' => isset( $params['state'] ) ? $params['state'] : '' ]));
        }
        // The client shouldn't automatically be approved so show them a form
        return View::make('oauth.authorise', $params);
    }
    public function postAuthorise() {
        $params = $this->getParams();
        //if it's not an array we have got back a redirect request
        if(! is_array($params)) {
            return $params;
        }
        if(Input::get('approve') !== null) {
            if(Input::get('terms') === null) {
                $e = new MessageBag( [ 'terms' => Lang::get('oauth.errors.authorize.termsNotAccepted') ] );
                return Redirect::action('OauthController@getAuthorise')->withErrors($e);
            }
            // Generate an authorisation code
            $code = $this->authserver->getGrantType('authorization_code')
                    ->newAuthoriseRequest('user', $params['user_id'], $params);
            // Redirect the user back to the client with an authorisation code
            $redirectResponse = [ 'code' => $code, 'state' => isset( $params['state'] ) ? $params['state'] : '' ];
        }
        // If the user has denied the client so redirect them back without an authorisation code
        if(Input::get('deny') !== null) {
            $redirectResponse = [ 'error'         => 'access_denied',
                                  'error_message' => $this->authserver->getExceptionMessage('access_denied'),
                                  'state'         => isset( $params['state'] ) ? $params['state'] : '' ];
        }
        if(isset( $redirectResponse )) {
            return Redirect::to(RedirectUri::make($params['redirect_uri'], $redirectResponse));
        } else {
            //we didn't get a valid response from the user, let's start again
            return Redirect::action('OauthController@getIndex');
        }
    }
    public function postAccessToken() {
        try {
            // Tell the auth server to issue an access token
            $this->authserver->setAccessTokenTTL(15768000); //ask user for permissions every half year
            $response = $this->authserver->issueAccessToken();
            return Response::json($response, 200, [ 'cache-control' => 'Cache-Control', 'Pragma' => 'no-cache' ]);
        } /*
        |--------------------------------------------------------------------------
        | An error occurred inside the library
        |--------------------------------------------------------------------------
        */
        catch(Server'Exception'ClientException $e) {
            // Throw an exception because there was a problem with the client's request
            $exceptionType = Server'Authorization::getExceptionType($e->getCode());
            $response      = [
                'error'             => $exceptionType,
                'error_description' => $e->getMessage()
            ];
            // Grab the code out of the header string
            $code = substr(current(Server'Authorization::getExceptionHttpHeaders($exceptionType)), 9, 3);
            return Response::json($response, $code);
        } /*
        |--------------------------------------------------------------------------
        | An error occurred outside the library, probably a generic server error
        |--------------------------------------------------------------------------
        */
        catch(Exception $e) {
            // Throw an error when a non-library specific exception has been thrown
            $response = [ 'error' => 'undefined_error', 'error_description' => $e->getMessage() ];
            return Response::json($response, 500);
        }
    }
}

这是因为OAuth通常是一个授权协议:

使用授权服务器进行身份验证的用户已授予 您的 Laravel 网络应用程序调用以下端点的权限 受授权服务器保护。它将使用access_token 要做到这一点。

您想要的是将其用作将用户标识为系统中有效用户的一种方式。为此,您可以在 AS 上调用 userinfo 端点(使用 access_token),然后在服务器中为该用户创建会话。Laravel似乎有一种手动登录用户的方法,因此您将在成功返回userinfo后调用该方法。(经过身份验证的用户的代理)。

userinfo 是一个端点,通常返回有关用户的信息(例如电子邮件、姓名、ID 等)。您可以使用该信息并将其关联到您身边的用户记录。