Skip to content

Commit

Permalink
Add helpers and some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremy-jung1 committed Sep 18, 2024
1 parent 2af2671 commit c5cdf45
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "error"
}
},
{
"files": ["./src/static/helpers/passwordlessHelper.ts"],
"rules": {
"@typescript-eslint/ban-types": "off"
}
}
],
"settings": {
Expand Down
1 change: 1 addition & 0 deletions src/static/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// Doing so may lead to circular dependencies or duplicate exports (due to rollup mangling the types)
export * from './environment';
export * from './slasHelper';
export * from './passwordlessHelper';
export * from './types';
export * from './customApi';
export * from './fetchHelper';
130 changes: 130 additions & 0 deletions src/static/helpers/passwordlessHelper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* @jest-environment node
*/
/* eslint header/header: "off", max-lines:"off" */
/*
* Copyright (c) 2022, salesforce.com, inc.
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import nock from 'nock';
import {ShopperLogin} from '../../lib/shopperLogin';
import * as slasHelper from './slasHelper';
import {
authorizePasswordless,
getPasswordLessAccessToken,
} from './passwordlessHelper';

type slasHelperType = typeof slasHelper;
// Mock the module
jest.mock('./slasHelper', () => {
const actualUtils = jest.requireActual<slasHelperType>('./slasHelper');

Check warning on line 23 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (12)

Delete `··`

Check warning on line 23 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (14)

Delete `··`

Check warning on line 23 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (16)

Delete `··`

Check warning on line 23 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (18)

Delete `··`
const createCodeVerifierMock = jest.fn(() => 'code_verifier')

Check warning on line 24 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (12)

Replace `····const·createCodeVerifierMock·=·jest.fn(()·=>·'code_verifier')` with `··const·createCodeVerifierMock·=·jest.fn(()·=>·'code_verifier');`

Check warning on line 24 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (14)

Replace `····const·createCodeVerifierMock·=·jest.fn(()·=>·'code_verifier')` with `··const·createCodeVerifierMock·=·jest.fn(()·=>·'code_verifier');`

Check warning on line 24 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (16)

Replace `····const·createCodeVerifierMock·=·jest.fn(()·=>·'code_verifier')` with `··const·createCodeVerifierMock·=·jest.fn(()·=>·'code_verifier');`

Check warning on line 24 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (18)

Replace `····const·createCodeVerifierMock·=·jest.fn(()·=>·'code_verifier')` with `··const·createCodeVerifierMock·=·jest.fn(()·=>·'code_verifier');`
return {

Check warning on line 25 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (12)

Delete `··`

Check warning on line 25 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (14)

Delete `··`

Check warning on line 25 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (16)

Delete `··`

Check warning on line 25 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (18)

Delete `··`
...actualUtils,

Check warning on line 26 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (12)

Replace `······` with `····`

Check warning on line 26 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (14)

Replace `······` with `····`

Check warning on line 26 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (16)

Replace `······` with `····`

Check warning on line 26 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (18)

Replace `······` with `····`
createCodeVerifier: createCodeVerifierMock, // Mock the specific function

Check warning on line 27 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (12)

Delete `··`

Check warning on line 27 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (14)

Delete `··`

Check warning on line 27 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (16)

Delete `··`

Check warning on line 27 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (18)

Delete `··`
};

Check warning on line 28 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (12)

Delete `··`

Check warning on line 28 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (14)

Delete `··`

Check warning on line 28 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (16)

Delete `··`

Check warning on line 28 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (18)

Delete `··`
});

Check warning on line 29 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (12)

Delete `··`

Check warning on line 29 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (14)

Delete `··`

Check warning on line 29 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (16)

Delete `··`

Check warning on line 29 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (18)

Delete `··`

const authorizePasswordlessCustomerMock = jest.fn();
const getPasswordLessAccessTokenMock = jest.fn();

const createMockSlasClient = () =>
({
clientConfig: {
parameters: {
shortCode: 'short_code',
organizationId: 'organization_id',
clientId: 'client_id',
siteId: 'site_id',
},
},
authorizePasswordlessCustomer: authorizePasswordlessCustomerMock,
getPasswordLessAccessToken: getPasswordLessAccessTokenMock,
} as unknown as ShopperLogin<{
shortCode: string;
organizationId: string;
clientId: string;
siteId: string;
}>);

beforeEach(() => {
jest.clearAllMocks();
nock.cleanAll();
});

describe('authorizePasswordless is working', () => {
test('Correct parameters are used to call SLAS Client authorize', async () => {

Check warning on line 59 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (12)

Delete `··`

Check warning on line 59 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (14)

Delete `··`

Check warning on line 59 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (16)

Delete `··`

Check warning on line 59 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (18)

Delete `··`
const mockSlasClient = createMockSlasClient();

Check warning on line 60 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (12)

Delete `····`

Check warning on line 60 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (14)

Delete `····`

Check warning on line 60 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (16)

Delete `····`

Check warning on line 60 in src/static/helpers/passwordlessHelper.test.ts

View workflow job for this annotation

GitHub Actions / linux-tests (18)

Delete `····`
const {clientId, organizationId, siteId} = mockSlasClient.clientConfig.parameters;

const credentials = {
clientSecret: 'slas_private_secret',
};
const parameters = {
callbackURI: 'www.something.com/callback',
usid: 'a_usid',
userid: 'a_userid',
locale: 'a_locale',
mode: 'callback',
};
const authHeaderExpected = `Basic ${slasHelper.stringToBase64(
`${clientId}:${credentials.clientSecret}`
)}`;
await authorizePasswordless(mockSlasClient, credentials, parameters)
const expectedReqOptions = {
headers: {
Authorization: authHeaderExpected
},
parameters: {
organizationId
},
body: {
user_id: parameters.userid,
mode: parameters.mode,
locale: parameters.locale,
channel_id: siteId,
callback_uri: parameters.callbackURI,
usid: parameters.usid
}
}
expect(authorizePasswordlessCustomerMock).toBeCalledWith(expectedReqOptions);
});
});

describe('getPasswordLessAccessToken is working', () => {
test('Correct parameters are used to call SLAS Client authorize', async () => {
const mockSlasClient = createMockSlasClient();
const {clientId, organizationId} = mockSlasClient.clientConfig.parameters;

const credentials = {
clientSecret: 'slas_private_secret',
};
const parameters = {
pwdlessLoginToken: '123456',
dnt: '1'
};
const authHeaderExpected = `Basic ${slasHelper.stringToBase64(
`${clientId}:${credentials.clientSecret}`
)}`;
await getPasswordLessAccessToken(mockSlasClient, credentials, parameters)
const expectedReqOptions = {
headers: {
Authorization: authHeaderExpected
},
parameters: {
organizationId
},
body: {
dnt: parameters.dnt,
code_verifier: "code_verifier",
grant_type: 'client_credentials',
hint: 'pwdless_login',
pwdless_login_token: parameters.pwdlessLoginToken
}
}
expect(getPasswordLessAccessTokenMock).toBeCalledWith(expectedReqOptions);
});
});
113 changes: 113 additions & 0 deletions src/static/helpers/passwordlessHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2024, Salesforce, Inc.
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import {ShopperLogin, TokenResponse} from '../../lib/shopperLogin';
import {createCodeVerifier, stringToBase64} from './slasHelper';

/**
* Function to send passwordless login token
* **Note** At the moment, passwordless is only supported on private client
* @param slasClient a configured instance of the ShopperLogin SDK client.
* @param credentials - the id and password and clientSecret (if applicable) to login with.
* @param credentials.clientSecret? - secret associated with client ID
* @param parameters - parameters to pass in the API calls.
* @param parameters.callbackURI? - URI to send the passwordless login token to. Must be listed in your SLAS configuration. Required when mode is callback
* @param parameters.usid? - Unique Shopper Identifier to enable personalization.
* @param parameters.userid - User Id for login
* @param parameters.locale - The locale of the template. Not needed for the callback mode
* @param parameters.mode - Medium of sending login token
* @returns Promise of Response or Object
*/
export async function authorizePasswordless(
slasClient: ShopperLogin<{
shortCode: string;
organizationId: string;
clientId: string;
siteId: string;
}>,
credentials: {
clientSecret: string;
},
parameters: {
callbackURI?: string;
usid?: string;
userid: string;
locale?: string;
mode: string;
}
): Promise<Response | Object> {
const authHeaderIdSecret = `Basic ${stringToBase64(
`${slasClient.clientConfig.parameters.clientId}:${credentials.clientSecret}`
)}`;
const tokenBody = {
user_id: parameters.userid,
mode: parameters.mode,
...(parameters.locale && {locale: parameters.locale}),
...(parameters.usid && {usid: parameters.usid}),
channel_id: slasClient.clientConfig.parameters.siteId,
...(parameters.callbackURI && {callback_uri: parameters.callbackURI}),
};

return slasClient.authorizePasswordlessCustomer({
headers: {
Authorization: authHeaderIdSecret,
},
parameters: {
organizationId: slasClient.clientConfig.parameters.organizationId,
},
body: tokenBody,
});
}

/**
* Function to login user with passwordless login token
* **Note** At the moment, passwordless is only supported on private client
* @param slasClient a configured instance of the ShopperLogin SDK client.
* @param credentials - the id and password and clientSecret (if applicable) to login with.
* @param credentials.clientSecret? - secret associated with client ID
* @param parameters - parameters to pass in the API calls.
* @param parameters.callbackURI? - URI to send the passwordless login token to. Must be listed in your SLAS configuration. Required when mode is callback
* @param parameters.pwdlessLoginToken - Passwordless login token
* @param parameters.dnt? - Optional parameter to enable Do Not Track (DNT) for the user.
* @returns Promise of Response or Object
*/
export async function getPasswordLessAccessToken(
slasClient: ShopperLogin<{
shortCode: string;
organizationId: string;
clientId: string;
siteId: string;
}>,
credentials: {
clientSecret: string;
},
parameters: {
pwdlessLoginToken: string;
dnt?: string;
}
): Promise<TokenResponse> {
const codeVerifier = createCodeVerifier();
const authHeaderIdSecret = `Basic ${stringToBase64(
`${slasClient.clientConfig.parameters.clientId}:${credentials.clientSecret}`
)}`;

const tokenBody = {
grant_type: 'client_credentials',
hint: 'pwdless_login',
pwdless_login_token: parameters.pwdlessLoginToken,
code_verifier: codeVerifier,
...(parameters.dnt && {dnt: parameters.dnt}),
};
return slasClient.getPasswordLessAccessToken({
headers: {
Authorization: authHeaderIdSecret,
},
parameters: {
organizationId: slasClient.clientConfig.parameters.organizationId,
},
body: tokenBody,
});
}

0 comments on commit c5cdf45

Please sign in to comment.