@okta/okta-auth-js
Version:
The Okta Auth SDK
390 lines (347 loc) • 9.83 kB
JavaScript
exports.run = run;
var _interact = require("./interact");
var _introspect = require("./introspect");
var _remediate = require("./remediate");
var _flow = require("./flow");
var _types = require("./types");
var _transactionMeta = require("./transactionMeta");
var _util = require("./util");
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/*!
* 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.
*/
/* eslint-disable max-statements, complexity, max-depth */
function initializeValues(options) {
// remove known options, everything else is assumed to be a value
const knownOptions = ['flow', 'remediators', 'actions', 'withCredentials', 'step', 'useGenericRemediator', 'exchangeCodeForTokens'];
const values = { ...options
};
knownOptions.forEach(option => {
delete values[option];
});
return values;
}
function initializeData(authClient, data) {
let {
options
} = data;
options = { ...authClient.options.idx,
...options
};
let {
flow,
withCredentials,
remediators,
actions
} = options;
const status = _types.IdxStatus.PENDING; // certain options can be set by the flow specification
flow = flow || authClient.idx.getFlow() || 'default';
if (flow) {
authClient.idx.setFlow(flow);
const flowSpec = (0, _flow.getFlowSpecification)(authClient, flow); // Favor option values over flow spec
withCredentials = typeof withCredentials !== 'undefined' ? withCredentials : flowSpec.withCredentials;
remediators = remediators || flowSpec.remediators;
actions = actions || flowSpec.actions;
}
return { ...data,
options: { ...options,
flow,
withCredentials,
remediators,
actions
},
status
};
}
async function getDataFromIntrospect(authClient, data) {
const {
options
} = data;
const {
stateHandle,
withCredentials,
version,
state,
scopes,
recoveryToken,
activationToken,
maxAge,
nonce
} = options;
let idxResponse;
let meta = (0, _transactionMeta.getSavedTransactionMeta)(authClient, {
state,
recoveryToken,
activationToken
}); // may be undefined
if (stateHandle) {
idxResponse = await (0, _introspect.introspect)(authClient, {
withCredentials,
version,
stateHandle
});
} else {
var _meta;
let interactionHandle = (_meta = meta) === null || _meta === void 0 ? void 0 : _meta.interactionHandle; // may be undefined
if (!interactionHandle) {
// start a new transaction
authClient.transactionManager.clear();
const interactResponse = await (0, _interact.interact)(authClient, {
withCredentials,
state,
scopes,
activationToken,
recoveryToken,
maxAge,
nonce
});
interactionHandle = interactResponse.interactionHandle;
meta = interactResponse.meta;
} // Introspect to get idx response
idxResponse = await (0, _introspect.introspect)(authClient, {
withCredentials,
version,
interactionHandle
});
}
return { ...data,
idxResponse,
meta
};
}
async function getDataFromRemediate(authClient, data) {
let {
idxResponse,
options,
values
} = data;
const {
autoRemediate,
remediators,
actions,
flow,
step,
useGenericRemediator
} = options;
const shouldRemediate = autoRemediate !== false && (remediators || actions || step);
if (!shouldRemediate) {
return data;
}
values = { ...values,
stateHandle: idxResponse.rawIdxState.stateHandle
}; // Can we handle the remediations?
const {
idxResponse: idxResponseFromRemediation,
nextStep,
canceled
} = await (0, _remediate.remediate)(authClient, idxResponse, values, {
remediators,
actions,
flow,
step,
useGenericRemediator
});
idxResponse = idxResponseFromRemediation;
return { ...data,
idxResponse,
nextStep,
canceled
};
}
async function getTokens(authClient, data) {
let {
meta,
idxResponse
} = data;
const {
interactionCode
} = idxResponse;
const {
clientId,
codeVerifier,
ignoreSignature,
redirectUri,
urls,
scopes
} = meta;
const tokenResponse = await authClient.token.exchangeCodeForTokens({
interactionCode,
clientId,
codeVerifier,
ignoreSignature,
redirectUri,
scopes
}, urls);
return tokenResponse.tokens;
}
async function finalizeData(authClient, data) {
let {
options,
idxResponse,
canceled,
status
} = data;
const {
exchangeCodeForTokens
} = options;
let shouldSaveResponse = false;
let shouldClearTransaction = false;
let clearSharedStorage = true;
let interactionCode;
let tokens;
let enabledFeatures;
let availableSteps;
let messages;
let terminal;
if (idxResponse) {
shouldSaveResponse = !!(idxResponse.requestDidSucceed || idxResponse.stepUp);
enabledFeatures = (0, _util.getEnabledFeatures)(idxResponse);
availableSteps = (0, _util.getAvailableSteps)(authClient, idxResponse, options.useGenericRemediator);
messages = (0, _util.getMessagesFromResponse)(idxResponse, options);
terminal = (0, _util.isTerminalResponse)(idxResponse);
}
if (terminal) {
status = _types.IdxStatus.TERMINAL; // In most cases a terminal response should not clear transaction data. The user should cancel or skip to continue.
// A terminal "success" is a non-error response with no further actions available.
// In these narrow cases, saved transaction data should be cleared.
// One example of a terminal success is when the email verify flow is continued in another tab
const hasActions = Object.keys(idxResponse.actions).length > 0;
const hasErrors = !!messages.find(msg => msg.class === 'ERROR');
const isTerminalSuccess = !hasActions && !hasErrors && idxResponse.requestDidSucceed === true;
if (isTerminalSuccess) {
shouldClearTransaction = true;
} else {
// save response if there are actions available (ignore messages)
shouldSaveResponse = !!hasActions;
} // leave shared storage intact so the transaction can be continued in another tab
clearSharedStorage = false;
} else if (canceled) {
status = _types.IdxStatus.CANCELED;
shouldClearTransaction = true;
} else if (idxResponse !== null && idxResponse !== void 0 && idxResponse.interactionCode) {
interactionCode = idxResponse.interactionCode;
if (exchangeCodeForTokens === false) {
status = _types.IdxStatus.SUCCESS;
shouldClearTransaction = false;
} else {
tokens = await getTokens(authClient, data);
status = _types.IdxStatus.SUCCESS;
shouldClearTransaction = true;
}
}
return { ...data,
status,
interactionCode,
tokens,
shouldSaveResponse,
shouldClearTransaction,
clearSharedStorage,
enabledFeatures,
availableSteps,
messages,
terminal
};
}
async function run(authClient, options = {}) {
let data = {
options,
values: initializeValues(options)
};
data = initializeData(authClient, data);
data = await getDataFromIntrospect(authClient, data);
data = await getDataFromRemediate(authClient, data);
data = await finalizeData(authClient, data);
const {
idxResponse,
meta,
shouldSaveResponse,
shouldClearTransaction,
clearSharedStorage,
status,
enabledFeatures,
availableSteps,
tokens,
nextStep,
messages,
error,
interactionCode
} = data;
if (shouldClearTransaction) {
authClient.transactionManager.clear({
clearSharedStorage
});
} else {
// ensures state is saved to sessionStorage
(0, _transactionMeta.saveTransactionMeta)(authClient, { ...meta
});
if (shouldSaveResponse) {
var _context;
// Save intermediate idx response in storage to reduce introspect call
const {
rawIdxState: rawIdxResponse,
requestDidSucceed
} = idxResponse;
authClient.transactionManager.saveIdxResponse({
rawIdxResponse,
requestDidSucceed,
stateHandle: (_context = idxResponse.context) === null || _context === void 0 ? void 0 : _context.stateHandle,
interactionHandle: meta === null || meta === void 0 ? void 0 : meta.interactionHandle
});
}
} // copy all fields from idxResponse which are needed by the widget
const {
actions,
context,
neededToProceed,
proceed,
rawIdxState,
requestDidSucceed,
stepUp
} = idxResponse || {};
return {
status: status,
...(meta && {
meta
}),
...(enabledFeatures && {
enabledFeatures
}),
...(availableSteps && {
availableSteps
}),
...(tokens && {
tokens
}),
...(nextStep && {
nextStep
}),
...(messages && messages.length && {
messages
}),
...(error && {
error
}),
...(stepUp && {
stepUp
}),
interactionCode,
// if options.exchangeCodeForTokens is false
// from idx-js
actions: actions,
context: context,
neededToProceed: neededToProceed,
proceed: proceed,
rawIdxState: rawIdxState,
requestDidSucceed
};
}
//# sourceMappingURL=run.js.map
;