UNPKG

angular-simple-oidc

Version:

Angular Library implementing Open Id Connect specification. Code Flow, Refresh Tokens, Session Management, Discovery Document.

863 lines (849 loc) 35.5 kB
import { Injectable, NgModule } from '@angular/core'; import { KJUR, hextob64u, KEYUTIL, b64utoutf8 } from 'jsrsasign'; import { HttpParams } from '@angular/common/http'; class TokenCryptoService { sha256b64First128Bits(payload) { const hash = KJUR.crypto.Util.hashString(payload, 'sha256'); const first128bits = hash.substr(0, hash.length / 2); return hextob64u(first128bits); } sha256btoa(payload) { const hash = KJUR.crypto.Util.hashString(payload, 'sha256'); return hextob64u(hash); } verifySignature(key, message) { const pk = KEYUTIL.getKey(key); return KJUR.jws.JWS.verify(message, pk, ['RS256']); } generateNonce() { return 'N' + Math.random() + '' + Date.now(); } generateState() { return Date.now() + '' + Math.random() + Math.random(); } generateCodesForCodeVerification() { const codeVerifier = 'C' + Math.random() + '' + Date.now() + '' + Date.now() + Math.random(); const codeChallenge = this.sha256btoa(codeVerifier); const method = 'S256'; return { codeVerifier, codeChallenge, method }; } } TokenCryptoService.decorators = [ { type: Injectable } ]; /** * Inspired on https://github.com/damienbod/angular-auth-oidc-client */ class TokenHelperService { convertTokenClaimToDate(claim) { if (!claim) { return null; } const date = new Date(0); // The 0 here is the key, which sets the date to the epoch date.setUTCSeconds(claim); return date; } isTokenExpired(expiresAt) { return new Date().getTime() > expiresAt; } getExpirationFromExpiresIn(expiresIn) { const now = new Date(); // expires_in = access token expiration in seconds (optional) // 3.2.2.5. Successful Authentication Response // https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse now.setSeconds(now.getSeconds() + expiresIn); return now; } getHeaderFromToken(idToken) { return this.getTokenSlice(idToken, 0); } getPayloadFromToken(idToken) { return this.getTokenSlice(idToken, 1); } getSignatureFromToken(idToken) { return this.getTokenSlice(idToken, 2); } getTokenSlice(idToken, index) { if (!idToken || idToken.split('.').length !== 3) { // Quick and dirty validation. // The caller is expcetd to validate the token properly return null; } const slice = idToken.split('.')[index]; const result = b64utoutf8(slice); return JSON.parse(result); } } TokenHelperService.decorators = [ { type: Injectable } ]; class SimpleOidcError extends Error { constructor(message, code, context) { super(message); this.code = code; this.context = context; this.name = code; } } class RequiredParemetersMissingError extends SimpleOidcError { constructor(paramName, context) { super(`Expected a valid value in provided parameter: ${paramName}`, 'required-param-missing', context); } } function validateObjectRequiredProps(obj, props) { for (const key of props) { if (!obj.hasOwnProperty(key)) { throw new RequiredParemetersMissingError(key.toString(), { object: obj, requiredProps: props }); } } } class TokenUrlService { constructor(tokenCrypto) { this.tokenCrypto = tokenCrypto; } createAuthorizationCodeRequestPayload(params) { let httpParams = new HttpParams() .set('client_id', params.clientId) .set('client_secret', params.clientSecret) .set('grant_type', 'authorization_code') .set('code_verifier', params.codeVerifier) .set('code', params.code) .set('redirect_uri', params.redirectUri); if (params.scope) { httpParams = httpParams .set('scope', params.scope); } if (params.acrValues) { httpParams = httpParams .set('acr_values', params.acrValues); } return httpParams.toString(); } createRefreshTokenRequestPayload(params) { let httpParams = new HttpParams() .set('client_id', params.clientId) .set('client_secret', params.clientSecret) .set('grant_type', 'refresh_token') .set('refresh_token', params.refreshToken); if (params.scope) { httpParams = httpParams .set('scope', params.scope); } if (params.acrValues) { httpParams = httpParams .set('acr_values', params.acrValues); } return httpParams.toString(); } createAuthorizeUrl(authorizeEndpointUrl, params) { if (!authorizeEndpointUrl || !authorizeEndpointUrl.length) { throw new RequiredParemetersMissingError(`authorizeEndpointUrl`, arguments); } if (!params) { throw new RequiredParemetersMissingError(`params`, arguments); } validateObjectRequiredProps(params, ['clientId', 'redirectUri', 'scope', 'responseType']); const state = this.tokenCrypto.generateState(); const nonce = this.tokenCrypto.generateNonce(); const verification = this.tokenCrypto.generateCodesForCodeVerification(); let httpParams = new HttpParams() .set('client_id', params.clientId) .set('scope', params.scope) .set('redirect_uri', params.redirectUri) .set('response_type', params.responseType) .set('state', state) .set('nonce', nonce) .set('code_challenge', verification.codeChallenge) .set('code_challenge_method', verification.method); if (params.prompt) { httpParams = httpParams.set('prompt', params.prompt); } if (params.loginHint) { httpParams = httpParams.set('login_hint', params.loginHint); } if (params.uiLocales) { httpParams = httpParams.set('ui_locales', params.uiLocales); } if (params.acrValues) { httpParams = httpParams.set('acr_values', params.acrValues); } if (params.idTokenHint) { httpParams = httpParams.set('id_token_hint', params.idTokenHint); } if (params.display) { httpParams = httpParams.set('display', params.display); } const url = `${authorizeEndpointUrl}?${httpParams}`; return { nonce, state, codeVerifier: verification.codeVerifier, codeChallenge: verification.codeChallenge, url, }; } createEndSessionUrl(endSessionEndpointUrl, params = {}) { if (!endSessionEndpointUrl || !endSessionEndpointUrl.length) { throw new RequiredParemetersMissingError(`endSessionEndpointUrl`, arguments); } const state = this.tokenCrypto.generateState(); let httpParams = new HttpParams() .set('state', state); if (params.idTokenHint) { httpParams = httpParams .set('id_token_hint', params.idTokenHint); } if (params.postLogoutRedirectUri) { httpParams = httpParams .set('post_logout_redirect_uri', params.postLogoutRedirectUri); } const url = `${endSessionEndpointUrl}?${httpParams}`; return { url, state }; } parseAuthorizeCallbackParamsFromUrl(url) { if (!url || !url.length) { throw new RequiredParemetersMissingError(`url`, arguments); } const paramsError = new RequiredParemetersMissingError(`url must have params`, arguments); if (!url.includes('?')) { throw paramsError; } const params = new HttpParams({ fromString: url.split('?')[1], }); if (!params.keys().length) { throw paramsError; } return { code: params.get('code'), state: params.get('state'), error: params.get('error'), sessionState: params.get('session_state') }; } } TokenUrlService.decorators = [ { type: Injectable } ]; TokenUrlService.ctorParameters = () => [ { type: TokenCryptoService } ]; class TokenValidationError extends SimpleOidcError { } class IdentityTokenMalformedError extends TokenValidationError { constructor(context) { super('Identity token format invalid: it needs to have three dots. (header.payload.signature)', 'id-token-invalid-format', context); } } class JWTKeysMissingError extends TokenValidationError { constructor(context) { super('Provided JWT Keys are empty or invalid', 'jwt-keys-empty', context); } } class JWTKeysInvalidError extends TokenValidationError { constructor(context) { super('Failed to find a valid key from provided JWT Keys. No key with kty=RSA and use=sig found.', 'jwt-keys-invalid', context); } } class InvalidSignatureError extends TokenValidationError { constructor(context) { super('Failed to validate signature against any of the JWT keys', 'invalid-signature', context); } } class SignatureAlgorithmNotSupportedError extends TokenValidationError { constructor(context) { super('Only "RS256" alg is currently supported', 'signature-alg-not-supported', context); } } class ClaimRequiredError extends TokenValidationError { constructor(claim, context) { super(`Required claim ${claim} is missing`, `missing-claim`, context); } } class ClaimTypeInvalidError extends TokenValidationError { constructor(claim, expectedType, actualType, context) { super(`Claim ${claim} is expected to be ${expectedType} got ${actualType} instead.`, `invalid-claim-type`, context); } } class DateClaimInvalidError extends TokenValidationError { constructor(claim, context) { super(`Failed to parse claim ${claim} value into a Date object.`, `invalid-date-claim`, context); } } class IssuedAtValidationFailedError extends TokenValidationError { constructor(offset, context) { super(`Issued at (iat claim) validation failed. Token was expected to have been issued between ${offset} seconds offset`, `iat-validation-failed`, context); } } class IssuerValidationFailedError extends TokenValidationError { constructor(identityTokenIssuer, discoveryIssuer, context) { super( // tslint:disable-next-line: max-line-length `Issuer (iss) validation failed. Identity Token's iss (${identityTokenIssuer}) does not match discovery document's issuer (${discoveryIssuer})`, `iss-validation-failed`, context); } } class AudienceValidationFailedError extends TokenValidationError { constructor(identityTokenAud, clientId, context) { super( // tslint:disable-next-line: max-line-length `Audience (aud) validation failed. Identity Token's aud (${identityTokenAud}) does not include this client's ID (${clientId}). The token may not intended for this client.`, `aud-validation-failed`, context); } } class TokenExpiredError extends TokenValidationError { constructor(expiration, context) { super(`The token has already expired at ${expiration}`, `token-expired`, context); } } class AccessTokenHashValidationFailedError extends TokenValidationError { constructor(context) { super(`Access Token Hash (at_hash) validation has failed. at_hash does not match hash of access token`, `access-token-validation-failed`, context); } } class InvalidStateError extends SimpleOidcError { constructor(context) { super('State returned by IDP does not match local stored state.' + 'Are you performing multiple authorize calls at the same time?', 'invalid-state', context); } } class InvalidNonceError extends TokenValidationError { constructor(context) { super('Nonce returned by IDP does not match local stored nonce.' + 'Are you performing multiple authorize calls at the same time?', 'invalid-nonce', context); } } class AuthorizationCallbackFormatError extends SimpleOidcError { constructor(context) { super(`IDP redirected with invalid URL`, `authorize-callback-format`, context); } } class AuthorizationCallbackMissingParameterError extends SimpleOidcError { constructor(param, context) { super(`IDP redirected with invalid/missing parameters on the URL: ${param}`, `authorize-callback-missing-${param}`, context); } } class AuthorizationCallbackError extends SimpleOidcError { constructor(error, context) { super(`IDP returned an error after authorization redirection: ${error}`, `authorize-callback-error`, context); } } /** * Implements Identity and Access tokens validations according to the * Open ID Connect specification. * https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation * Inspiration taken from https://github.com/damienbod/angular-auth-oidc-client */ class TokenValidationService { constructor(tokenHelper, tokenCrypto) { this.tokenHelper = tokenHelper; this.tokenCrypto = tokenCrypto; } validateIdToken(thisClientId, idToken, decodedIdToken, nonce, discoveryDocument, jwtKeys, tokenValidationConfig) { // Apply all validation as defined on // https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation this.validateIdTokenSignature(idToken, jwtKeys); this.validateIdTokenNonce(decodedIdToken, nonce); this.validateIdTokenRequiredFields(decodedIdToken); this.validateIdTokenIssuedAt(decodedIdToken, tokenValidationConfig); this.validateIdTokenIssuer(decodedIdToken, discoveryDocument.issuer); this.validateIdTokenAud(decodedIdToken, thisClientId); this.validateIdTokenExpiration(decodedIdToken); } /** * The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery) * MUST exactly match the value of the iss (issuer) Claim. */ validateIdTokenIssuer(idToken, discoveryDocumentIssuer) { if (idToken.iss !== discoveryDocumentIssuer) { throw new IssuerValidationFailedError(idToken.iss, discoveryDocumentIssuer, { idToken, discoveryDocumentIssuer }); } } /** * Access Token Validation * Hash the octets of the ASCII representation of the access_token with the hash algorithm specified in JWA * for the alg Header Parameter of the ID Token's JOSE Header. * For instance, if the alg is RS256, the hash algorithm used is SHA-256. * Take the left- most half of the hash and base64url- encode it. * The value of at_hash in the ID Token MUST match the value produced in the previous step * if at_hash is present in the ID Token */ validateAccessToken(accessToken, idTokenAtHash) { // The at_hash is optional for the code flow if (!idTokenAtHash) { console.info(`No "at_hash" in Identity Token: Skipping access token validation.`); return; } const accessTokenHash = this.tokenCrypto.sha256b64First128Bits(accessToken); if (idTokenAtHash !== accessTokenHash) { throw new AccessTokenHashValidationFailedError({ accessToken, idTokenAtHash, calculatedHash: accessTokenHash }); } } /** * The Client MUST validate that the aud (audience) Claim contains * its client_id value registered at the Issuer identified by the iss (issuer) Claim as an audience. * The ID Token MUST be rejected if the ID Token does not list the Client as a valid audience, * or if it contains additional audiences not trusted by the Client */ validateIdTokenAud(idToken, thisClientId) { let aud = idToken.aud; if (!Array.isArray(aud)) { aud = [aud]; } const valid = aud.includes(thisClientId); if (!valid) { throw new AudienceValidationFailedError(aud.join(','), thisClientId, { idToken, thisClientId, aud }); } } /** * The Client MUST validate the signature of the ID Token according to JWS using the algorithm * specified in the alg Header Parameter of the JOSE Header. * The Client MUST use the keys provided by the Issuer. * The alg value SHOULD be RS256. * Validation of tokens using other signing algorithms is described in the * OpenID Connect Core 1.0 specification. */ validateIdTokenSignature(idToken, jwtKeys) { if (!jwtKeys || !jwtKeys.keys || !jwtKeys.keys.length) { throw new JWTKeysMissingError({ idToken, jwtKeys }); } const header = this.tokenHelper.getHeaderFromToken(idToken); if (header.alg !== 'RS256') { throw new SignatureAlgorithmNotSupportedError({ idToken, jwtKeys, header, }); } // Filter keys according to kty and use let keysToTry = jwtKeys.keys .filter(k => k.kty === 'RSA' && k.use === 'sig'); if (!keysToTry.length) { throw new JWTKeysInvalidError({ idToken, jwtKeys, header, keysToTry }); } // Token header may have a 'kid' claim (key id) // which determines which JWT key should be used for validation // If present, must be a case sensitive string. // https://tools.ietf.org/html/rfc7515#section-4.1.4 // https://tools.ietf.org/html/rfc7515#appendix-D let kid; if (header.kid && typeof header.kid === 'string' && header.kid.length) { if (keysToTry.some(k => k.kid === header.kid)) { // Threat the kid as a hint, prioritizing it's key // but still trying the other keys if the desired key fails. kid = header.kid; keysToTry = keysToTry.sort(k => k.kid === kid ? -1 : 1); } else { console.info(`Identity token's header contained 'kid' but no key with that kid was found on JWT Keys. Will still try to validate using other keys, if any. kid: ${header.kid}, ValidKeys kids: ${JSON.stringify(keysToTry.map(k => k.kid))}`); } } // Validate each key returning as soon as one suceeds for (const key of keysToTry) { if (this.tokenCrypto.verifySignature(key, idToken)) { if (kid && kid !== key.kid) { console.info(`Identity token's header contained 'kid' ${kid} but key signature was validated using key ${key.kid}`); } return; } } throw new InvalidSignatureError({ idToken, jwtKeys, header, keysToTry, kid }); } /** * The current time MUST be before the time represented by the exp Claim * (possibly allowing for some small leeway to account for clock skew) */ validateIdTokenExpiration(idToken, offsetSeconds) { this.validateTokenNumericClaim(idToken, 'exp'); const tokenExpirationDate = this.tokenHelper.convertTokenClaimToDate(idToken.exp); if (!tokenExpirationDate) { throw new DateClaimInvalidError('exp', { idToken, offsetSeconds, parsedDate: tokenExpirationDate }); } offsetSeconds = offsetSeconds || 0; const tokenExpirationMs = tokenExpirationDate.valueOf(); const maxDateMs = new Date().valueOf() - offsetSeconds * 1000; const tokenNotExpired = tokenExpirationMs > maxDateMs; if (!tokenNotExpired) { throw new TokenExpiredError(tokenExpirationDate, { idToken, offsetSeconds, tokenExpirationDate, tokenExpirationMs, maxDateMs, maxDate: new Date(maxDateMs) }); } } /** * The iat Claim can be used to reject tokens that were issued too far away from the current time, * limiting the amount of time that nonces need to be stored to prevent attacks. * The acceptable range is Client specific. */ validateIdTokenIssuedAt(idToken, config = {}) { if (config.disableIdTokenIATValidation) { console.info('Issued At validation has been disabled by configuration'); return; } this.validateTokenNumericClaim(idToken, 'iat'); const idTokenIATDate = this.tokenHelper.convertTokenClaimToDate(idToken.iat); if (!idTokenIATDate) { throw new DateClaimInvalidError('iat', { idToken, config, parsedDate: idTokenIATDate }); } const maxOffsetInMs = (config.idTokenIATOffsetAllowed || 5) * 1000; const now = new Date().valueOf(); const valid = (now - idTokenIATDate.valueOf()) < maxOffsetInMs; if (!valid) { throw new IssuedAtValidationFailedError(maxOffsetInMs / 1000, { idToken, config, iatDiff: now - idTokenIATDate.valueOf(), maxOffsetInMs, }); } } /** * The value of the nonce Claim MUST be checked to verify that it is the same value as the one * that was sent in the Authentication Request. * The Client SHOULD check the nonce value for replay attacks. * The precise method for detecting replay attacks is Client specific. */ validateIdTokenNonce(idToken, localNonce) { if (idToken.nonce !== localNonce) { throw new InvalidNonceError({ localNonce, idTokenNonce: idToken.nonce, idToken }); } } /** * iss * REQUIRED. Issuer Identifier for the Issuer of the response. * The iss value is a case-sensitive URL using the https scheme that contains scheme, host, * and optionally, port number and path components and no query or fragment components. * * sub * REQUIRED. Subject Identifier.Locally unique and never reassigned identifier within the Issuer for the End- User, * which is intended to be consumed by the Client, e.g., 24400320 or AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4. * It MUST NOT exceed 255 ASCII characters in length.The sub value is a case-sensitive string. * * aud * REQUIRED. Audience(s) that this ID Token is intended for. * It MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value. * It MAY also contain identifiers for other audiences.In the general case, the aud value is an array of case-sensitive strings. * In the common special case when there is one audience, the aud value MAY be a single case-sensitive string. * * exp * REQUIRED. Expiration time on or after which the ID Token MUST NOT be accepted for processing. * The processing of this parameter requires that the current date/ time MUST be before * the expiration date/ time listed in the value. * Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. * Its value is a JSON [RFC7159] number representing the number of seconds from 1970- 01 - 01T00: 00:00Z * as measured in UTC until the date/ time. * See RFC 3339 [RFC3339] for details regarding date/ times in general and UTC in particular. * * iat * REQUIRED. Time at which the JWT was issued. Its value is a JSON number representing the number of seconds * from 1970- 01 - 01T00: 00:00Z as measured * in UTC until the date/ time. */ validateIdTokenRequiredFields(idToken) { const requiredClaims = ['iss', 'sub', 'aud', 'exp', 'iat']; for (const key of requiredClaims) { if (!idToken.hasOwnProperty(key)) { throw new ClaimRequiredError(key, { idToken, requiredClaims, missingClaim: key }); } } } /** * Validates that an expected token numeric field is a number on runtime. */ validateTokenNumericClaim(idToken, claim) { if (typeof idToken[claim] !== 'number') { if (!idToken[claim]) { throw new ClaimRequiredError(claim.toString(), { idToken, requiredClaim: claim }); } else { throw new ClaimTypeInvalidError(claim.toString(), 'number', typeof (idToken[claim]), { idToken, claim, claimType: typeof (idToken[claim]), claimExpectedType: 'number' }); } } } /** * Makes sure that the format of the identity token is correct. * It needs to be a non-empty string and contain three dots */ validateIdTokenFormat(idToken) { if (!idToken || !idToken.length) { throw new RequiredParemetersMissingError('idToken', null); } const expectedSliceAmount = 3; const slices = idToken.split('.'); if (slices.length !== expectedSliceAmount) { throw new IdentityTokenMalformedError({ idToken }); } } /** * Validates the local state against the * returned state from the IDP to make sure it matches */ validateAuthorizeCallbackState(localState, state) { if (state !== localState) { throw new InvalidStateError({ localState, returnedState: state, }); } } validateAuthorizeCallbackFormat(code, state, error, href) { if (typeof error === 'string') { throw new AuthorizationCallbackError(error, { url: href, error, }); } if (typeof code !== 'string') { throw new AuthorizationCallbackMissingParameterError('code', { url: href, }); } if (typeof state !== 'string') { throw new AuthorizationCallbackMissingParameterError('state', { url: href, }); } } } TokenValidationService.decorators = [ { type: Injectable } ]; TokenValidationService.ctorParameters = () => [ { type: TokenHelperService }, { type: TokenCryptoService } ]; class IssuerValidationError extends TokenValidationError { constructor(originalIssuer, newIssuer, context) { super( // tslint:disable-next-line: max-line-length `Issuer (iss) validation failed. Original Identity Token's iss (${originalIssuer}) does not match new token's issuer (${newIssuer})`, `iss-validation-failed-refresh`, context); } } class SubjectValidationError extends TokenValidationError { constructor(originalSubject, newSubject, context) { super( // tslint:disable-next-line: max-line-length `Subject (sub) validation failed. Original Identity Token's sub (${originalSubject}) does not match new token's sub (${newSubject})`, `sub-validation-failed-refresh`, context); } } class IssuedAtValidationError extends TokenValidationError { constructor(context) { super(`Issued At (iat) validation failed. Expected new iat to represent the time of the new token creation.`, `iat-validation-failed-refresh`, context); } } class AudienceValidationError extends TokenValidationError { constructor(context) { super(`Audience (aud) validation failed. Original Identity Token's aud does not match new token's aud`, `aud-validation-failed-refresh`, context); } } class AuthTimeValidationError extends TokenValidationError { constructor(context) { super(`Auth Time (auth_time) validation failed. Original Identity Token's auth_time does not match new token's auth_time`, `auth-time-validation-failed-refresh`, context); } } class AuthorizedPartyValidationError extends TokenValidationError { constructor(context) { super(`Authorized Party (azp) validation failed. Original Identity Token's azp does not match new token's azp`, `azp-validation-failed-refresh`, context); } } /** * its iss Claim Value MUST be the same as in the ID Token issued when the original authentication occurred, * its sub Claim Value MUST be the same as in the ID Token issued when the original authentication occurred, * its iat Claim MUST represent the time that the new ID Token is issued, * its aud Claim Value MUST be the same as in the ID Token issued when the original authentication occurred, * if the ID Token contains an auth_time Claim, its value MUST represent the time * of the original authentication - not the time that the new ID token is issued, * its azp Claim Value MUST be the same as in the ID Token issued when the original authentication occurred; * if no azp Claim was present in the original ID Token, one MUST NOT be present in the new ID Token, and * otherwise, the same rules apply as apply when issuing an ID Token at the time of the original authentication. */ class RefreshTokenValidationService { /** * Perform validations according to * 12.2. Successful Refresh Response * https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens */ validateIdToken(originalIdToken, newIdToken) { this.validateIssuer(originalIdToken, newIdToken); this.validateSubject(originalIdToken, newIdToken); this.validateIssuedAt(originalIdToken, newIdToken); this.validateAudience(originalIdToken, newIdToken); this.validateAuthTime(originalIdToken, newIdToken); this.validateAuthorizedParty(originalIdToken, newIdToken); } /** * its iss Claim Value MUST be the same as in the ID Token issued when the original authentication occurred */ validateIssuer(originalIdToken, newIdToken) { if (originalIdToken.iss !== newIdToken.iss) { throw new IssuerValidationError(originalIdToken.iss, newIdToken.iss, { originalIdToken, newIdToken }); } } /** * its sub Claim Value MUST be the same as in the ID Token issued when the original authentication occurred, */ validateSubject(originalIdToken, newIdToken) { if (originalIdToken.sub !== newIdToken.sub) { throw new SubjectValidationError(originalIdToken.sub, newIdToken.sub, { originalIdToken, newIdToken }); } } /** * its iat Claim MUST represent the time that the new ID Token is issued, */ validateIssuedAt(originalIdToken, newIdToken) { if (!(newIdToken.iat >= originalIdToken.iat)) { throw new IssuedAtValidationError({ originalIdToken, newIdToken }); } } /** * its aud Claim Value MUST be the same as in the ID Token issued when the original authentication occurred, */ validateAudience(originalIdToken, newIdToken) { if (originalIdToken.aud !== newIdToken.aud) { throw new AudienceValidationError({ originalIdToken, newIdToken }); } } /** * if the ID Token contains an auth_time Claim, its value MUST represent * the time of the original authentication - not the time that the new ID token is issued, */ validateAuthTime(originalIdToken, newIdToken) { if (newIdToken.auth_time && (originalIdToken.auth_time !== newIdToken.auth_time)) { throw new AuthTimeValidationError({ originalIdToken, newIdToken }); } } /** * its azp Claim Value MUST be the same as in the ID Token issued when the original authentication occurred; * if no azp Claim was present in the original ID Token, one MUST NOT be present in the new ID Token, and * otherwise, the same rules apply as apply when issuing an ID Token at the time of the original authentication. */ validateAuthorizedParty(originalIdToken, newIdToken) { if (originalIdToken.azp !== newIdToken.azp) { throw new AuthorizedPartyValidationError({ originalIdToken, newIdToken }); } } } RefreshTokenValidationService.decorators = [ { type: Injectable } ]; class AngularSimpleOidcCoreModule { } AngularSimpleOidcCoreModule.decorators = [ { type: NgModule, args: [{ imports: [], providers: [ TokenCryptoService, TokenUrlService, TokenHelperService, TokenValidationService, RefreshTokenValidationService, ], declarations: [], },] } ]; var TokenStorageKeys; (function (TokenStorageKeys) { TokenStorageKeys["Nonce"] = "simple.oidc.nonce"; TokenStorageKeys["State"] = "simple.oidc.state"; TokenStorageKeys["CodeVerifier"] = "simple.oidc.code-verifier"; TokenStorageKeys["AuthorizationCode"] = "simple.oidc.authorization-code"; TokenStorageKeys["SessionState"] = "simple.oidc.session-state"; TokenStorageKeys["OriginalIdentityToken"] = "simple.oidc.original-identity-token"; TokenStorageKeys["IdentityToken"] = "simple.oidc.identity-token"; TokenStorageKeys["IdentityTokenDecoded"] = "simple.oidc.identity-token-decoded"; TokenStorageKeys["AccessToken"] = "simple.oidc.access-token"; TokenStorageKeys["RefreshToken"] = "simple.oidc.refresh-token"; TokenStorageKeys["AccessTokenExpiration"] = "simple.oidc.access-token-expiration"; TokenStorageKeys["PreRedirectUrl"] = "simple.oidc.pre-redirect-url"; })(TokenStorageKeys || (TokenStorageKeys = {})); /** * Generated bundle index. Do not edit. */ export { AccessTokenHashValidationFailedError, AngularSimpleOidcCoreModule, AudienceValidationError, AudienceValidationFailedError, AuthTimeValidationError, AuthorizationCallbackError, AuthorizationCallbackFormatError, AuthorizationCallbackMissingParameterError, AuthorizedPartyValidationError, ClaimRequiredError, ClaimTypeInvalidError, DateClaimInvalidError, IdentityTokenMalformedError, InvalidNonceError, InvalidSignatureError, InvalidStateError, IssuedAtValidationError, IssuedAtValidationFailedError, IssuerValidationError, IssuerValidationFailedError, JWTKeysInvalidError, JWTKeysMissingError, RefreshTokenValidationService, RequiredParemetersMissingError, SignatureAlgorithmNotSupportedError, SimpleOidcError, SubjectValidationError, TokenCryptoService, TokenExpiredError, TokenHelperService, TokenStorageKeys, TokenUrlService, TokenValidationError, TokenValidationService }; //# sourceMappingURL=angular-simple-oidc-core.js.map