/** * Decrypts and verifies a nested JWT. * * If the supplied token is a JWT, this function calls {@link getObject()} * to decode the JWT. * * If the supplied token is a JWE, the JWE is firstly decrypted, then the underlying * plaintext is treated as a JWT, and further decoded. * * @param SimpleJWT\Keys\KeySet $keys the key set containing the decryption * and verification keys * @param string $expected_jwe_alg the expected value of the `alg` parameter for the * JWE, which should be agreed between the parties out-of-band * @param string $expected_jwt_alg the expected value of the `alg` parameter for the * underlying JWT, which should be agreed between the parties out-of-band * @param string $jwe_kid the ID of the key to use for decryption. If null, this * is automatically retrieved * @param string $jwt_kid the ID of the key to use for verification. If null, this * is automatically retrieved * @return JWT the decoded JWT * @throws InvalidTokenException if the token is invalid for any reason */ function getJWTObject($keys, $expected_jwe_alg, $expected_jwt_alg, $jwe_kid = null, $jwt_kid = null) { switch ($this->type) { case 'JWT': return $this->getObject($keys, $expected_jwt_alg, $jwt_kid); case 'JWE': $jwe = JWE::decrypt($this->data, $keys, $expected_jwe_alg, $jwe_kid, $this->format); if ($jwe->getHeader('cty') != 'JWT') { throw new InvalidTokenException('Not a nested JWT', InvalidTokenException::TOKEN_PARSE_ERROR); } return JWT::decode($jwe->getPlaintext(), $keys, $expected_jwt_alg, $jwt_kid); } }
/** * @see SimpleID\API\OAuthHooks::oAuthProcessAuthRequestHook() */ function oAuthProcessAuthRequestHook($request, $response) { $store = StoreManager::instance(); $auth = AuthManager::instance(); $client = $store->loadClient($request['client_id'], 'SimpleID\\Protocols\\OAuth\\OAuthClient'); // Check 1: Check whether the prompt parameter is present in the request $request->setImmediate($request->paramContains('prompt', 'none')); if ($request->paramContains('prompt', 'login')) { $this->f3->set('message', $this->t('This app\'s policy requires you to log in again to confirm your identity.')); $request->paramRemove('prompt', 'login'); return self::CHECKID_REENTER_CREDENTIALS; } if ($request->paramContains('prompt', 'consent')) { $request->paramRemove('prompt', 'consent'); return self::CHECKID_APPROVAL_REQUIRED; } // Check 2: If id_token_hint is provided, check that it refers to the current logged-in user if (isset($request['id_token_hint'])) { try { $jwt = JWT::decode($request['id_token_hint']); $user_match = $jwt->getClaim('sub') == $this->getSubject($auth->getUser(), $client); } catch (CryptException $e) { $user_match = false; } if (!$user_match) { $auth->logout(); return self::CHECKID_LOGIN_REQUIRED; } } // Check 3: Check whether the max_age or acr parameters are present in the client defaults // or the request parameters if (isset($request['max_age'])) { $max_age = $request['max_age']; } elseif (isset($client['connect']) && isset($client['connect']['default_max_age'])) { $max_age = $client['connect']['default_max_age']; } else { $max_age = -1; } // If the relying party provides a max_auth_age if ($max_age > -1 && $auth->isLoggedIn()) { $auth_level = $auth->getAuthLevel(); if ($auth_level == null) { $auth_level = AuthManager::AUTH_LEVEL_SESSION; } $auth_time = $auth->getAuthTime(); if ($auth_time == null) { $auth_time = 0; } // If the last time we logged on actively (i.e. using a password) is greater than // max_age, we then require the user to log in again if ($auth_level < AuthManager::AUTH_LEVEL_CREDENTIALS || time() - $auth->getAuthTime() > $max_age) { $this->f3->set('message', $this->t('This app\'s policy requires you to log in again to confirm your identity.')); return self::CHECKID_REENTER_CREDENTIALS; } } if (isset($request['acr'])) { $acr = $request['acr']; } elseif (isset($client['connect']) && isset($client['connect']['default_acr'])) { $acr = $client['connect']['default_acr']; } else { $acr = -1; } if ($acr > -1) { return self::CHECKID_INSUFFICIENT_TRUST; } return null; }