UNPKG

@byteowls/capacitor-oauth2

Version:
219 lines 9.35 kB
// import sha256 from "fast-sha256"; export class WebUtils { /** * Public only for testing */ static getAppId(options) { return this.getOverwritableValue(options, "appId"); } static getOverwritableValue(options, key) { let base = options[key]; if (options.web && key in options.web) { base = options.web[key]; } return base; } /** * Public only for testing */ static getAuthorizationUrl(options) { let url = options.authorizationBaseUrl + "?client_id=" + options.appId; url += "&response_type=" + options.responseType; if (options.redirectUrl) { url += "&redirect_uri=" + options.redirectUrl; } if (options.scope) { url += "&scope=" + options.scope; } url += "&state=" + options.state; if (options.additionalParameters) { for (const key in options.additionalParameters) { url += "&" + key + "=" + options.additionalParameters[key]; } } if (options.pkceCodeChallenge) { url += "&code_challenge=" + options.pkceCodeChallenge; url += "&code_challenge_method=" + options.pkceCodeChallengeMethod; } return encodeURI(url); } static getTokenEndpointData(options, code) { let body = ''; body += encodeURIComponent('grant_type') + '=' + encodeURIComponent('authorization_code') + '&'; body += encodeURIComponent('client_id') + '=' + encodeURIComponent(options.appId) + '&'; body += encodeURIComponent('redirect_uri') + '=' + encodeURIComponent(options.redirectUrl) + '&'; body += encodeURIComponent('code') + '=' + encodeURIComponent(code) + '&'; body += encodeURIComponent('code_verifier') + '=' + encodeURIComponent(options.pkceCodeVerifier); return body; } /** * Public only for testing */ static getUrlParams(url) { const urlString = `${url !== null && url !== void 0 ? url : ''}`.trim(); if (urlString.length === 0) { return; } const parsedUrl = new URL(urlString); if (!parsedUrl.search && !parsedUrl.hash) { return; } let urlParamStr; if (parsedUrl.search) { urlParamStr = parsedUrl.search.substr(1); } else { urlParamStr = parsedUrl.hash.substr(1); } const keyValuePairs = urlParamStr.split(`&`); // @ts-ignore return keyValuePairs.reduce((accumulator, currentValue) => { const [key, val] = currentValue.split(`=`); if (key && key.length > 0) { return Object.assign(Object.assign({}, accumulator), { [key]: decodeURIComponent(val) }); } }, {}); } static randomString(length = 10) { const haystack = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; let randomStr; if (window.crypto) { let numberArray = new Uint32Array(length); window.crypto.getRandomValues(numberArray); numberArray = numberArray.map(x => haystack.charCodeAt(x % haystack.length)); let stringArray = []; numberArray.forEach(x => { stringArray.push(haystack.charAt(x % haystack.length)); }); randomStr = stringArray.join(""); } else { randomStr = ""; for (let i = 0; i < length; i++) { randomStr += haystack.charAt(Math.floor(Math.random() * haystack.length)); } } return randomStr; } static async buildWebOptions(configOptions) { const webOptions = new WebOptions(); webOptions.appId = this.getAppId(configOptions); webOptions.authorizationBaseUrl = this.getOverwritableValue(configOptions, "authorizationBaseUrl"); webOptions.responseType = this.getOverwritableValue(configOptions, "responseType"); if (!webOptions.responseType) { webOptions.responseType = "token"; } webOptions.redirectUrl = this.getOverwritableValue(configOptions, "redirectUrl"); // controlling parameters webOptions.resourceUrl = this.getOverwritableValue(configOptions, "resourceUrl"); webOptions.accessTokenEndpoint = this.getOverwritableValue(configOptions, "accessTokenEndpoint"); webOptions.pkceEnabled = this.getOverwritableValue(configOptions, "pkceEnabled"); if (webOptions.pkceEnabled) { webOptions.pkceCodeVerifier = this.randomString(64); if (CryptoUtils.HAS_SUBTLE_CRYPTO) { await CryptoUtils.deriveChallenge(webOptions.pkceCodeVerifier).then(c => { webOptions.pkceCodeChallenge = c; webOptions.pkceCodeChallengeMethod = "S256"; }); } else { webOptions.pkceCodeChallenge = webOptions.pkceCodeVerifier; webOptions.pkceCodeChallengeMethod = "plain"; } } webOptions.scope = this.getOverwritableValue(configOptions, "scope"); webOptions.state = this.getOverwritableValue(configOptions, "state"); if (!webOptions.state || webOptions.state.length === 0) { webOptions.state = this.randomString(20); } let parametersMapHelper = this.getOverwritableValue(configOptions, "additionalParameters"); if (parametersMapHelper) { webOptions.additionalParameters = {}; for (const key in parametersMapHelper) { if (key && key.trim().length > 0) { let value = parametersMapHelper[key]; if (value && value.trim().length > 0) { webOptions.additionalParameters[key] = value; } } } } let headersMapHelper = this.getOverwritableValue(configOptions, "additionalResourceHeaders"); if (headersMapHelper) { webOptions.additionalResourceHeaders = {}; for (const key in headersMapHelper) { if (key && key.trim().length > 0) { let value = headersMapHelper[key]; if (value && value.trim().length > 0) { webOptions.additionalResourceHeaders[key] = value; } } } } webOptions.logsEnabled = this.getOverwritableValue(configOptions, "logsEnabled"); return webOptions; } static buildWindowOptions(configOptions) { const windowOptions = new WebOptions(); if (configOptions.web) { if (configOptions.web.windowOptions) { windowOptions.windowOptions = configOptions.web.windowOptions; } if (configOptions.web.windowTarget) { windowOptions.windowTarget = configOptions.web.windowTarget; } } return windowOptions; } } export class CryptoUtils { static toUint8Array(str) { const buf = new ArrayBuffer(str.length); const bufView = new Uint8Array(buf); for (let i = 0; i < str.length; i++) { bufView[i] = str.charCodeAt(i); } return bufView; } static toBase64Url(base64) { return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); } static toBase64(bytes) { let len = bytes.length; let base64 = ""; for (let i = 0; i < len; i += 3) { base64 += this.BASE64_CHARS[bytes[i] >> 2]; base64 += this.BASE64_CHARS[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; base64 += this.BASE64_CHARS[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; base64 += this.BASE64_CHARS[bytes[i + 2] & 63]; } if ((len % 3) === 2) { base64 = base64.substring(0, base64.length - 1) + "="; } else if (len % 3 === 1) { base64 = base64.substring(0, base64.length - 2) + "=="; } return base64; } static deriveChallenge(codeVerifier) { if (codeVerifier.length < 43 || codeVerifier.length > 128) { return Promise.reject(new Error('ERR_PKCE_CODE_VERIFIER_INVALID_LENGTH')); } if (!CryptoUtils.HAS_SUBTLE_CRYPTO) { return Promise.reject(new Error('ERR_PKCE_CRYPTO_NOTSUPPORTED')); } return new Promise((resolve, reject) => { crypto.subtle.digest('SHA-256', this.toUint8Array(codeVerifier)).then(arrayBuffer => { return resolve(this.toBase64Url(this.toBase64(new Uint8Array(arrayBuffer)))); }, error => reject(error)); }); } } CryptoUtils.BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; CryptoUtils.HAS_SUBTLE_CRYPTO = typeof window !== 'undefined' && !!window.crypto && !!window.crypto.subtle; export class WebOptions { constructor() { this.windowTarget = "_blank"; } } //# sourceMappingURL=web-utils.js.map