Skip to content

Instantly share code, notes, and snippets.

@T0miii
Created December 4, 2018 10:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save T0miii/ba63f2154b3a9c03140f5c20daadebec to your computer and use it in GitHub Desktop.
Save T0miii/ba63f2154b3a9c03140f5c20daadebec to your computer and use it in GitHub Desktop.
Laravel Passport Code Grant return User when login.
<?php
namespace App\Http\Controllers;
use Psr\Http\Message\ServerRequestInterface;
use App\Http\Controllers\Traits\UserByAccessToken;
use League\OAuth2\Server\Exception\OAuthServerException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use \Laravel\Passport\Http\Controllers\AccessTokenController as ATC;
class AccessTokenController extends ATC
{
use UserByAccessToken;
/**
* @param ServerRequestInterface $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Illuminate\Http\Response
*/
public function issueToken(ServerRequestInterface $request)
{
try {
$tokenResponse = parent::issueToken($request);
$content = $tokenResponse->getContent();
$data = json_decode($content, true);
if (isset($data["error"])) {
throw new OAuthServerException('The user credentials were incorrect.', 6, 'invalid_credentials', 401);
}
$userId = $this->updateSSOCache($data['access_token']);
$data['user_id'] = $userId;
return response()->json($data);
} catch (ModelNotFoundException $e) {
//return error message
return response(["message" => "User not found"], 500);
} catch (OAuthServerException $e) {
//return error message
return response(["message" => "The user credentials were incorrect.', 6, 'invalid_credentials"], 500);
} catch (\Exception $e) {
////return error message
return response(["message" => "Internal server error"], 500);
}
}
}
<?php
namespace App\Http\Controllers\Traits;
use App\User;
use Lcobucci\JWT\Parser;
use Laravel\Passport\Token;
use Illuminate\Support\Facades\Cache;
use Laravel\Passport\TokenRepository;
trait UserByAccessToken
{
/**
* @param string $accessToken
* @return \Laravel\Passport\Token
*/
protected function getTokenByAccessToken(string $accessToken): Token
{
$tokenRepository = new TokenRepository();
$jwt = (new Parser())->parse($accessToken);
return $tokenRepository->find($jwt->getClaim('jti'));
}
/**
* @param string $accessToken
* @return User
* @throws \Exception
*/
protected function getTokenUser(string $accessToken): User
{
$token = $this->getTokenByAccessToken($accessToken);
if (!$token) throw new \Exception('Access Token not set');
return User::findOrFail($token->user_id);
}
/**
* @param string $accessToken
* @return int
* @throws \Exception
*/
protected function updateSSOCache(string $accessToken): int
{
$user = $this->getTokenUser($accessToken);
if (!$user) {
throw new \Exception('No User found for Access Token');
}
$this->alterCache($user->user_id, $accessToken);
return $user->user_id;
}
/**
* @param int $userId
* @param string $accessToken
* @return Void
*/
protected function alterCache(int $userId, string $accessToken): Void
{
$userCacheKey = 'user_tokens_' . $userId;
$defaultPrefix = Cache::getPrefix();
Cache::setPrefix('sso_auth:');
$cachedData = Cache::get($userCacheKey);
if ($cachedData) {
$cachedData[] = $accessToken;
} else {
$cachedData = [$accessToken];
}
Cache::add($userCacheKey, $cachedData, 10);
Cache::setPrefix($defaultPrefix);
}
}
@T0miii
Copy link
Author

T0miii commented Dec 4, 2018

Inspired by a similar Usecase (laravel/passport#143) where someone wanted to get the User returned via Password Grant.
I had similar issue when using Code Grant , that i needed the User more explicit the User ID (for my own "fake SSO" implementation).

Basically after we get the access_token from $tokenResponse = parent::issueToken($request); we could call the getTokenUser function to get our associated User. After this you just need to return the User as an array with the access_token, refresh and expire_in.

Further i needed some kind of SSO implementation. Here we used redis. On the Auth server side i store the access_token associated with the User in redis as an array.

On my Client application i have a middleware wich checks if the User got any tokens in the redis that is equal with that one in the Cookie.
If yess go on, if not check the auth server, and refresh the token in redis.

This is basically just to make less server calls to the auth server. Ofc i destroy the keys in redis when i logout, and beside that they Time To Live is set to like 10 minutes, could be shorter.

Hope this could save some time for some one, some day. Cheers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment