我已经将 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);
}
}
}