Skip to content
Draft
2 changes: 2 additions & 0 deletions lib/idx/flow/AuthenticationFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import { RemediationFlow } from './RemediationFlow';
import {
Identify,
DeviceIdentificationChallenge,
SelectAuthenticatorAuthenticate,
ChallengeAuthenticator,
ReEnrollAuthenticator,
Expand All @@ -30,6 +31,7 @@ import {
} from '../remediators';

export const AuthenticationFlow: RemediationFlow = {
'device-identification-challenge': DeviceIdentificationChallenge,
'identify': Identify,
'select-authenticator-authenticate': SelectAuthenticatorAuthenticate,
'select-authenticator-enroll': SelectAuthenticatorEnroll,
Expand Down
48 changes: 48 additions & 0 deletions lib/idx/getDeviceChallenge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* eslint-disable complexity */
/*!
* Copyright (c) 2021, 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 { validateVersionConfig } from './idxState';
import { IntrospectOptions, OktaAuthIdxInterface } from './types';
import { isRawIdxResponse } from './types/idx-js';
import { IDX_API_VERSION } from '../constants';
import { isAuthApiError } from '../errors';
import { loadInvisibleFrame } from '../oidc/util/browser';

export async function getDeviceChallenge (
authClient: OktaAuthIdxInterface,
href: string,
options: IntrospectOptions = {}
): Promise<void> {

// try load from storage first, TODO: delete this
// const savedIdxResponse = authClient.transactionManager.loadIdxResponse(options);
// if (savedIdxResponse) {
// response = savedIdxResponse.rawIdxResponse;
// }

// if (!response) {
const version = options.version || IDX_API_VERSION;
try {
validateVersionConfig(version);
const iFrameId = 'deviceChallengeIFrameId';
loadInvisibleFrame(href, iFrameId); // SIW will init polling, no reponse needed
} catch (err) {
if (isAuthApiError(err) && err.xhr && isRawIdxResponse(err.xhr.responseJSON)) {
console.log(err.xhr.responseJSON);
throw new Error('Auth Api Error');
} else {
throw err;
}
}
// }
}
1 change: 1 addition & 0 deletions lib/idx/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export {
} from './emailVerify';
export { interact } from './interact';
export { introspect } from './introspect';
export { getDeviceChallenge } from './getDeviceChallenge';
export { poll } from './poll';
export { proceed, canProceed } from './proceed';
export { register } from './register';
Expand Down
21 changes: 21 additions & 0 deletions lib/idx/remediators/DeviceIdentificationChallenge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*!
* 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 { Remediator, RemediationValues } from './Base/Remediator';
export class DeviceIdentificationChallenge extends Remediator<RemediationValues> {
static remediationName = 'device-identification-challenge';

canRemediate(): boolean {
return false;
}
}
1 change: 1 addition & 0 deletions lib/idx/remediators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export * from './ChallengePoll';
export * from './ResetAuthenticator';
export * from './EnrollProfile';
export * from './Identify';
export * from './DeviceIdentificationChallenge';
export * from './ReEnrollAuthenticator';
export * from './RedirectIdp';
export * from './SelectAuthenticatorAuthenticate';
Expand Down
39 changes: 39 additions & 0 deletions lib/idx/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
/* eslint-disable max-statements, complexity, max-depth */
import { interact } from './interact';
import { introspect } from './introspect';
import { getDeviceChallenge } from './getDeviceChallenge';
import { remediate } from './remediate';
import { getFlowSpecification } from './flow';
import * as remediators from './remediators';
Expand All @@ -32,6 +33,8 @@ import { getSavedTransactionMeta, saveTransactionMeta } from './transactionMeta'
import { getAvailableSteps, getEnabledFeatures, getMessagesFromResponse, isTerminalResponse } from './util';
import { Tokens } from '../oidc/types';
import { APIError } from '../errors/types';
import { DeviceIdentificationChallenge } from './remediators';
import { makeIdxState } from './idxState';
declare interface RunData {
options: RunOptions;
values: remediators.RemediationValues;
Expand Down Expand Up @@ -154,6 +157,38 @@ async function getDataFromIntrospect(authClient, data: RunData): Promise<RunData
return { ...data, idxResponse, meta };
}

// const delay = ms => new Promise(res => setTimeout(res, ms));

async function collectChromeDeviceSignals(authClient, data: RunData): Promise<any> {
const { options } = data;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let idxResponse;
const {
withCredentials,
version
} = options;

const remediations = data.idxResponse?.rawIdxState.remediation?.value;
remediations?.forEach(async remediation => { // TODO: only if 1st remediation is device-challenge-poll
if (remediation['name'] == 'device-challenge-poll') {
const challengeMethod = data.idxResponse?.rawIdxState['authenticatorChallenge']?.value.challengeMethod;
if (challengeMethod == 'CHROME_DTC') {
const href = data.idxResponse?.rawIdxState['authenticatorChallenge']?.value.href;
await getDeviceChallenge(authClient, href, { withCredentials, version });
}
}
});

// TODO: add a wait here for 2 seconds so that signals are collected, check if view is gone and continue to next

// remove the 1st remediations, regardless if we collect device signals successfully or not.
// backend will have log, syslog and Splunk monitor set up.
// This is not needed when backend has correct logic to add DeviceIdentificationChallenge
// data.idxResponse?.rawIdxState.remediation?.value?.shift();
// data.idxResponse?.neededToProceed.shift();
// return data;
}

async function getDataFromRemediate(authClient, data: RunData): Promise<RunData> {
let {
idxResponse,
Expand Down Expand Up @@ -308,6 +343,10 @@ export async function run(

data = initializeData(authClient, data);
data = await getDataFromIntrospect(authClient, data);

// collect device signals if elibigle
await collectChromeDeviceSignals(authClient, data);

data = await getDataFromRemediate(authClient, data);
data = await finalizeData(authClient, data);

Expand Down
13 changes: 13 additions & 0 deletions lib/oidc/util/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ export function loadFrame(src) {
return document.body.appendChild(iframe);
}

export function loadInvisibleFrame(src, id) {
var iframe = document.createElement('iframe');
iframe.src = src;
iframe.id = id;
// invisible and not take up any space
iframe.height = '0';
iframe.width = '0';
iframe.style.position = 'absolute';
iframe.style.border = '0';

return document.body.appendChild(iframe);
}

export function loadPopup(src, options) {
var title = options.popupTitle || 'External Identity Provider User Authentication';
var appearance = 'toolbar=no, scrollbars=yes, resizable=yes, ' +
Expand Down