Skip to content

Commit 79c0e87

Browse files
authored
fix: add optional token validations (#164)
## Related Issues descope/etc#2883 ## Description added optional string validations for top level functions that was missing
1 parent 9d60461 commit 79c0e87

File tree

6 files changed

+85
-18
lines changed

6 files changed

+85
-18
lines changed

packages/core-js-sdk/src/sdk/index.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,17 @@ import withSaml from './saml';
1717
import withTotp from './totp';
1818
import withPassword from './password';
1919
import { JWTResponse, UserResponse } from './types';
20-
import { stringNonEmpty, withValidations } from './validations';
20+
import {
21+
stringNonEmpty,
22+
withValidations,
23+
isStringOrUndefinedValidator,
24+
} from './validations';
2125
import withWebauthn from './webauthn';
2226

2327
const withJwtValidations = withValidations(stringNonEmpty('token'));
28+
const withOptionalTokenValidations = withValidations(
29+
isStringOrUndefinedValidator('token')
30+
);
2431

2532
/** Returns Descope SDK with all available operations */
2633
export default (httpClient: HttpClient) => ({
@@ -34,18 +41,20 @@ export default (httpClient: HttpClient) => ({
3441
webauthn: withWebauthn(httpClient),
3542
password: withPassword(httpClient),
3643
flow: withFlow(httpClient),
37-
refresh: (token?: string) =>
44+
refresh: withOptionalTokenValidations((token?: string) =>
3845
transformResponse<JWTResponse>(
3946
httpClient.post(apiPaths.refresh, {}, { token })
40-
),
41-
logout: (token?: string) =>
42-
transformResponse<never>(httpClient.post(apiPaths.logout, {}, { token })),
43-
logoutAll: (token?: string) =>
44-
transformResponse<never>(
45-
httpClient.post(apiPaths.logoutAll, {}, { token })
46-
),
47-
me: (token?: string) =>
48-
transformResponse<UserResponse>(httpClient.get(apiPaths.me, { token })),
47+
)
48+
),
49+
logout: withOptionalTokenValidations((token?: string) =>
50+
transformResponse<never>(httpClient.post(apiPaths.logout, {}, { token }))
51+
),
52+
logoutAll: withOptionalTokenValidations((token?: string) =>
53+
transformResponse<never>(httpClient.post(apiPaths.logoutAll, {}, { token }))
54+
),
55+
me: withOptionalTokenValidations((token?: string) =>
56+
transformResponse<UserResponse>(httpClient.get(apiPaths.me, { token }))
57+
),
4958
isJwtExpired: withJwtValidations(isJwtExpired),
5059
getTenants: withJwtValidations(getTenants),
5160
getJwtPermissions: withJwtValidations(getJwtPermissions),

packages/core-js-sdk/src/sdk/validations/core.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ export const createValidator =
66
(val) =>
77
!rule(val) ? msg.replace('{val}', val) : false;
88

9+
export const createOrValidator =
10+
(validators: Validator[], defaultMsg?: string): MakeValidator =>
11+
(msg = defaultMsg) =>
12+
(val) => {
13+
const errors = validators.filter((validator) => validator(val));
14+
15+
if (errors.length < validators.length) return false;
16+
17+
return msg ? msg.replace('{val}', val) : errors.join(' OR ');
18+
};
19+
920
export const createValidation = (...validators: Validator[]) => ({
1021
validate: (val: any) => {
1122
validators.forEach((validator) => {

packages/core-js-sdk/src/sdk/validations/index.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
import { createValidation } from './core';
1+
import { createOrValidator, createValidation } from './core';
22
import { Validator } from './types';
3-
import { isEmail, isNotEmpty, isPhone, isString } from './validators';
3+
import {
4+
isEmail,
5+
isNotEmpty,
6+
isPhone,
7+
isString,
8+
isStringOrUndefined,
9+
} from './validators';
410

511
/**
612
*
13+
* Validate that all of the validators passes
714
* @params each parameter is an array of validators, those validators will be verified against the wrapped function argument which in the same place
815
* @throws if any of the validators fails, an error with the relevant message will be thrown
916
*/
@@ -21,6 +28,11 @@ export const withValidations =
2128
export const string = (fieldName: string) => [
2229
isString(`"${fieldName}" must be a string`),
2330
];
31+
32+
export const isStringOrUndefinedValidator = (fieldName: string) => [
33+
isStringOrUndefined(`"${fieldName}" must be string or undefined`),
34+
];
35+
2436
export const stringNonEmpty = (fieldName: string) => [
2537
isString(`"${fieldName}" must be a string`),
2638
isNotEmpty(`"${fieldName}" must not be empty`),

packages/core-js-sdk/src/sdk/validations/validators.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import get from 'lodash.get';
2-
import { createValidation, createValidator } from './core';
2+
import { createOrValidator, createValidation, createValidator } from './core';
33
import { Validator } from './types';
44

55
const regexMatch = (regex: RegExp) => (val: any) => regex.test(val);
66

77
const validateString = (val: any) => typeof val === 'string';
8+
9+
const validateUndefined = (val: any) => val === undefined;
10+
811
const validateEmail = regexMatch(
912
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
1013
);
@@ -30,6 +33,17 @@ export const isString = createValidator(
3033
validateString,
3134
'Input is not a string'
3235
);
36+
37+
export const isUndefined = createValidator(
38+
validateUndefined,
39+
'Input is defined'
40+
);
41+
42+
export const isStringOrUndefined = createOrValidator(
43+
[isString(), isUndefined()],
44+
'Input is not a string or undefined'
45+
);
46+
3347
// export const isPlainObject = createValidator(validatePlainObject, 'Input is not a plain object');
3448
export const hasPathValue = (path: string, rules: Validator[]) =>
3549
createValidator(validatePathValue(path, rules))();

packages/core-js-sdk/test/sdk/sdk.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ describe('sdk', () => {
1414
});
1515

1616
describe('refresh', () => {
17+
it('should throw an error when token is not a string', () => {
18+
expect(() => sdk.refresh({ a: 'b' })).toThrow(
19+
'"token" must be string or undefined'
20+
);
21+
});
1722
it('should send the correct request', () => {
1823
const httpRespJson = { key: 'val' };
1924
const httpResponse = {
@@ -102,6 +107,16 @@ describe('sdk', () => {
102107
});
103108

104109
describe('logout', () => {
110+
it('should throw an error when token is not a string', () => {
111+
expect(() => sdk.logout({ a: 'b' })).toThrow(
112+
'"token" must be string or undefined'
113+
);
114+
});
115+
116+
it('should not throw an error when token is undefined', () => {
117+
expect(() => sdk.logout()).not.toThrow();
118+
});
119+
105120
it('should send the correct request', () => {
106121
const httpRespJson = { key: 'val' };
107122
const httpResponse = {
@@ -124,6 +139,11 @@ describe('sdk', () => {
124139
});
125140

126141
describe('logoutAll', () => {
142+
it('should throw an error when token is not a string', () => {
143+
expect(() => sdk.logoutAll({ a: 'b' })).toThrow(
144+
'"token" must be string or undefined'
145+
);
146+
});
127147
it('should send the correct request', () => {
128148
const httpRespJson = { key: 'val' };
129149
const httpResponse = {
@@ -146,6 +166,11 @@ describe('sdk', () => {
146166
});
147167

148168
describe('me', () => {
169+
it('should throw an error when token is not a string', () => {
170+
expect(() => sdk.me({ a: 'b' })).toThrow(
171+
'"token" must be string or undefined'
172+
);
173+
});
149174
it('should send the correct request', () => {
150175
const httpRespJson = { key: 'val' };
151176
const httpResponse = {

packages/core-js-sdk/test/sdk/webauthn.test.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,6 @@ describe('webauthn', () => {
173173

174174
describe('signIn', () => {
175175
describe('start', () => {
176-
it('should throw an error when loginId is not a string', () => {
177-
expect(sdk.webauthn.signIn.start).toThrow('"loginId" must be a string');
178-
});
179-
180176
it('should not throw an error when loginId is empty', () => {
181177
expect(() => sdk.webauthn.signIn.start('', 'origin')).not.toThrow();
182178
});

0 commit comments

Comments
 (0)