This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
site-base/spark/src/TokenGuard.php
2017-11-03 16:26:07 +11:00

186 lines
5.2 KiB
PHP

<?php
namespace Laravel\Spark;
use Exception;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Laravel\Spark\Contracts\Repositories\TokenRepository;
class TokenGuard
{
/**
* The token repository implementation.
*
* @var TokenRepository
*/
protected $tokens;
/**
* Create a new token guard instance.
*
* @param TokenRepository $tokens
* @return void
*/
public function __construct(TokenRepository $tokens)
{
$this->tokens = $tokens;
}
/**
* Get the authenticated user for the given request.
*
* @param Request $request
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user(Request $request)
{
if (Auth::user()) {
return $this->alreadyHasUser();
}
if (! $token = $this->getToken($request)) {
return;
}
// If the token is valid we will return the user instance that is associated with
// the token as well as populate the token usage time. If a token wasn't found
// of course this method will return null and no user will be authenticated.
Auth::setDefaultDriver('api');
$token->touchLastUsedTimestamp();
return $token->user->setToken($token);
}
/**
* Return the current user with a fresh transient token.
*
* @return \Illuminate\Contracts\Auth\Authenticatable
*/
protected function alreadyHasUser()
{
return Auth::user()->setToken(
$this->createTransientToken(Auth::id(), Carbon::now()->addMinutes(5))
);
}
/**
* Get the token instance from the database.
*
* @param Request $request
* @return Token
*/
protected function getToken(Request $request)
{
$token = $this->getTokenFromRequest($request);
if ($token instanceof Token) {
return $token;
} else {
return $token ? $this->tokens->validToken($token) : null;
}
}
/**
* Get the token for the given request.
*
* @param Request $request
* @return Token|string
*/
protected function getTokenFromRequest(Request $request)
{
$bearer = $request->bearerToken();
// First we will check to see if the token is in the request input data or is a bearer
// token on the request. If it is, we will consider this the token, otherwise we'll
// look for the token in the cookies then attempt to validate that it is correct.
if ($token = $request->input('api_token', $bearer)) {
return $token;
}
if ($request->cookie('spark_token')) {
return $this->getTokenFromCookie($request);
}
}
/**
* Get the token for the given request cookie.
*
* @param Request $request
* @return Token
*/
protected function getTokenFromCookie($request)
{
// If we need to retrieve the token from the cookie, it'll be encrypted so we must
// first decrypt the cookie and then attempt to find the token value within the
// database. If we can't decrypt the value we'll bail out with a null return.
try {
$token = JWT::decode(decrypt($request->cookie('spark_token')));
} catch (Exception $e) {
return;
}
// We will compare the XSRF token in the decoded API token against the XSRF header
// sent with the request. If the two don't match then this request is sent from
// a valid source and we won't authenticate the request for further handling.
if (! $this->validXsrf($token, $request)) {
return;
}
// Here we will create a token instance from the JWT token. This'll be a transient
// token which allows all operations since the user is physically logged into a
// screen of the application. We'll check the expiration date then return it.
$token = $this->createTransientToken(
$token['sub'], Carbon::createFromTimestamp($token['expiry'])
);
return $token->isExpired() ? null : $token;
}
/**
* Create a new transient token instance for the given user.
*
* @param int $userId
* @param Carbon $expiration
* @return Token
*/
protected function createTransientToken($userId, Carbon $expiration)
{
return (new Token)->forceFill([
'user_id' => $userId,
'transient' => true,
'expires_at' => $expiration,
]);
}
/**
* Determine if the XSRF / header are valid and match.
*
* @param array $token
* @param Request $request
* @return bool
*/
protected function validXsrf($token, $request)
{
return isset($token['xsrf']) && hash_equals(
$token['xsrf'], (string) $this->decryptXsrfHeader($request)
);
}
/**
* Decrypt the XSRF header on the given request.
*
* @param Request $request
* @return string|null
*/
protected function decryptXsrfHeader($request)
{
try {
return decrypt($request->header('X-XSRF-TOKEN'));
} catch (Exception $e) {
}
}
}