Skip to content

Commit bc6240c

Browse files
authored
Merge pull request #110 from mt-gdiniz/master
[LINK-2174] add support for authnMethod config
2 parents 98a5785 + 03a15a4 commit bc6240c

File tree

12 files changed

+230
-37
lines changed

12 files changed

+230
-37
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: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,23 @@ mtLinkSdk.init(clientId, options);
6565
| options.mode | `production`, `staging`, `develop`, `local` | false | `production` | <p>Environment for the SDK to connect to, the SDK will connect to the Moneytree production server by default.<ul><li>Moneytree clients should use `staging` for development as `develop` may contain unstable features.</li><li>`local` should only be used for SDK development as it has local dependencies.</li></ul></p> |
6666
| options.locale | string | false | Auto detect. | Force Moneytree to load content in this specific locale. A default value will be auto detected based on guest langauges configurations and location if available. Check this [spec](https://www.w3.org/TR/html401/struct/dirlang.html#h-8.1.1) for more information.<br /><br />Currently supported values are:<br />`en`, `en-AU`, `ja`. |
6767
| options.cobrandClientId (private) | string | false | | <strong>NOTE: This is an internal attribute. Please do not use it unless instructed by your integration representative.</strong><br /><br />Brand Moneytree apps with client's branding. E.g: logo or theme. |
68+
| options.samlSubjectId | string | false | | Sets subject Id for saml session version. |
69+
70+
### setSamlSubjectId
71+
72+
The `setSamlSubjectId` method is used to set the value of the `saml_subject_id` parameter.
73+
The `setSamlSubjectId` parameter allows the client to pass a guest identifier to Moneytree so that Moneytree can forward it to the Identity Provider (IdP) via the SAMLRequest.
74+
The `saml_subject_id` parameter will be forwarded to the `authorize`, `logout` and `open-service` methods when defined.
75+
76+
<h6>Usage:</h6>
77+
78+
```javascript
79+
mtLinkSdk.setSamlSubjectId(samlSubjectId);
80+
```
81+
82+
| Parameter | Type | Required | Default Value | Description |
83+
| ------------- | ------ | -------- | ------------- | ---------------------------------------- |
84+
| sublSubjectId | string | true | | <p>Set the saml_subject_id parameter</p> |
6885

6986
### authorize
7087

@@ -320,8 +337,9 @@ These common options are used in multiple APIs. Instead of repeating the same op
320337
| options.backTo | string | false | Value set during `init`. | A redirection URL for redirecting a guest back to in the following condition: <li>Guest clicks on `Back to [App Name]` button in any Moneytree screen.</li><li>Guest refuses to give consent to access permission in the consent screen.</li><li>Guest logs out from Moneytree via an app with this client id</li><li>Revoke an app's consent from settings screen opened via an app with this client id</li><br /><br /><strong>NOTE:</strong> No `Back to [App Name]` button will be shown if this value is not set, and any of the actions mentioned above will redirect the guest back to login screen by default. |
321338
| <span id="authorize_option_auth_action">options.authAction</span> | `login`, `signup` | false | Value set during `init`.<p><strong>OR</strong></p>`login` | Show login or sign up screen when a session does not exist during an `authorize` call. |
322339
| options.showAuthToggle | boolean | false | Value set during `init`.<p><strong>OR</strong></p>`true` | If you wish to disable the login to sign up form toggle button and vice-versa in the auth screen, set this to `false`. |
323-
| options.showRememberMe | boolean | false | Value set during `init`.<p><strong>OR</strong></p>`true` | If you wish to disable the `Stay logged in for 30 days` checkbox in the login screen, set this to `false`. |
340+
| options.showRememberMe | boolean | false | Value set during `init`.<p><strong>OR</strong></p>`true` | If you wish to disable the `Stay logged in for 30 days` checkbox in the login screen, set this to `false`. |
324341
| options.isNewTab | boolean | false | Value set during `init`.<p><strong>OR</strong></p>`false` | Call method and open/render in a new browser tab, by default all views open in the same tab. |
342+
| options.authnMethod | string | | Value set during `init`. | Use different authentication methods. This can be a string with the following values:<br /><br />`sso`, `passwordless`, `credentials`. |
325343
| options.sdkPlatform (private) | string | false | Generated by the SDK. | <strong>NOTE: this is for Moneytree internal use, please do not use it to avoid unintended behavior!</strong><br /><br />Indicating sdk platform. |
326344
| options.sdkVersion (private) | semver | false | Generated by the SDK. | <strong>NOTE: this is for Moneytree internal use, please do not use it to avoid unintended behavior!</strong><br /><br />Indicating sdk version. |
327345

src/__tests__/helper.test.ts

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { constructScopes, getIsTabValue, mergeConfigs, generateConfigs } from '../helper';
22
import packageJson from '../../package.json';
3+
import { AuthAction, AuthnMethod, ConfigsOptions } from '../typings';
34

45
describe('helper', () => {
56
test('constuctScopes', () => {
@@ -23,16 +24,18 @@ describe('helper', () => {
2324
backTo: 'backTo',
2425
authAction: 'signup',
2526
showAuthToggle: true,
26-
showRememberMe: true
27+
showRememberMe: true,
28+
authnMethod: 'sso'
2729
},
2830
{}
2931
)
30-
).toMatchObject({
32+
).toEqual({
33+
authAction: 'signup',
3134
email: 'email',
3235
backTo: 'backTo',
33-
authAction: 'signup',
36+
showRememberMe: true,
3437
showAuthToggle: true,
35-
showRememberMe: true
38+
authnMethod: 'sso'
3639
});
3740
});
3841

@@ -73,13 +76,14 @@ describe('helper', () => {
7376
showAuthToggle: true,
7477
showRememberMe: true,
7578
// @ts-ignore: set unsupported key
76-
whatIsThis: false
79+
whatIsThis: false,
80+
authnMethod: 'not really valid' as AuthnMethod
7781
},
7882
{
7983
whatIsThis2: false
8084
}
8185
)
82-
).toMatchObject({
86+
).toEqual({
8387
email: 'email',
8488
backTo: 'backTo',
8589
authAction: 'signup',
@@ -116,15 +120,55 @@ describe('helper', () => {
116120

117121
describe('generateConfigs', () => {
118122
test('with parameter', () => {
119-
expect(
120-
generateConfigs({
121-
email: 'email',
122-
backTo: 'backTo',
123-
authAction: 'signup',
124-
showAuthToggle: true,
125-
showRememberMe: true
126-
})
127-
).toBe(
123+
const configPayload: ConfigsOptions = {
124+
email: 'email',
125+
backTo: 'backTo',
126+
authAction: 'signup',
127+
showAuthToggle: true,
128+
showRememberMe: true,
129+
authnMethod: 'sso'
130+
};
131+
132+
expect(generateConfigs(configPayload)).toBe(
133+
`sdk_platform=js&sdk_version=${packageJson.version}&email=email&back_to=backTo&auth_action=signup&show_auth_toggle=true` +
134+
`&show_remember_me=true&authn_method=sso`
135+
);
136+
});
137+
138+
test('query encoding should make sure config params are also encoded', () => {
139+
const configPayload: ConfigsOptions = {
140+
email: 'email&!@#(*)-304should be_encoded',
141+
backTo: 'backTo #!@with []special= chars',
142+
authAction: 'signup',
143+
showAuthToggle: true,
144+
showRememberMe: true,
145+
authnMethod: 'sso'
146+
};
147+
148+
expect(generateConfigs(configPayload)).toBe(
149+
`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=sso`
150+
);
151+
});
152+
153+
test('Should raise an error when passing an array in authnMethod', () => {
154+
const configPayload: ConfigsOptions = {
155+
authnMethod: ['oh-not-valid', 'should raise'] as unknown as AuthnMethod
156+
};
157+
158+
expect(() => generateConfigs(configPayload)).toThrow(TypeError);
159+
});
160+
161+
test('Should reject invalid authnMethod from config', () => {
162+
const configPayload: ConfigsOptions = {
163+
email: 'email',
164+
backTo: 'backTo',
165+
authAction: 'signup',
166+
showAuthToggle: true,
167+
showRememberMe: true,
168+
authnMethod: 'oh-not-valid' as AuthnMethod
169+
};
170+
171+
expect(generateConfigs(configPayload)).toBe(
128172
`sdk_platform=js&sdk_version=${packageJson.version}&email=email&back_to=backTo&auth_action=signup&show_auth_toggle=true` +
129173
`&show_remember_me=true`
130174
);

src/__tests__/index.test.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,19 @@ describe('index', () => {
2222
const instance = new MtLinkSdk();
2323

2424
instance.init('clientId', {
25-
redirectUri: 'redirectUri'
25+
redirectUri: 'redirectUri',
26+
authnMethod: 'sso',
27+
samlSubjectId: 'samlSubjectId'
2628
});
2729

2830
const options = instance.storedOptions;
2931
const storedOptions = {
3032
clientId: options.clientId,
3133
mode: options.mode,
3234
redirectUri: options.redirectUri,
33-
state: options.state
35+
state: options.state,
36+
authnMethod: options.authnMethod,
37+
samlSubjectId: options.samlSubjectId
3438
};
3539

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

9599
expect(mtLinkSdk.storedOptions.mode).toBe('production');
96100
});
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+
});
97116
});

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

0 commit comments

Comments
 (0)