Skip to content

Commit 29bf6a9

Browse files
committed
Add SamlSubjectId Support
1 parent 5829221 commit 29bf6a9

File tree

12 files changed

+145
-42
lines changed

12 files changed

+145
-42
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,4 @@ typings/
6161
.env
6262

6363
# next.js build output
64-
.next
64+
.next

docs/README.md

Lines changed: 28 additions & 11 deletions
Large diffs are not rendered by default.

src/__tests__/helper.test.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { constructScopes, getIsTabValue, mergeConfigs, generateConfigs } from '../helper';
22
import packageJson from '../../package.json';
33
import { ConfigsOptions } from '../typings';
4+
import { stringify } from 'qs';
45

56
describe('helper', () => {
67
test('constuctScopes', () => {
@@ -123,26 +124,37 @@ describe('helper', () => {
123124
authAction: 'signup',
124125
showAuthToggle: true,
125126
showRememberMe: true,
126-
authnMethod: 'sso',
127-
}
127+
authnMethod: 'sso'
128+
};
128129

129-
expect(
130-
generateConfigs(configPayload)
131-
).toBe(
130+
expect(generateConfigs(configPayload)).toBe(
132131
`sdk_platform=js&sdk_version=${packageJson.version}&email=email&back_to=backTo&auth_action=signup&show_auth_toggle=true` +
133132
`&show_remember_me=true&authn_method=sso`
134133
);
135134
});
136135

137136
test('with authnMethod parameter as an array', () => {
138137
const configPayload: ConfigsOptions = {
139-
authnMethod: ['sso', 'passwordless'],
140-
}
138+
authnMethod: ['sso', 'passwordless']
139+
};
141140

142-
expect(
143-
generateConfigs(configPayload)
144-
).toBe(
145-
`sdk_platform=js&sdk_version=${packageJson.version}&authn_method[]=sso&authn_method[]=passwordless`
141+
expect(generateConfigs(configPayload)).toBe(
142+
`sdk_platform=js&sdk_version=${packageJson.version}&authn_method%5B%5D=sso&authn_method%5B%5D=passwordless`
143+
);
144+
});
145+
146+
test('query encoding should make sure config params are also encoded', () => {
147+
const configPayload: ConfigsOptions = {
148+
email: 'email&!@#(*)-304should be_encoded',
149+
backTo: 'backTo #!@with []special= chars',
150+
authAction: 'signup',
151+
showAuthToggle: true,
152+
showRememberMe: true,
153+
authnMethod: ['sso', 'passwordless']
154+
};
155+
156+
expect(generateConfigs(configPayload)).toBe(
157+
`sdk_platform=js&sdk_version=${packageJson.version}&email=email%26%21%40%23%28%2A%29-304should%20be_encoded&back_to=backTo%20%23%21%40with%20%5B%5Dspecial%3D%20chars&auth_action=signup&show_auth_toggle=true&show_remember_me=true&authn_method%5B%5D=sso&authn_method%5B%5D=passwordless`
146158
);
147159
});
148160

src/__tests__/index.test.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ describe('index', () => {
2323

2424
instance.init('clientId', {
2525
redirectUri: 'redirectUri',
26-
authnMethod: 'sso'
26+
authnMethod: 'sso',
27+
samlSubjectId: 'samlSubjectId'
2728
});
2829

2930
const options = instance.storedOptions;
@@ -32,7 +33,8 @@ describe('index', () => {
3233
mode: options.mode,
3334
redirectUri: options.redirectUri,
3435
state: options.state,
35-
authnMethod: options.authnMethod
36+
authnMethod: options.authnMethod,
37+
samlSubjectId: options.samlSubjectId
3638
};
3739

3840
const result1 = instance.authorize({ scopes: 'scopes' });
@@ -96,4 +98,19 @@ describe('index', () => {
9698

9799
expect(mtLinkSdk.storedOptions.mode).toBe('production');
98100
});
101+
102+
test('sets the samlSubjectId when calling setSamlSubjectId', () => {
103+
const samlSubjectId = 'samlSubjectId';
104+
mtLinkSdk.init('clientId', {
105+
samlSubjectId
106+
});
107+
108+
expect(mtLinkSdk.storedOptions.samlSubjectId).toBe(samlSubjectId);
109+
110+
const newSamlSubjectId = 'newSamlSubjectId';
111+
112+
mtLinkSdk.setSamlSubjectId(newSamlSubjectId);
113+
114+
expect(mtLinkSdk.storedOptions.samlSubjectId).toBe(newSamlSubjectId);
115+
});
99116
});

src/api/__tests__/authorize.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@ describe('api', () => {
4343
const scopes = 'points_read';
4444
const cobrandClientId = 'cobrandClientId';
4545
const locale = 'locale';
46+
const samlSubjectId = 'mySubject';
4647

4748
const mtLinkSdk = new MtLinkSdk();
4849
mtLinkSdk.init(clientId, {
4950
redirectUri,
5051
scopes,
5152
locale,
52-
cobrandClientId
53+
cobrandClientId,
54+
samlSubjectId
5355
});
5456

5557
authorize(mtLinkSdk.storedOptions);
@@ -64,6 +66,7 @@ describe('api', () => {
6466
redirect_uri: redirectUri,
6567
country,
6668
locale,
69+
saml_subject_id: samlSubjectId,
6770
configs: generateConfigs()
6871
});
6972
const url = `${MY_ACCOUNT_DOMAINS.production}/oauth/authorize?${query}`;
@@ -77,9 +80,10 @@ describe('api', () => {
7780
const state = 'state';
7881
const country = 'JP';
7982
const scopes = 'points_read';
83+
const samlSubjectId = 'mySubject';
8084

8185
const mtLinkSdk = new MtLinkSdk();
82-
mtLinkSdk.init(clientId);
86+
mtLinkSdk.init(clientId, { samlSubjectId });
8387

8488
authorize(mtLinkSdk.storedOptions, {
8589
state,
@@ -96,6 +100,7 @@ describe('api', () => {
96100
redirect_uri: redirectUri,
97101
state,
98102
country,
103+
saml_subject_id: samlSubjectId,
99104
configs: generateConfigs()
100105
});
101106
const url = `${MY_ACCOUNT_DOMAINS.production}/oauth/authorize?${query}`;

src/api/__tests__/open-service.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,45 @@ describe('api', () => {
197197
}).toThrow('[mt-link-sdk] Invalid `serviceId` in `openService`, got: invalid');
198198
});
199199

200+
test('saml_subject_id is passed when initialized', () => {
201+
open.mockClear();
202+
203+
const instance = new MtLinkSdk();
204+
instance.init('clientId', { samlSubjectId: 'samlSubjectId' });
205+
206+
openService(instance.storedOptions, 'myaccount');
207+
208+
expect(open).toBeCalledTimes(1);
209+
210+
const query = qs.stringify({
211+
client_id: 'clientId',
212+
saml_subject_id: 'samlSubjectId',
213+
configs: generateConfigs()
214+
});
215+
const url = `${MY_ACCOUNT_DOMAINS.production}/?${query}`;
216+
217+
expect(open).toBeCalledWith(url, '_self', 'noreferrer');
218+
});
219+
220+
test('undefined saml_subject_id should not be passed down', () => {
221+
open.mockClear();
222+
223+
const instance = new MtLinkSdk();
224+
instance.init('clientId', { samlSubjectId: undefined });
225+
226+
openService(instance.storedOptions, 'myaccount');
227+
228+
expect(open).toBeCalledTimes(1);
229+
230+
const query = qs.stringify({
231+
client_id: 'clientId',
232+
configs: generateConfigs()
233+
});
234+
const url = `${MY_ACCOUNT_DOMAINS.production}/?${query}`;
235+
236+
expect(open).toBeCalledWith(url, '_self', 'noreferrer');
237+
});
238+
200239
test('without window', () => {
201240
const windowSpy = jest.spyOn(global, 'window', 'get');
202241
// @ts-ignore: mocking window object to undefined

src/api/authorize.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ export default function authorize(storedOptions: StoredOptions, options: Authori
2323
cobrandClientId,
2424
locale,
2525
scopes: defaultScopes,
26-
redirectUri: defaultRedirectUri
26+
redirectUri: defaultRedirectUri,
27+
samlSubjectId
2728
} = storedOptions;
2829

2930
if (!clientId) {
@@ -61,6 +62,7 @@ export default function authorize(storedOptions: StoredOptions, options: Authori
6162
state,
6263
country: 'JP',
6364
locale,
65+
saml_subject_id: samlSubjectId,
6466
configs: generateConfigs(mergeConfigs(storedOptions, rest))
6567
});
6668

src/api/logout.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ export default function logout(storedOptions: StoredOptions, options: LogoutOpti
99
throw new Error(`[mt-link-sdk] \`logout\` only works in the browser.`);
1010
}
1111

12-
const { clientId, mode, cobrandClientId, locale } = storedOptions;
12+
const { clientId, mode, cobrandClientId, locale, samlSubjectId } = storedOptions;
1313
const { isNewTab, ...rest } = options;
1414

1515
const queryString = stringify({
1616
client_id: clientId,
1717
cobrand_client_id: cobrandClientId,
1818
locale,
19+
saml_subject_id: samlSubjectId,
1920
configs: generateConfigs(mergeConfigs(storedOptions, rest))
2021
});
2122

src/api/open-service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ interface QueryData {
1616
cobrand_client_id?: string;
1717
locale?: string;
1818
configs: string;
19+
saml_subject_id?: string;
1920
}
2021

2122
export default function openService(
@@ -27,14 +28,15 @@ export default function openService(
2728
throw new Error('[mt-link-sdk] `openService` only works in the browser.');
2829
}
2930

30-
const { clientId, mode, cobrandClientId, locale } = storedOptions;
31+
const { clientId, mode, cobrandClientId, locale, samlSubjectId } = storedOptions;
3132
const { isNewTab, view = '', ...rest } = options;
3233

3334
const getQueryValue = (needStringify = true): string | QueryData => {
3435
const query: QueryData = {
3536
client_id: clientId,
3637
cobrand_client_id: cobrandClientId,
3738
locale,
39+
saml_subject_id: samlSubjectId,
3840
configs: generateConfigs(mergeConfigs(storedOptions, rest))
3941
};
4042

src/helper.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { encode } from 'url-safe-base64';
77
import { v4 as uuid } from 'uuid';
88
import storage from './storage';
99

10-
import { Scopes, InitOptions, ConfigsOptions, AuthAction, AuthNMethod } from './typings';
10+
import { Scopes, InitOptions, ConfigsOptions, AuthAction, AuthnMethod } from './typings';
1111

1212
export function constructScopes(scopes: Scopes = ''): string | undefined {
1313
return (Array.isArray(scopes) ? scopes.join(' ') : scopes) || undefined;
@@ -64,7 +64,7 @@ export function mergeConfigs(
6464
}
6565

6666
export function generateConfigs(configs: ConfigsOptions = {}): string {
67-
const snakeCaseConfigs: { [key: string]: string | AuthAction | boolean | AuthNMethod[] | undefined } = {};
67+
const snakeCaseConfigs: { [key: string]: string | AuthAction | boolean | AuthnMethod[] | undefined } = {};
6868

6969
const configKeys = [
7070
'email',
@@ -76,20 +76,23 @@ export function generateConfigs(configs: ConfigsOptions = {}): string {
7676
'forceLogout',
7777
'sdkPlatform',
7878
'sdkVersion',
79-
'authnMethod',
79+
'authnMethod'
8080
];
8181

8282
for (const key in configs) {
8383
if (configKeys.indexOf(key) !== -1) {
8484
snakeCaseConfigs[snakeCase(key)] = configs[key as keyof ConfigsOptions];
8585
}
8686
}
87-
88-
return stringify({
89-
sdk_platform: 'js',
90-
sdk_version: __VERSION__,
91-
...snakeCaseConfigs
92-
}, { indices: false, arrayFormat: 'brackets', encode: false });
87+
88+
return stringify(
89+
{
90+
sdk_platform: 'js',
91+
sdk_version: __VERSION__,
92+
...snakeCaseConfigs
93+
},
94+
{ indices: false, arrayFormat: 'brackets' }
95+
);
9396
}
9497

9598
export function generateCodeChallenge(): string {

0 commit comments

Comments
 (0)