Skip to content

Commit

Permalink
feat: allow passing device context to interact call
Browse files Browse the repository at this point in the history
OKTA-452538
<<<Jenkins Check-In of Tested SHA: a8827b8 for eng_productivity_ci_bot_okta@okta.com>>>
Artifact: okta-auth-js
Files changed count: 5
PR Link: "#1093"
  • Loading branch information
shuowu authored and eng-prod-CI-bot-okta committed Feb 4, 2022
1 parent 11d6f9e commit 2fff139
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- [#1036](https://github.com/okta/okta-auth-js/pull/1036) Adds `webauthn` authenticator support in idx module
- [#1075](https://github.com/okta/okta-auth-js/pull/1075) Adds top level `invokeApiMethod` method as an escape hatch to make arbitrary OKTA API request
- [#1093](https://github.com/okta/okta-auth-js/pull/1093) Allows passing device context headers (`X-Forwarded-For`, `User-Agent`, `X-Okta-User-Agent-Extended` and `X-Device-Token`) to `idx.interact`. Follow [setHeaders](README.md#setheaders) section to add headers to http requests.

### Fixes

Expand Down
11 changes: 9 additions & 2 deletions lib/idx/interact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface InteractOptions {
codeChallengeMethod?: string;
activationToken?: string;
recoveryToken?: string;
clientSecret?: string;
}

export interface InteractResponse {
Expand Down Expand Up @@ -65,8 +66,9 @@ export async function interact (authClient: OktaAuth, options: InteractOptions =
codeChallenge,
codeChallengeMethod,
activationToken,
recoveryToken
recoveryToken,
} = meta as IdxTransactionMeta;
const clientSecret = options.clientSecret || authClient.options.clientSecret;

const interactionHandle = await idx.interact({
withCredentials,
Expand All @@ -86,7 +88,12 @@ export async function interact (authClient: OktaAuth, options: InteractOptions =
activationToken,

// Recovery
recoveryToken
recoveryToken,

// X-Device-Token header need to pair with `client_secret`
// eslint-disable-next-line max-len
// https://oktawiki.atlassian.net/wiki/spaces/eng/pages/2445902453/Support+Device+Binding+in+interact#Scenario-1%3A-Non-User-Agent-with-Confidential-Client-(top-priority)
clientSecret
});
const newMeta = {
...meta,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
},
"dependencies": {
"@babel/runtime": "^7.12.5",
"@okta/okta-idx-js": "0.24.0",
"@okta/okta-idx-js": "0.25.0",
"@peculiar/webcrypto": "1.1.6",
"Base64": "1.1.0",
"atob": "^2.1.2",
Expand Down
76 changes: 75 additions & 1 deletion test/spec/idx/interact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ jest.mock('@okta/okta-idx-js', () => {
});

jest.mock('../../../lib/idx/transactionMeta', () => {
const actual = jest.requireActual('../../../lib/idx/transactionMeta');
return {
createTransactionMeta: () => {},
...actual,
getSavedTransactionMeta: () => {},
saveTransactionMeta: () => {}
};
Expand Down Expand Up @@ -391,6 +392,79 @@ describe('idx/interact', () => {
});
});
});

describe('clientSecret', () => {
beforeEach(() => {
// use original createTransactionMeta implementation
jest.spyOn(mocked.transactionMeta, 'createTransactionMeta').mockRestore();
jest.spyOn(mocked.transactionMeta, 'saveTransactionMeta');
});

it('uses clientSecret from sdk options', async () => {
const { authClient } = testContext;
authClient.options.clientSecret = 'sdk-clientSecret';
await interact(authClient, {});
expect(mocked.idx.interact).toHaveBeenCalled();
const interactArg = mocked.idx.interact.mock.calls[0][0];
expect(interactArg).toMatchObject({
'clientId': 'authClient-clientId',
'baseUrl': 'authClient-issuer/oauth2',
'codeChallenge': 'tp-codeChallenge',
'codeChallengeMethod': 'tp-codeChallengeMethod',
'redirectUri': 'authClient-redirectUri',
'scopes': ['tp-scopes'],
'state': 'tp-state',
'clientSecret': 'sdk-clientSecret'
});
expect(mocked.transactionMeta.saveTransactionMeta).toHaveBeenCalledWith(authClient, {
'clientId': 'authClient-clientId',
'codeChallenge': 'tp-codeChallenge',
'codeChallengeMethod': 'tp-codeChallengeMethod',
'codeVerifier': 'tp-codeVerifier',
'flow': 'default',
'interactionHandle': 'idx-interactionHandle',
'issuer': 'authClient-issuer',
'redirectUri': 'authClient-redirectUri',
'responseType': 'tp-responseType',
'scopes': ['tp-scopes'],
'state': 'tp-state',
'urls': expect.any(Object),
'withCredentials': true,
});
});
it('uses clientSecret from function options (overrides sdk option)', async () => {
const { authClient } = testContext;
authClient.options.clientSecret = 'sdk-clientSecret';
await interact(authClient, { clientSecret: 'fn-clientSecret' });
expect(mocked.idx.interact).toHaveBeenCalled();
const functionArg = mocked.idx.interact.mock.calls[0][0];
expect(functionArg).toMatchObject({
'clientId': 'authClient-clientId',
'baseUrl': 'authClient-issuer/oauth2',
'codeChallenge': 'tp-codeChallenge',
'codeChallengeMethod': 'tp-codeChallengeMethod',
'redirectUri': 'authClient-redirectUri',
'scopes': ['tp-scopes'],
'state': 'tp-state',
'clientSecret': 'fn-clientSecret'
});
expect(mocked.transactionMeta.saveTransactionMeta).toHaveBeenCalledWith(authClient, {
'clientId': 'authClient-clientId',
'codeChallenge': 'tp-codeChallenge',
'codeChallengeMethod': 'tp-codeChallengeMethod',
'codeVerifier': 'tp-codeVerifier',
'flow': 'default',
'interactionHandle': 'idx-interactionHandle',
'issuer': 'authClient-issuer',
'redirectUri': 'authClient-redirectUri',
'responseType': 'tp-responseType',
'scopes': ['tp-scopes'],
'state': 'tp-state',
'urls': expect.any(Object),
'withCredentials': true,
});
});
});

it('saves returned interactionHandle', async () => {
const { authClient } = testContext;
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2364,10 +2364,10 @@
mkdirp "^1.0.4"
rimraf "^3.0.2"

"@okta/okta-idx-js@0.24.0":
version "0.24.0"
resolved "https://registry.yarnpkg.com/@okta/okta-idx-js/-/okta-idx-js-0.24.0.tgz#f7c048a866add49f40fde0e25734fe61998be7c3"
integrity sha512-ut4LPYvsXKKQm+PVgsGuoeU1RzVteNHrZPYvhz7REJEaNkY7pKUPBmYxsq9zyjRyzEEkAH7YrWeyhMcW8Lpt9w==
"@okta/okta-idx-js@0.25.0":
version "0.25.0"
resolved "https://registry.yarnpkg.com/@okta/okta-idx-js/-/okta-idx-js-0.25.0.tgz#4ca1d1a9b5617cbdc5374965caea9949b5b22cbc"
integrity sha512-cNOYSHCzLR5NKqiBsvPYYuWYPerLIVva6SNF7+/vb4zH1/hBjznlj/W6guVl0m5Tycz/ZUuVSQzHtEQoock/uQ==
dependencies:
"@babel/runtime" "^7.12.5"
"@babel/runtime-corejs3" "^7.12.5"
Expand Down

0 comments on commit 2fff139

Please sign in to comment.