0

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

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

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

我在这里做错了什么还是应该授权服务器 - 在识别出预先存在的 client_id 和 user_id 后自动授权客户端?并且 webapp 是否应该在每个后续会话中获得一个新的 access_token(其中 user_id 已经从用户会话/cookie 中退出,所以我们必须要求授权服务器再次告诉我们用户是谁)。

Oauth 控制器代码

<?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);
        }
    }

}
4

1 回答 1

0

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

通过授权服务器进行身份验证的用户已授予您的 Laravel webapp 调用受授权服务器保护的端点的权限。它会使用access_token 来做到这一点。

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

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

于 2013-10-14T23:05:41.617 回答