Skip to content

Commit c68d223

Browse files
committed
refactor!: remove auto generate state logic
BREAKING CHANGE: No more auto generate state on init to be used in `authorize` and `onboard` API. There will be no default `state` and you will have to pass one via the APIs' options parameter if you need one.
1 parent 491dda8 commit c68d223

File tree

11 files changed

+15
-71
lines changed

11 files changed

+15
-71
lines changed

docs/README.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ mtLinkSdk.authorize(options);
8484
| <span id="api-authorize_options">options</span> | object | false | Value set during `init`. | Optional parameters as described in [common options](#common-api-options). |
8585
| options.scopes | string <p><strong>OR</strong></p> string[] | false | Value set during `init`.<p><strong>OR</strong></p>`guest_read` | Access scopes you're requesting. This can be a single scope, or an array of scopes.<br /><br />Currently supported scopes are:<br />`guest_read`, `accounts_read`, `points_read`, `point_transactions_read`, `transactions_read`, `transactions_write`, `expense_claims_read`, `categories_read`, `investment_accounts_read`, `investment_transactions_read`, `notifications_read`, `request_refresh`, `life_insurance_read`. |
8686
| options.redirectUri | string | false | Value set during `init`. | OAuth redirection URI, refer [here](https://www.oauth.com/oauth2-servers/redirect-uris/) for more details.<br /><br /><strong>NOTE:</strong> This function will throw an error if this value is undefined <strong>and</strong> no default value was provided in the [init options](?id=api-init_options). |
87-
| options.state | string | false | Value set during `init`.<p><strong>OR</strong></p>Randomly generated [uuid](<https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)>). | Refer [here](https://auth0.com/docs/protocols/oauth2/oauth-state) for more details.<br /><br /><strong>NOTE:</strong> Make sure to set this value explicitly if your server generates an identifier for the OAuth authorization request so that you can use to acquire the access token after the OAuth redirect occurs. |
87+
| options.state | string | false | You can pass in an optional value here during OAuth authorization request and validate the value is still same after an OAuth redirection. [Click here](https://tools.ietf.org/html/rfc6749#section-4.1.1)|
8888
| options.codeVerifier | string | false | Value set during `init`. | We only support SHA256, therefore this `codeVerifier` will be used to generate the `code_challenge` using the SHA256 hash algorithm. [Click here](https://auth0.com/docs/api-auth/tutorials/authorization-code-grant-pkce) for more details.</p><strong>NOTE:</strong> Make sure to set this value explicitly if your server generates an identifier for the OAuth authorization request so that you can use to acquire the access token after the OAuth redirect occurs. |
8989
| <span id="authorize_option_force_logout">options.forceLogout</span> | boolean | false | `false` | Force existing guest session to logout and call authorize with a clean state. |
9090
| options.country | `AU`, ` JP` | false | Value set during `init`. | Server location for the guest to login or sign up. If you wish to restrict your guest to only one country, make sure to set this value.<br /><br /><strong>NOTE:</strong> For apps created after 2020-07-08, the sign up form will display a country selection dropdown for the guest to select a country when this value is undefined or invalid. |
@@ -117,9 +117,7 @@ mtLinkSdk.onboard(options)
117117

118118
Since we are using PKCE/Code grant, we will have to exchange the `code` for a token. You can optionally pass `code` via options parameter or it will fallback to automatically extract it from the browser URL.
119119

120-
Options for the `state`, `codeVerifier` and `onboard` calls will use the default value from `init`, however if you explicitly pass a new value when calling `authorize` or `onboard` via the options parameter, make sure to reuse the same value when calling this API, otherwise the authentication server will throw an error due to a value mismatch.
121-
122-
If there is a `state` passed via this API option (or it exists in the URL), it will be used internally to compare to the `state` used in the previous `authorize` or `onboard` call during the same session. This API will throw an error when states do not match. Refer [here](https://auth0.com/docs/protocols/oauth2/oauth-state) for more details.
120+
Options for the, `codeVerifier` and `onboard` calls will use the default value from `init`, however if you explicitly pass a new value when calling `authorize` or `onboard` via the options parameter, make sure to reuse the same value when calling this API, otherwise the authentication server will throw an error due to a value mismatch.
123121

124122
`code` will be invalidated (can be used only once) after exchanged for a token, it is your responsibility to store the token yourself as the SDK does not store it internally.
125123

@@ -140,7 +138,6 @@ const token = await mtLinkSdk.exchangeToken(options);
140138
| - | - | - | - | - |
141139
| options | object | false | Value set during `init`. | Optional parameters. |
142140
| options.code | string | false | Value from browser URL | Code from OAuth redirection used to exchange for a token, SDK will try to extract it from the browser URL if none is provided.<br /><br /><strong>NOTE:</strong> SDK will throw an error if no value is provided here and the client library failed to extract it from browser URL. |
143-
| options.state | string | false | Value set during `init`. | Make sure the value of `state` here is the same state value used during the `authorize` or `onboard` call. |
144141
| options.codeVerifier | string | false | Value set during `init`. | Make sure the value of `codeVerifier` here is the same codeVerifier value used during the `authorize` or `onboard` call. |
145142
| options.redirectUri | string | false | Value set during `init`. | Make sure the value of `redirectUri` here is the same redirectUri value used during the `authorize` or `onboard` call.<br /><br /><strong>NOTE:</strong> The SDK will throw an error if both this parameter and the default value from the [init options](?id=api-init_options) are undefined. |
146143

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@
2626
"node-fetch": "^2.6.1",
2727
"qs": "^6.9.4",
2828
"snake-case": "^3.0.3",
29-
"url-safe-base64": "^1.1.1",
30-
"uuid": "^8.3.1"
29+
"url-safe-base64": "^1.1.1"
3130
},
3231
"devDependencies": {
3332
"@commitlint/cli": "^11.0.0",
@@ -37,7 +36,6 @@
3736
"@types/node-fetch": "^2.5.4",
3837
"@types/qs": "^6.9.5",
3938
"@types/url-safe-base64": "^1.1.0",
40-
"@types/uuid": "^8.3.0",
4139
"@typescript-eslint/eslint-plugin": "^4.5.0",
4240
"@typescript-eslint/parser": "^4.5.0",
4341
"conventional-changelog-cli": "^2.1.0",

src/api/__tests__/authorize.test.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ describe('api', () => {
4040
open.mockClear();
4141

4242
const codeVerifier = 'codeVerifier';
43-
const state = 'state';
4443
const country = 'JP';
4544
const scopes = 'points_read';
4645
const cobrandClientId = 'cobrandClientId';
@@ -49,7 +48,6 @@ describe('api', () => {
4948
const mtLinkSdk = new MtLinkSdk();
5049
mtLinkSdk.init(clientId, {
5150
redirectUri,
52-
state,
5351
codeVerifier,
5452
country,
5553
scopes,
@@ -69,16 +67,14 @@ describe('api', () => {
6967
redirect_uri: redirectUri,
7068
code_challenge: 'N1E4yRMD7xixn_oFyO_W3htYN3rY7-HMDKJe6z6r928',
7169
code_challenge_method: 'S256',
72-
state,
7370
country,
7471
locale,
7572
configs: generateConfigs(),
7673
});
7774
const url = `${MY_ACCOUNT_DOMAINS.production}/oauth/authorize?${query}`;
7875
expect(open).toBeCalledWith(url, '_self');
7976

80-
expect(mockedStorage.set).toBeCalledTimes(2);
81-
expect(mockedStorage.set).toBeCalledWith('state', state);
77+
expect(mockedStorage.set).toBeCalledTimes(1);
8278
expect(mockedStorage.set).toBeCalledWith('codeVerifier', codeVerifier);
8379
});
8480

@@ -118,8 +114,7 @@ describe('api', () => {
118114
const url = `${MY_ACCOUNT_DOMAINS.production}/oauth/authorize?${query}`;
119115
expect(open).toBeCalledWith(url, '_self');
120116

121-
expect(mockedStorage.set).toBeCalledTimes(4);
122-
expect(mockedStorage.set).toBeCalledWith('state', state);
117+
expect(mockedStorage.set).toBeCalledTimes(2);
123118
expect(mockedStorage.set).toBeCalledWith('codeVerifier', codeVerifier);
124119
});
125120

src/api/__tests__/exchange-token.test.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,6 @@ describe('api', () => {
4444
);
4545
});
4646

47-
test('state have to match if passed via options or extracted from url', async () => {
48-
await expect(exchangeToken(mtLinkSdk.storedOptions, { code, state: 'test' })).rejects.toThrow(
49-
'[mt-link-sdk] `state` does not matched, make sure to pass in the correct state used during `authorize` or `onboard` call'
50-
);
51-
});
52-
5347
test('make request', async () => {
5448
fetch.mockClear();
5549

src/api/__tests__/onboard.test.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ describe('api', () => {
6868
open.mockClear();
6969

7070
const codeVerifier = 'codeVerifier';
71-
const state = 'state';
7271
const country = 'JP';
7372
const scopes = 'points_read';
7473
const cobrandClientId = 'cobrandClientId';
@@ -77,7 +76,6 @@ describe('api', () => {
7776
const mtLinkSdk = new MtLinkSdk();
7877
mtLinkSdk.init(clientId, {
7978
redirectUri,
80-
state,
8179
codeVerifier,
8280
country,
8381
scopes,
@@ -98,16 +96,14 @@ describe('api', () => {
9896
redirect_uri: redirectUri,
9997
code_challenge: 'N1E4yRMD7xixn_oFyO_W3htYN3rY7-HMDKJe6z6r928',
10098
code_challenge_method: 'S256',
101-
state,
10299
country,
103100
locale,
104101
configs: generateConfigs({ email }),
105102
});
106103
const url = `${MY_ACCOUNT_DOMAINS.production}/onboard?${query}`;
107104
expect(open).toBeCalledWith(url, '_self');
108105

109-
expect(mockedStorage.set).toBeCalledTimes(2);
110-
expect(mockedStorage.set).toBeCalledWith('state', state);
106+
expect(mockedStorage.set).toBeCalledTimes(1);
111107
expect(mockedStorage.set).toBeCalledWith('codeVerifier', codeVerifier);
112108
});
113109

@@ -148,8 +144,7 @@ describe('api', () => {
148144
const url = `${MY_ACCOUNT_DOMAINS.production}/onboard?${query}`;
149145
expect(open).toBeCalledWith(url, '_self');
150146

151-
expect(mockedStorage.set).toBeCalledTimes(4);
152-
expect(mockedStorage.set).toBeCalledWith('state', state);
147+
expect(mockedStorage.set).toBeCalledTimes(2);
153148
expect(mockedStorage.set).toBeCalledWith('codeVerifier', codeVerifier);
154149
});
155150

src/api/authorize.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export default function authorize(
2222
locale,
2323
scopes: defaultScopes,
2424
redirectUri: defaultRedirectUri,
25-
state: defaultState,
2625
codeVerifier: defaultCodeVerifier,
2726
country: defaultCountry,
2827
} = storedOptions;
@@ -34,18 +33,13 @@ export default function authorize(
3433
const {
3534
scopes = defaultScopes,
3635
redirectUri = defaultRedirectUri,
37-
state = defaultState,
3836
codeVerifier = defaultCodeVerifier,
3937
country = defaultCountry,
4038
isNewTab,
39+
state,
4140
...rest
4241
} = options;
4342

44-
// update state
45-
if (state !== defaultState) {
46-
storage.set('state', state);
47-
}
48-
4943
// update codeVerifier
5044
if (codeVerifier !== defaultCodeVerifier) {
5145
storage.set('codeVerifier', codeVerifier);

src/api/exchange-token.ts

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,17 @@ import qs from 'qs';
33
import { MY_ACCOUNT_DOMAINS } from '../server-paths';
44
import { StoredOptions, ExchangeTokenOptions } from '../typings';
55

6-
function getCodeAndState(): { code?: string; state?: string } {
6+
function getCode(): string | undefined {
77
// not available in node environment
88
if (!window) {
9-
return {};
9+
return;
1010
}
1111

12-
const { code, state } = qs.parse(window.location.search, {
12+
const { code } = qs.parse(window.location.search, {
1313
ignoreQueryPrefix: true,
1414
});
1515

16-
return {
17-
code: (Array.isArray(code) ? code[code.length - 1] : code) as string,
18-
state: (Array.isArray(state) ? state[state.length - 1] : state) as string,
19-
};
16+
return (Array.isArray(code) ? code[code.length - 1] : code) as string | undefined;
2017
}
2118

2219
export default async function exchangeToken(
@@ -26,7 +23,6 @@ export default async function exchangeToken(
2623
const {
2724
clientId,
2825
redirectUri: defaultRedirectUri,
29-
state: defaultState,
3026
mode,
3127
codeVerifier: defaultCodeVerifier,
3228
} = storedOptions;
@@ -35,13 +31,10 @@ export default async function exchangeToken(
3531
throw new Error('[mt-link-sdk] Make sure to call `init` before calling `exchangeToken`.');
3632
}
3733

38-
const { code: extractedCode, state: extractedState } = getCodeAndState();
39-
4034
const {
4135
redirectUri = defaultRedirectUri,
42-
state = extractedState,
4336
codeVerifier = defaultCodeVerifier,
44-
code = extractedCode,
37+
code = getCode(),
4538
} = options;
4639

4740
if (!code) {
@@ -50,12 +43,6 @@ export default async function exchangeToken(
5043
);
5144
}
5245

53-
if (state && defaultState !== state) {
54-
throw new Error(
55-
'[mt-link-sdk] `state` does not matched, make sure to pass in the correct state used during `authorize` or `onboard` call'
56-
);
57-
}
58-
5946
if (!redirectUri) {
6047
throw new Error(
6148
'[mt-link-sdk] Missing option `redirectUri` in `exchangeToken`, make sure to pass one via `exchangeToken` options or `init` options.'

src/api/onboard.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export default function onboard(storedOptions: StoredOptions, options: OnboardOp
1919
locale,
2020
scopes: defaultScopes,
2121
redirectUri: defaultRedirectUri,
22-
state: defaultState,
2322
codeVerifier: defaultCodeVerifier,
2423
country: defaultCountry,
2524
} = storedOptions;
@@ -31,10 +30,10 @@ export default function onboard(storedOptions: StoredOptions, options: OnboardOp
3130
const {
3231
scopes = defaultScopes,
3332
redirectUri = defaultRedirectUri,
34-
state = defaultState,
3533
codeVerifier = defaultCodeVerifier,
3634
country = defaultCountry,
3735
isNewTab,
36+
state,
3837
...rest
3938
} = options;
4039
const configs = mergeConfigs(storedOptions, rest, [
@@ -46,11 +45,6 @@ export default function onboard(storedOptions: StoredOptions, options: OnboardOp
4645

4746
const { email } = configs;
4847

49-
// update state
50-
if (state !== defaultState) {
51-
storage.set('state', state);
52-
}
53-
5448
// update codeVerifier
5549
if (codeVerifier !== defaultCodeVerifier) {
5650
storage.set('codeVerifier', codeVerifier);

src/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { v4 as uuid } from 'uuid';
2-
31
import authorize from './api/authorize';
42
import onboard from './api/onboard';
53
import logout from './api/logout';
@@ -28,7 +26,6 @@ const validModes: Mode[] = ['production', 'staging', 'develop', 'local'];
2826
export class MtLinkSdk {
2927
public storedOptions: StoredOptions = {
3028
mode: 'production',
31-
state: storage.get('state') || uuid(),
3229
codeVerifier: storage.get('codeVerifier') || '',
3330
};
3431

@@ -47,7 +44,6 @@ export class MtLinkSdk {
4744
mode: validModes.indexOf(mode) === -1 ? 'production' : mode,
4845
};
4946

50-
storage.set('state', this.storedOptions.state);
5147
storage.set('codeVerifier', this.storedOptions.codeVerifier);
5248
}
5349

src/typings.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ export interface StoredOptions extends InitOptions {
8080
clientId?: string;
8181
mode: Mode;
8282
codeVerifier: string;
83-
state: string;
8483
}
8584

8685
export interface ExchangeTokenOptions extends OAuthSharedParams {

0 commit comments

Comments
 (0)