Select Git revision
TokenBuilder.php
-
Sonia Zorba authoredSonia Zorba authored
TokenBuilder.php 5.87 KiB
<?php
namespace RAP;
use \Firebase\JWT\JWT;
class TokenBuilder {
private $locator;
public function __construct(Locator $locator) {
$this->locator = $locator;
}
public function getIdToken(AccessTokenData $tokenData, \Closure $jwtCustomizer = null): string {
$keyPair = $this->getNewestKeyPair();
$payload = $this->createIdTokenPayloadArray($tokenData, $jwtCustomizer);
return JWT::encode($payload, $keyPair->privateKey, $keyPair->alg, $keyPair->keyId);
}
private function createIdTokenPayloadArray(AccessTokenData $tokenData, \Closure $jwtCustomizer = null) {
$user = $this->locator->getUserDAO()->findUserById($tokenData->userId);
$payloadArr = array(
'iss' => $this->locator->config->jwtIssuer,
'sub' => strval($user->id),
'iat' => intval($tokenData->creationTime),
'exp' => intval($tokenData->expirationTime),
'aud' => $tokenData->clientId
);
$name = $user->getCompleteName();
if ($name !== null) {
$payloadArr['name'] = $name;
}
if (in_array("email", $tokenData->scope)) {
$payloadArr['email'] = $user->getPrimaryEmail();
}
if (in_array("profile", $tokenData->scope)) {
$payloadArr['given_name'] = $user->getName();
$payloadArr['family_name'] = $user->getSurname();
if ($user->getInstitution() !== null) {
$payloadArr['org'] = $user->getInstitution();
}
}
if ($jwtCustomizer !== null) {
// Add additional custom claims
$jwtCustomizer($payloadArr);
}
return $payloadArr;
}
public function getAccessToken(AccessTokenData $tokenData, \Closure $jwtCustomizer = null): string {
$keyPair = $this->getNewestKeyPair();
$user = $this->locator->getUserDAO()->findUserById($tokenData->userId);
if ($user === null) {
// CLI client
$sub = $tokenData->clientId;
} else {
$sub = $user->id;
}
$payload = array(
'iss' => $this->locator->config->jwtIssuer,
'sub' => strval($sub),
'iat' => intval($tokenData->creationTime),
'exp' => intval($tokenData->expirationTime),
'aud' => $this->getAudience($tokenData),
'scope' => implode(' ', $tokenData->scope)
);
if ($jwtCustomizer !== null) {
// Add additional custom claims
$jwtCustomizer($payload);
}
return JWT::encode($payload, $keyPair->privateKey, $keyPair->alg, $keyPair->keyId);
}
private function getAudience(AccessTokenData $tokenData) {
if ($tokenData->audience !== null) {
return $this->getAudienceClaim($tokenData->audience);
}
$client = $this->locator->getBrowserBasedOAuth2ClientById($tokenData->clientId, true);
if ($client === null) {
// CLI client without audience
return null;
}
$audiences = [$tokenData->clientId];
if ($client->scopeAudienceMap !== null) {
foreach ($tokenData->scope as $scope) {
if (array_key_exists($scope, $client->scopeAudienceMap)) {
$audience = ((array) $client->scopeAudienceMap)[$scope];
if (!in_array($audience, $audiences)) {
array_push($audiences, $audience);
}
}
}
}
return $this->getAudienceClaim($audiences);
}
private function getAudienceClaim($audiences) {
if (count($audiences) === 1) {
// according to RFC 7519 audience can be a single value or an array
return $audiences[0];
}
return $audiences;
}
public function generateToken(array $claims) {
$iat = time();
// basic payload
$payload = array(
'iss' => $this->locator->config->jwtIssuer,
'iat' => $iat
);
// copy claims passed as parameter
foreach ($claims as $key => $value) {
$payload[$key] = $value;
}
// set expiration claim if it doesn't exist
if (!array_key_exists('exp', $payload)) {
$payload['exp'] = $iat + 3600;
}
$keyPair = $this->getNewestKeyPair();
return JWT::encode($payload, $keyPair->privateKey, $keyPair->alg, $keyPair->keyId);
}
/**
* @param int $lifespan in hours
* @param string $audience target service
*/
public function generateNewToken(string $subject, int $lifespan, string $audience) {
$keyPair = $this->getNewestKeyPair();
$iat = time();
$exp = $iat + $lifespan * 3600;
$payload = array(
'iss' => $this->locator->config->jwtIssuer,
'sub' => strval($subject),
'iat' => $iat,
'exp' => $exp,
'aud' => $audience
);
$conf = $this->getTokenIssuerConfig($audience);
if (property_exists($conf, 'aud')) {
$payload['aud'] = $conf->aud;
}
if (property_exists($conf, 'scope')) {
$payload['scope'] = $conf->scope;
}
return JWT::encode($payload, $keyPair->privateKey, $keyPair->alg, $keyPair->keyId);
}
private function getTokenIssuerConfig($audience) {
foreach ($this->locator->config->tokenIssuer->services as $service) {
if ($service->id === $audience) {
return $service;
}
}
throw new \Exception("Unable to find configuration for " . $audience);
}
private function getNewestKeyPair(): RSAKeyPair {
$keyPair = $this->locator->getJWKSDAO()->getNewestKeyPair();
if ($keyPair === null) {
$keyPair = $this->locator->getJWKSHandler()->generateKeyPair();
}
return $keyPair;
}
}