UNPKG

@okta-dfuhriman/okta-auth-js

Version:
101 lines (90 loc) 3.81 kB
/* eslint-disable complexity */ /*! * Copyright (c) 2015-present, Okta, Inc. and/or its affiliates. All rights reserved. * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (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 { getWellKnown } from '../endpoints/well-known'; import { AuthSdkError } from '../../errors'; import { clone } from '../../util/'; import { OktaAuthOAuthInterface, TokenParams } from '../types'; import { getDefaultTokenParams } from './defaultTokenParams'; import { DEFAULT_CODE_CHALLENGE_METHOD } from '../../constants'; import PKCE from './pkce'; import { preparePAR } from '.'; import { OktaAuthBaseInterface } from '../../base/types'; export function assertPKCESupport(sdk: OktaAuthBaseInterface) { if (!sdk.features.isPKCESupported()) { var errorMessage = 'PKCE requires a modern browser with encryption support running in a secure context.'; if (!sdk.features.isHTTPS()) { // eslint-disable-next-line max-len errorMessage += '\nThe current page is not being served with HTTPS protocol. PKCE requires secure HTTPS protocol.'; } if (!sdk.features.hasTextEncoder()) { // eslint-disable-next-line max-len errorMessage += '\n"TextEncoder" is not defined. To use PKCE, you may need to include a polyfill/shim for this browser.'; } throw new AuthSdkError(errorMessage); } } export async function validateCodeChallengeMethod(sdk: OktaAuthOAuthInterface, codeChallengeMethod?: string) { // set default code challenge method, if none provided codeChallengeMethod = codeChallengeMethod || sdk.options.codeChallengeMethod || DEFAULT_CODE_CHALLENGE_METHOD; // validate against .well-known/openid-configuration const wellKnownResponse = await getWellKnown(sdk); var methods = wellKnownResponse['code_challenge_methods_supported'] || []; if (methods.indexOf(codeChallengeMethod) === -1) { throw new AuthSdkError('Invalid code_challenge_method'); } return codeChallengeMethod; } export async function preparePKCE( sdk: OktaAuthOAuthInterface, tokenParams: TokenParams ): Promise<TokenParams> { let { codeVerifier, codeChallenge, codeChallengeMethod } = tokenParams; // PKCE calculations can be avoided by passing a codeChallenge codeChallenge = codeChallenge || sdk.options.codeChallenge; if (!codeChallenge) { assertPKCESupport(sdk); codeVerifier = codeVerifier || PKCE.generateVerifier(); codeChallenge = await PKCE.computeChallenge(codeVerifier); } codeChallengeMethod = await validateCodeChallengeMethod(sdk, codeChallengeMethod); // Clone/copy the params. Set PKCE values tokenParams = { ...tokenParams, responseType: 'code', // responseType is forced codeVerifier, codeChallenge, codeChallengeMethod }; return tokenParams; } // Prepares params for a call to /authorize or /token export async function prepareTokenParams( sdk: OktaAuthOAuthInterface, tokenParams: TokenParams = {} ): Promise<TokenParams> { // build params using defaults + options tokenParams = Object.assign({}, getDefaultTokenParams(sdk), clone(tokenParams)); if (tokenParams?.pushAuthorizationRequest) { return preparePAR(sdk, tokenParams); } if (tokenParams.pkce === false) { // Implicit flow or authorization_code without PKCE return tokenParams; } return preparePKCE(sdk, tokenParams); }