*/ protected $userinfo = null; /** * Cache of decoded JWT token * @var array */ protected $token_data = null; /** * Initialize token with the from*() static methods */ protected function __construct() { } /** * Create a token given a access_token and optionally refresh_token, passed * as a string * * @param string $access_token * @param ?string $refresh_token * @return Token */ public static function fromString(string $access_token, ?string $refresh_token = null): Token { $token = new Token(); $token->access_token = $access_token; $token->refresh_token = $refresh_token; return $token; } /** * Create a token from a OIDC response from the token endpoint * * @param object $response * @return Token */ public static function fromResponse(object $response): Token { $token = new Token(); $token->access_token = $response->access_token ?? null; $token->access_token_expires_at = isset($response->expires_in) ? time() + $response->expires_in : null; $token->refresh_token = $response->refresh_token ?? null; $token->refresh_token_expires_at = isset($response->refresh_expires_in) ? time() + $response->refresh_expires_in : null; return $token; } /** * Fetch an oidc client authenticated with this token * * @return Client */ public function getClient(): Client { if (!isset($this->client)) { $this->client = new Client(new Authentication\TokenAuthentication($this)); } return $this->client; } /** * Refresh the access token * * @return void */ public function refresh() { } /** * Fetch the raw decoded data out of our JWT access token * * @return array */ public function getTokenData(): array { if (!isset($this->token_data)) { $this->token_data = json_decode(json_encode($this->decode()), true); } return $this->token_data; } /** * Check whether the access token is expired * * If we fail to parse it because it's expired, or the expiration time is * within EXPIRATION_GRACE_PERIOD seconds of now, we consider it expired. * * @return bool */ public function isExpired(): bool { try { $data = $this->getTokenData(); } catch (\Firebase\JWT\ExpiredException $ex) { return true; } if ($data['exp'] <= time() - static::EXPIRATION_GRACE_PERIOD) return true; else return false; } /** * Decode the access token as a JWT token * * @return object */ protected function decode(): object { $client = $this->getClient(); $jwks = Authkit2::cache('oidc.jwks', function() use ($client) { $response = $client->get(Client::getOidcConfig()['jwks_uri']); return json_decode(json_encode($response), true); }); return JWT::decode($this->access_token, JWK::parseKeySet($jwks), Client::getOidcConfig()['id_token_signing_alg_values_supported']); } /** * Fetch the underlying access token this token represents * * @return string */ public function getAccessToken(): string { return $this->access_token; } /** * Fetch the user's refresh token * * @return string */ public function getRefreshToken(): string { return $this->refresh_token; } /** * Fetch the user info associated with this token from the OIDC * provider * * @return array */ public function getUserInfo(): array { if (!isset($this->userinfo)) { $this->userinfo = json_decode(json_encode($this->getClient()->get('userinfo')), true); } return $this->userinfo; } /** * Fetch the roles encoded in this token * * @return string[] */ public function getRoles(): array { return $this->getTokenData()['realm_access']['roles']; } /** * Fetch the uuid encoded in this token * * @return string */ public function getUserId(): string { return 'crn:user:'.$this->getTokenData()['sub']; } }