@asgardeo/auth-js
Version:
Asgardeo Auth JS SDK to be used in JavaScript and TypeScript applications.
467 lines • 26.7 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/**
* Copyright (c) 2020, WSO2 LLC. (https://www.wso2.com). All Rights Reserved.
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { AUTHORIZATION_ENDPOINT, FetchCredentialTypes, OIDC_SCOPE, OP_CONFIG_INITIATED, SESSION_STATE, SIGN_OUT_SUCCESS_PARAM, STATE } from "../constants";
import { AsgardeoAuthException } from "../exception";
import { AuthenticationHelper, CryptoHelper } from "../helpers";
import { AuthenticationUtils } from "../utils";
export class AuthenticationCore {
constructor(dataLayer, cryptoUtils) {
this._cryptoUtils = cryptoUtils;
this._cryptoHelper = new CryptoHelper(cryptoUtils);
this._authenticationHelper = new AuthenticationHelper(dataLayer, this._cryptoHelper);
this._dataLayer = dataLayer;
this._config = () => __awaiter(this, void 0, void 0, function* () { return yield this._dataLayer.getConfigData(); });
this._oidcProviderMetaData = () => __awaiter(this, void 0, void 0, function* () { return yield this._dataLayer.getOIDCProviderMetaData(); });
}
getAuthorizationURLParams(config, userID) {
var _a, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
const configData = yield this._config();
const authorizeRequestParams = new Map();
authorizeRequestParams.set("response_type", "code");
authorizeRequestParams.set("client_id", configData.clientID);
if (configData.clientSecret && configData.clientSecret.trim().length > 0) {
authorizeRequestParams.set("client_secret", configData.clientSecret);
}
let scope = OIDC_SCOPE;
if (configData.scope && configData.scope.length > 0) {
if (!configData.scope.includes(OIDC_SCOPE)) {
configData.scope.push(OIDC_SCOPE);
}
scope = configData.scope.join(" ");
}
authorizeRequestParams.set("scope", scope);
authorizeRequestParams.set("redirect_uri", configData.signInRedirectURL);
if (configData.responseMode) {
authorizeRequestParams.set("response_mode", configData.responseMode);
}
const pkceKey = yield this._authenticationHelper.generatePKCEKey(userID);
if (configData.enablePKCE) {
const codeVerifier = (_a = this._cryptoHelper) === null || _a === void 0 ? void 0 : _a.getCodeVerifier();
const codeChallenge = (_b = this._cryptoHelper) === null || _b === void 0 ? void 0 : _b.getCodeChallenge(codeVerifier);
yield this._dataLayer.setTemporaryDataParameter(pkceKey, codeVerifier, userID);
authorizeRequestParams.set("code_challenge_method", "S256");
authorizeRequestParams.set("code_challenge", codeChallenge);
}
if (configData.prompt) {
authorizeRequestParams.set("prompt", configData.prompt);
}
const customParams = config;
if (customParams) {
for (const [key, value] of Object.entries(customParams)) {
if (key != "" && value != "" && key !== STATE) {
authorizeRequestParams.set(key, value.toString());
}
}
}
authorizeRequestParams.set(STATE, AuthenticationUtils.generateStateParamForRequestCorrelation(pkceKey, customParams ? (_c = customParams[STATE]) === null || _c === void 0 ? void 0 : _c.toString() : ""));
return authorizeRequestParams;
});
}
getAuthorizationURL(config, userID) {
return __awaiter(this, void 0, void 0, function* () {
const authorizeEndpoint = (yield this._dataLayer.getOIDCProviderMetaDataParameter(AUTHORIZATION_ENDPOINT));
if (!authorizeEndpoint || authorizeEndpoint.trim().length === 0) {
throw new AsgardeoAuthException("JS-AUTH_CORE-GAU-NF01", "No authorization endpoint found.", "No authorization endpoint was found in the OIDC provider meta data from the well-known endpoint " +
"or the authorization endpoint passed to the SDK is empty.");
}
const authorizeRequest = new URL(authorizeEndpoint);
const authorizeRequestParams = yield this.getAuthorizationURLParams(config, userID);
for (const [key, value] of authorizeRequestParams.entries()) {
authorizeRequest.searchParams.append(key, value);
}
return authorizeRequest.toString();
});
}
requestAccessToken(authorizationCode, sessionState, state, userID, tokenRequestConfig) {
return __awaiter(this, void 0, void 0, function* () {
const tokenEndpoint = (yield this._oidcProviderMetaData()).token_endpoint;
const configData = yield this._config();
if (!tokenEndpoint || tokenEndpoint.trim().length === 0) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RAT1-NF01", "Token endpoint not found.", "No token endpoint was found in the OIDC provider meta data returned by the well-known endpoint " +
"or the token endpoint passed to the SDK is empty.");
}
sessionState && (yield this._dataLayer.setSessionDataParameter(SESSION_STATE, sessionState, userID));
const body = new URLSearchParams();
body.set("client_id", configData.clientID);
if (configData.clientSecret && configData.clientSecret.trim().length > 0) {
body.set("client_secret", configData.clientSecret);
}
const code = authorizationCode;
body.set("code", code);
body.set("grant_type", "authorization_code");
body.set("redirect_uri", configData.signInRedirectURL);
if (tokenRequestConfig === null || tokenRequestConfig === void 0 ? void 0 : tokenRequestConfig.params) {
Object.entries(tokenRequestConfig.params).forEach(([key, value]) => {
body.append(key, value);
});
}
if (configData.enablePKCE) {
body.set("code_verifier", `${yield this._dataLayer.getTemporaryDataParameter(AuthenticationUtils.extractPKCEKeyFromStateParam(state), userID)}`);
yield this._dataLayer.removeTemporaryDataParameter(AuthenticationUtils.extractPKCEKeyFromStateParam(state), userID);
}
let tokenResponse;
try {
tokenResponse = yield fetch(tokenEndpoint, {
body: body,
credentials: configData.sendCookiesInRequests
? FetchCredentialTypes.Include
: FetchCredentialTypes.SameOrigin,
headers: new Headers(AuthenticationUtils.getTokenRequestHeaders()),
method: "POST"
});
}
catch (error) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RAT1-NE02", "Requesting access token failed", error !== null && error !== void 0 ? error : "The request to get the access token from the server failed.");
}
if (!tokenResponse.ok) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RAT1-HE03", `Requesting access token failed with ${tokenResponse.statusText}`, yield tokenResponse.json());
}
return yield this._authenticationHelper.handleTokenResponse(tokenResponse, userID);
});
}
refreshAccessToken(userID) {
return __awaiter(this, void 0, void 0, function* () {
const tokenEndpoint = (yield this._oidcProviderMetaData()).token_endpoint;
const configData = yield this._config();
const sessionData = yield this._dataLayer.getSessionData(userID);
if (!sessionData.refresh_token) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RAT2-NF01", "No refresh token found.", "There was no refresh token found. Asgardeo doesn't return a " +
"refresh token if the refresh token grant is not enabled.");
}
if (!tokenEndpoint || tokenEndpoint.trim().length === 0) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RAT2-NF02", "No refresh token endpoint found.", "No refresh token endpoint was in the OIDC provider meta data returned by the well-known " +
"endpoint or the refresh token endpoint passed to the SDK is empty.");
}
const body = [];
body.push(`client_id=${configData.clientID}`);
body.push(`refresh_token=${sessionData.refresh_token}`);
body.push("grant_type=refresh_token");
if (configData.clientSecret && configData.clientSecret.trim().length > 0) {
body.push(`client_secret=${configData.clientSecret}`);
}
let tokenResponse;
try {
tokenResponse = yield fetch(tokenEndpoint, {
body: body.join("&"),
credentials: configData.sendCookiesInRequests
? FetchCredentialTypes.Include
: FetchCredentialTypes.SameOrigin,
headers: new Headers(AuthenticationUtils.getTokenRequestHeaders()),
method: "POST"
});
}
catch (error) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RAT2-NR03", "Refresh access token request failed.", error !== null && error !== void 0 ? error : "The request to refresh the access token failed.");
}
if (!tokenResponse.ok) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RAT2-HE04", `Refreshing access token failed with ${tokenResponse.statusText}`, yield tokenResponse.json());
}
return this._authenticationHelper.handleTokenResponse(tokenResponse, userID);
});
}
revokeAccessToken(userID) {
return __awaiter(this, void 0, void 0, function* () {
const revokeTokenEndpoint = (yield this._oidcProviderMetaData()).revocation_endpoint;
const configData = yield this._config();
if (!revokeTokenEndpoint || revokeTokenEndpoint.trim().length === 0) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RAT3-NF01", "No revoke access token endpoint found.", "No revoke access token endpoint was found in the OIDC provider meta data returned by " +
"the well-known endpoint or the revoke access token endpoint passed to the SDK is empty.");
}
const body = [];
body.push(`client_id=${configData.clientID}`);
body.push(`token=${(yield this._dataLayer.getSessionData(userID)).access_token}`);
body.push("token_type_hint=access_token");
if (configData.clientSecret && configData.clientSecret.trim().length > 0) {
body.push(`client_secret=${configData.clientSecret}`);
}
let response;
try {
response = yield fetch(revokeTokenEndpoint, {
body: body.join("&"),
credentials: configData.sendCookiesInRequests
? FetchCredentialTypes.Include
: FetchCredentialTypes.SameOrigin,
headers: new Headers(AuthenticationUtils.getTokenRequestHeaders()),
method: "POST"
});
}
catch (error) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RAT3-NE02", "The request to revoke access token failed.", error !== null && error !== void 0 ? error : "The request sent to revoke the access token failed.");
}
if (response.status !== 200 || !response.ok) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RAT3-HE03", `Invalid response status received for revoke access token request (${response.statusText}).`, yield response.json());
}
this._authenticationHelper.clearUserSessionData(userID);
return Promise.resolve(response);
});
}
requestCustomGrant(customGrantParams, userID) {
return __awaiter(this, void 0, void 0, function* () {
const oidcProviderMetadata = yield this._oidcProviderMetaData();
const configData = yield this._config();
let tokenEndpoint;
if (customGrantParams.tokenEndpoint && customGrantParams.tokenEndpoint.trim().length !== 0) {
tokenEndpoint = customGrantParams.tokenEndpoint;
}
else {
tokenEndpoint = oidcProviderMetadata.token_endpoint;
}
if (!tokenEndpoint || tokenEndpoint.trim().length === 0) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RCG-NF01", "Token endpoint not found.", "No token endpoint was found in the OIDC provider meta data returned by the well-known endpoint " +
"or the token endpoint passed to the SDK is empty.");
}
const data = yield Promise.all(Object.entries(customGrantParams.data).map(([key, value]) => __awaiter(this, void 0, void 0, function* () {
const newValue = yield this._authenticationHelper.replaceCustomGrantTemplateTags(value, userID);
return `${key}=${newValue}`;
})));
let requestHeaders = Object.assign({}, AuthenticationUtils.getTokenRequestHeaders());
if (customGrantParams.attachToken) {
requestHeaders = Object.assign(Object.assign({}, requestHeaders), { Authorization: `Bearer ${(yield this._dataLayer.getSessionData(userID)).access_token}` });
}
const requestConfig = {
body: data.join("&"),
credentials: configData.sendCookiesInRequests
? FetchCredentialTypes.Include
: FetchCredentialTypes.SameOrigin,
headers: new Headers(requestHeaders),
method: "POST"
};
let response;
try {
response = yield fetch(tokenEndpoint, requestConfig);
}
catch (error) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RCG-NE02", "The custom grant request failed.", error !== null && error !== void 0 ? error : "The request sent to get the custom grant failed.");
}
if (response.status !== 200 || !response.ok) {
throw new AsgardeoAuthException("JS-AUTH_CORE-RCG-HE03", `Invalid response status received for the custom grant request. (${response.statusText})`, yield response.json());
}
if (customGrantParams.returnsSession) {
return this._authenticationHelper.handleTokenResponse(response, userID);
}
else {
return Promise.resolve(yield response.json());
}
});
}
getBasicUserInfo(userID) {
return __awaiter(this, void 0, void 0, function* () {
const sessionData = yield this._dataLayer.getSessionData(userID);
const authenticatedUser = this._authenticationHelper
.getAuthenticatedUserInfo(sessionData === null || sessionData === void 0 ? void 0 : sessionData.id_token);
let basicUserInfo = {
allowedScopes: sessionData.scope,
sessionState: sessionData.session_state
};
Object.keys(authenticatedUser).forEach((key) => {
if (authenticatedUser[key] === undefined ||
authenticatedUser[key] === "" ||
authenticatedUser[key] === null) {
delete authenticatedUser[key];
}
});
basicUserInfo = Object.assign(Object.assign({}, basicUserInfo), authenticatedUser);
return basicUserInfo;
});
}
getDecodedIDToken(userID) {
return __awaiter(this, void 0, void 0, function* () {
const idToken = (yield this._dataLayer.getSessionData(userID)).id_token;
const payload = this._cryptoHelper.decodeIDToken(idToken);
return payload;
});
}
getCryptoHelper() {
return __awaiter(this, void 0, void 0, function* () {
return this._cryptoHelper;
});
}
getIDToken(userID) {
return __awaiter(this, void 0, void 0, function* () {
return (yield this._dataLayer.getSessionData(userID)).id_token;
});
}
getOIDCProviderMetaData(forceInit) {
return __awaiter(this, void 0, void 0, function* () {
const configData = yield this._config();
if (!forceInit && (yield this._dataLayer.getTemporaryDataParameter(OP_CONFIG_INITIATED))) {
return Promise.resolve();
}
const wellKnownEndpoint = configData.wellKnownEndpoint;
if (wellKnownEndpoint) {
let response;
try {
response = yield fetch(wellKnownEndpoint);
if (response.status !== 200 || !response.ok) {
throw new Error();
}
}
catch (_a) {
throw new AsgardeoAuthException("JS-AUTH_CORE-GOPMD-HE01", "Invalid well-known response", "The well known endpoint response has been failed with an error.");
}
yield this._dataLayer.setOIDCProviderMetaData(yield this._authenticationHelper.resolveEndpoints(yield response.json()));
yield this._dataLayer.setTemporaryDataParameter(OP_CONFIG_INITIATED, true);
return Promise.resolve();
}
else if (configData.baseUrl) {
try {
yield this._dataLayer.setOIDCProviderMetaData(yield this._authenticationHelper.resolveEndpointsByBaseURL());
}
catch (error) {
throw new AsgardeoAuthException("JS-AUTH_CORE-GOPMD-IV02", "Resolving endpoints failed.", error !== null && error !== void 0 ? error : "Resolving endpoints by base url failed.");
}
yield this._dataLayer.setTemporaryDataParameter(OP_CONFIG_INITIATED, true);
return Promise.resolve();
}
else {
yield this._dataLayer.setOIDCProviderMetaData(yield this._authenticationHelper.resolveEndpointsExplicitly());
yield this._dataLayer.setTemporaryDataParameter(OP_CONFIG_INITIATED, true);
return Promise.resolve();
}
});
}
getOIDCServiceEndpoints() {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
return __awaiter(this, void 0, void 0, function* () {
const oidcProviderMetaData = yield this._oidcProviderMetaData();
return {
authorizationEndpoint: (_a = oidcProviderMetaData.authorization_endpoint) !== null && _a !== void 0 ? _a : "",
checkSessionIframe: (_b = oidcProviderMetaData.check_session_iframe) !== null && _b !== void 0 ? _b : "",
endSessionEndpoint: (_c = oidcProviderMetaData.end_session_endpoint) !== null && _c !== void 0 ? _c : "",
introspectionEndpoint: (_d = oidcProviderMetaData.introspection_endpoint) !== null && _d !== void 0 ? _d : "",
issuer: (_e = oidcProviderMetaData.issuer) !== null && _e !== void 0 ? _e : "",
jwksUri: (_f = oidcProviderMetaData.jwks_uri) !== null && _f !== void 0 ? _f : "",
registrationEndpoint: (_g = oidcProviderMetaData.registration_endpoint) !== null && _g !== void 0 ? _g : "",
revocationEndpoint: (_h = oidcProviderMetaData.revocation_endpoint) !== null && _h !== void 0 ? _h : "",
tokenEndpoint: (_j = oidcProviderMetaData.token_endpoint) !== null && _j !== void 0 ? _j : "",
userinfoEndpoint: (_k = oidcProviderMetaData.userinfo_endpoint) !== null && _k !== void 0 ? _k : ""
};
});
}
getSignOutURL(userID) {
var _a, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
const logoutEndpoint = (_a = (yield this._oidcProviderMetaData())) === null || _a === void 0 ? void 0 : _a.end_session_endpoint;
const configData = yield this._config();
if (!logoutEndpoint || logoutEndpoint.trim().length === 0) {
throw new AsgardeoAuthException("JS-AUTH_CORE-GSOU-NF01", "Sign-out endpoint not found.", "No sign-out endpoint was found in the OIDC provider meta data returned by the well-known endpoint " +
"or the sign-out endpoint passed to the SDK is empty.");
}
const callbackURL = (_b = configData === null || configData === void 0 ? void 0 : configData.signOutRedirectURL) !== null && _b !== void 0 ? _b : configData === null || configData === void 0 ? void 0 : configData.signInRedirectURL;
if (!callbackURL || callbackURL.trim().length === 0) {
throw new AsgardeoAuthException("JS-AUTH_CORE-GSOU-NF03", "No sign-out redirect URL found.", "The sign-out redirect URL cannot be found or the URL passed to the SDK is empty. " +
"No sign-in redirect URL has been found either. ");
}
const queryParams = new URLSearchParams();
queryParams.set("post_logout_redirect_uri", callbackURL);
if (configData.sendIdTokenInLogoutRequest) {
const idToken = (_c = (yield this._dataLayer.getSessionData(userID))) === null || _c === void 0 ? void 0 : _c.id_token;
if (!idToken || idToken.trim().length === 0) {
throw new AsgardeoAuthException("JS-AUTH_CORE-GSOU-NF02", "ID token not found.", "No ID token could be found. Either the session information is lost or you have not signed in.");
}
queryParams.set("id_token_hint", idToken);
}
else {
queryParams.set("client_id", configData.clientID);
}
queryParams.set("state", SIGN_OUT_SUCCESS_PARAM);
return `${logoutEndpoint}?${queryParams.toString()}`;
});
}
clearUserSessionData(userID) {
return __awaiter(this, void 0, void 0, function* () {
yield this._authenticationHelper.clearUserSessionData(userID);
});
}
getAccessToken(userID) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
return (_a = (yield this._dataLayer.getSessionData(userID))) === null || _a === void 0 ? void 0 : _a.access_token;
});
}
/**
* The created timestamp of the token response in milliseconds.
*
* @param userID - User ID
* @returns Created at timestamp of the token response in milliseconds.
*/
getCreatedAt(userID) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
return (_a = (yield this._dataLayer.getSessionData(userID))) === null || _a === void 0 ? void 0 : _a.created_at;
});
}
/**
* The expires timestamp of the token response in seconds.
*
* @param userID - User ID
* @returns Expires in timestamp of the token response in seconds.
*/
getExpiresIn(userID) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
return (_a = (yield this._dataLayer.getSessionData(userID))) === null || _a === void 0 ? void 0 : _a.expires_in;
});
}
isAuthenticated(userID) {
return __awaiter(this, void 0, void 0, function* () {
const isAccessTokenAvailable = Boolean(yield this.getAccessToken(userID));
// Check if the access token is expired.
const createdAt = yield this.getCreatedAt(userID);
// Get the expires in value.
const expiresInString = yield this.getExpiresIn(userID);
// If the expires in value is not available, the token is invalid and the user is not authenticated.
if (!expiresInString) {
return false;
}
// Convert to milliseconds.
const expiresIn = parseInt(expiresInString) * 1000;
const currentTime = new Date().getTime();
const isAccessTokenValid = (createdAt + expiresIn) > currentTime;
const isAuthenticated = isAccessTokenAvailable && isAccessTokenValid;
return isAuthenticated;
});
}
getPKCECode(state, userID) {
return __awaiter(this, void 0, void 0, function* () {
return (yield this._dataLayer.getTemporaryDataParameter(AuthenticationUtils.extractPKCEKeyFromStateParam(state), userID));
});
}
setPKCECode(pkce, state, userID) {
return __awaiter(this, void 0, void 0, function* () {
return yield this._dataLayer.setTemporaryDataParameter(AuthenticationUtils.extractPKCEKeyFromStateParam(state), pkce, userID);
});
}
updateConfig(config) {
return __awaiter(this, void 0, void 0, function* () {
yield this._dataLayer.setConfigData(config);
yield this.getOIDCProviderMetaData(true);
});
}
}
//# sourceMappingURL=authentication-core.js.map