Skip to content

Commit b34e458

Browse files
committed
feat(javascript-sdk): refactor authorize URL utilities for DaVinci
1 parent 6eac0fc commit b34e458

File tree

3 files changed

+86
-29
lines changed

3 files changed

+86
-29
lines changed

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import PKCE from '../util/pkce';
2+
import { GetAuthorizationUrlOptions } from './interfaces';
3+
4+
function getStorageKey(clientId: string, prefix?: string) {
5+
return `${prefix || 'FR-SDK'}-authflow-${clientId}`;
6+
}
7+
8+
/**
9+
* Generate and store PKCE values for later use
10+
* @param { string } storageKey - Key to store authorization options in sessionStorage
11+
* @param {GenerateAndStoreAuthUrlValues} options - Options for generating PKCE values
12+
* @returns { state: string, verifier: string, GetAuthorizationUrlOptions }
13+
*/
14+
interface GenerateAndStoreAuthUrlValues extends GetAuthorizationUrlOptions {
15+
clientId: string;
16+
login?: 'redirect' | 'embedded';
17+
prefix?: string;
18+
}
19+
20+
export function generateAndStoreAuthUrlValues(options: GenerateAndStoreAuthUrlValues) {
21+
const verifier = PKCE.createVerifier();
22+
const state = PKCE.createState();
23+
const storageKey = getStorageKey(options.clientId, options.prefix);
24+
25+
const authorizeUrlOptions = {
26+
...options,
27+
state,
28+
verifier,
29+
};
30+
31+
if (options.login === 'redirect') {
32+
// Since `login` is configured for "redirect", store authorize values and redirect
33+
sessionStorage.setItem(storageKey, JSON.stringify(authorizeUrlOptions));
34+
}
35+
36+
return { state, verifier, authorizeUrlOptions };
37+
}
38+
39+
/**
40+
* @function getStoredAuthUrlValues - Retrieve stored authorization options from sessionStorage
41+
* @param { string } storageKey - Key to retrieve stored values from sessionStorage
42+
* @returns { GetAuthorizationUrlOptions }
43+
*/
44+
export function getStoredAuthUrlValues(
45+
clientId: string,
46+
prefix?: string,
47+
): GetAuthorizationUrlOptions {
48+
const storageKey = getStorageKey(clientId, prefix);
49+
const storedString = sessionStorage.getItem(storageKey);
50+
sessionStorage.removeItem(storageKey);
51+
52+
try {
53+
return JSON.parse(storedString as string);
54+
} catch (error) {
55+
throw new Error('Stored values for Auth URL could not be parsed');
56+
}
57+
}

packages/javascript-sdk/src/token-manager/index.ts

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import type { OAuth2Tokens } from '../oauth2-client';
1515
import OAuth2Client, { allowedErrors, ResponseType } from '../oauth2-client';
1616
import type { StringDict, Tokens } from '../shared/interfaces';
1717
import TokenStorage from '../token-storage';
18-
import PKCE from '../util/pkce';
18+
import { generateAndStoreAuthUrlValues, getStoredAuthUrlValues } from '../oauth2-client/state-pkce';
1919
import { parseQuery } from '../util/url';
2020
import { tokensWillExpireWithinThreshold } from './helpers';
2121

@@ -39,7 +39,7 @@ abstract class TokenManager {
3939
forceRenew: true, // If you want to get new tokens, despite existing ones
4040
login: 'embedded', // If user authentication is handled in-app
4141
serverConfig: {
42-
timeout: 5000, // If using "legacy", use a short timeout to catch error
42+
timeout: 5000,
4343
},
4444
});
4545
```
@@ -65,9 +65,11 @@ abstract class TokenManager {
6565
```
6666
*/
6767
public static async getTokens(options?: GetTokensOptions): Promise<OAuth2Tokens | void> {
68-
const { clientId, oauthThreshold } = Config.get(options as ConfigOptions);
69-
// const storageKey = `${Config.get().prefix}-authflow-${clientId}`;
70-
const storageKey = `${Config.get().prefix}-authflow-${clientId}`;
68+
const { clientId, oauthThreshold, prefix } = Config.get(options as ConfigOptions);
69+
70+
if (!clientId) {
71+
throw new Error('Client ID is required');
72+
}
7173

7274
/**
7375
* First, let's see if tokens exist locally
@@ -105,33 +107,31 @@ abstract class TokenManager {
105107
* and return acquired tokens
106108
*/
107109
if (options?.query?.code && options?.query?.state) {
108-
const storedString = sessionStorage.getItem(storageKey);
109-
sessionStorage.removeItem(storageKey);
110-
const storedValues: { state: string; verifier: string } = JSON.parse(storedString as string);
110+
const { state, verifier } = getStoredAuthUrlValues(clientId, prefix);
111111

112-
return await this.tokenExchange(options, storedValues);
112+
if (state === undefined || verifier === undefined) {
113+
throw new Error(
114+
'`state` and/or `verifier` not found in sessionStorage. Debugging: sessionStorage is not accessible in separate tabs.',
115+
);
116+
}
117+
return await this.tokenExchange(options, { state, verifier });
113118
}
114119

115-
/**
116-
* If we are here, then we are just beginning the auth code flow,
117-
* so let's generate authorize PKCE values and URL
118-
*/
119-
const verifier = PKCE.createVerifier();
120-
const state = PKCE.createState();
121-
122120
// so to not change the type of the above function
123121
// we assign it here if its undefined or null.
124-
125122
const config = Object.assign({}, options);
126123
delete config.forceRenew;
127-
delete config.login;
128124

129-
const authorizeUrlOptions = {
125+
/**
126+
* Generate state and verifier for PKCE
127+
*/
128+
const { authorizeUrlOptions } = generateAndStoreAuthUrlValues({
130129
...config,
130+
clientId,
131+
prefix,
131132
responseType: ResponseType.Code,
132-
state,
133-
verifier,
134-
};
133+
});
134+
135135
/**
136136
* Attempt to call the authorize URL to retrieve authorization code
137137
*/
@@ -178,17 +178,17 @@ abstract class TokenManager {
178178
throw err;
179179
}
180180

181-
// Since `login` is configured for "redirect", store authorize values and redirect
182-
sessionStorage.setItem(storageKey, JSON.stringify(authorizeUrlOptions));
183-
184181
const authorizeUrl = await OAuth2Client.createAuthorizeUrl(authorizeUrlOptions);
185182

186183
return location.assign(authorizeUrl);
187184
}
188185
/**
189186
* Exchange authorization code for tokens
190187
*/
191-
return await this.tokenExchange(options, { state, verifier });
188+
return await this.tokenExchange(options, {
189+
state: authorizeUrlOptions.state,
190+
verifier: authorizeUrlOptions.verifier,
191+
});
192192
}
193193

194194
public static async deleteTokens(): Promise<void> {

0 commit comments

Comments
 (0)