This repository has been archived on 2022-02-10. You can view files and clone it, but cannot push or open issues or pull requests.
rangitaki/vendor/bshaffer/oauth2-server-php/src/OAuth2/GrantType/JwtBearer.php

227 lines
7.1 KiB
PHP

<?php
namespace OAuth2\GrantType;
use OAuth2\ClientAssertionType\ClientAssertionTypeInterface;
use OAuth2\Storage\JwtBearerInterface;
use OAuth2\Encryption\Jwt;
use OAuth2\Encryption\EncryptionInterface;
use OAuth2\ResponseType\AccessTokenInterface;
use OAuth2\RequestInterface;
use OAuth2\ResponseInterface;
/**
* The JWT bearer authorization grant implements JWT (JSON Web Tokens) as a grant type per the IETF draft.
*
* @see http://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-04#section-4
*
* @author F21
* @author Brent Shaffer <bshafs at gmail dot com>
*/
class JwtBearer implements GrantTypeInterface, ClientAssertionTypeInterface
{
private $jwt;
protected $storage;
protected $audience;
protected $jwtUtil;
protected $allowedAlgorithms;
/**
* Creates an instance of the JWT bearer grant type.
*
* @param OAuth2\Storage\JWTBearerInterface|JwtBearerInterface $storage A valid storage interface that implements storage hooks for the JWT bearer grant type.
* @param string $audience The audience to validate the token against. This is usually the full URI of the OAuth token requests endpoint.
* @param EncryptionInterface|OAuth2\Encryption\JWT $jwtUtil OPTONAL The class used to decode, encode and verify JWTs.
* @param array $config
*/
public function __construct(JwtBearerInterface $storage, $audience, EncryptionInterface $jwtUtil = null, array $config = array())
{
$this->storage = $storage;
$this->audience = $audience;
if (is_null($jwtUtil)) {
$jwtUtil = new Jwt();
}
$this->config = array_merge(array(
'allowed_algorithms' => array('RS256', 'RS384', 'RS512')
), $config);
$this->jwtUtil = $jwtUtil;
$this->allowedAlgorithms = $this->config['allowed_algorithms'];
}
/**
* Returns the grant_type get parameter to identify the grant type request as JWT bearer authorization grant.
*
* @return
* The string identifier for grant_type.
*
* @see OAuth2\GrantType\GrantTypeInterface::getQuerystringIdentifier()
*/
public function getQuerystringIdentifier()
{
return 'urn:ietf:params:oauth:grant-type:jwt-bearer';
}
/**
* Validates the data from the decoded JWT.
*
* @return
* TRUE if the JWT request is valid and can be decoded. Otherwise, FALSE is returned.
*
* @see OAuth2\GrantType\GrantTypeInterface::getTokenData()
*/
public function validateRequest(RequestInterface $request, ResponseInterface $response)
{
if (!$request->request("assertion")) {
$response->setError(400, 'invalid_request', 'Missing parameters: "assertion" required');
return null;
}
// Store the undecoded JWT for later use
$undecodedJWT = $request->request('assertion');
// Decode the JWT
$jwt = $this->jwtUtil->decode($request->request('assertion'), null, false);
if (!$jwt) {
$response->setError(400, 'invalid_request', "JWT is malformed");
return null;
}
// ensure these properties contain a value
// @todo: throw malformed error for missing properties
$jwt = array_merge(array(
'scope' => null,
'iss' => null,
'sub' => null,
'aud' => null,
'exp' => null,
'nbf' => null,
'iat' => null,
'jti' => null,
'typ' => null,
), $jwt);
if (!isset($jwt['iss'])) {
$response->setError(400, 'invalid_grant', "Invalid issuer (iss) provided");
return null;
}
if (!isset($jwt['sub'])) {
$response->setError(400, 'invalid_grant', "Invalid subject (sub) provided");
return null;
}
if (!isset($jwt['exp'])) {
$response->setError(400, 'invalid_grant', "Expiration (exp) time must be present");
return null;
}
// Check expiration
if (ctype_digit($jwt['exp'])) {
if ($jwt['exp'] <= time()) {
$response->setError(400, 'invalid_grant', "JWT has expired");
return null;
}
} else {
$response->setError(400, 'invalid_grant', "Expiration (exp) time must be a unix time stamp");
return null;
}
// Check the not before time
if ($notBefore = $jwt['nbf']) {
if (ctype_digit($notBefore)) {
if ($notBefore > time()) {
$response->setError(400, 'invalid_grant', "JWT cannot be used before the Not Before (nbf) time");
return null;
}
} else {
$response->setError(400, 'invalid_grant', "Not Before (nbf) time must be a unix time stamp");
return null;
}
}
// Check the audience if required to match
if (!isset($jwt['aud']) || ($jwt['aud'] != $this->audience)) {
$response->setError(400, 'invalid_grant', "Invalid audience (aud)");
return null;
}
// Check the jti (nonce)
// @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-13#section-4.1.7
if (isset($jwt['jti'])) {
$jti = $this->storage->getJti($jwt['iss'], $jwt['sub'], $jwt['aud'], $jwt['exp'], $jwt['jti']);
//Reject if jti is used and jwt is still valid (exp parameter has not expired).
if ($jti && $jti['expires'] > time()) {
$response->setError(400, 'invalid_grant', "JSON Token Identifier (jti) has already been used");
return null;
} else {
$this->storage->setJti($jwt['iss'], $jwt['sub'], $jwt['aud'], $jwt['exp'], $jwt['jti']);
}
}
// Get the iss's public key
// @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06#section-4.1.1
if (!$key = $this->storage->getClientKey($jwt['iss'], $jwt['sub'])) {
$response->setError(400, 'invalid_grant', "Invalid issuer (iss) or subject (sub) provided");
return null;
}
// Verify the JWT
if (!$this->jwtUtil->decode($undecodedJWT, $key, $this->allowedAlgorithms)) {
$response->setError(400, 'invalid_grant', "JWT failed signature verification");
return null;
}
$this->jwt = $jwt;
return true;
}
public function getClientId()
{
return $this->jwt['iss'];
}
public function getUserId()
{
return $this->jwt['sub'];
}
public function getScope()
{
return null;
}
/**
* Creates an access token that is NOT associated with a refresh token.
* If a subject (sub) the name of the user/account we are accessing data on behalf of.
*
* @see OAuth2\GrantType\GrantTypeInterface::createAccessToken()
*/
public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
{
$includeRefreshToken = false;
return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken);
}
}