@openstax/ts-utils 1.44.4 → 1.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.d.ts +0 -1
- package/dist/cjs/index.js +0 -1
- package/dist/cjs/services/accountsGateway/index.d.ts +8 -4
- package/dist/cjs/services/accountsGateway/index.js +10 -2
- package/dist/cjs/services/authProvider/index.d.ts +6 -1
- package/dist/cjs/services/httpMessage/index.d.ts +2 -0
- package/dist/cjs/services/httpMessage/index.js +7 -0
- package/dist/cjs/services/httpMessage/signer.d.ts +15 -0
- package/dist/cjs/services/httpMessage/signer.js +42 -0
- package/dist/cjs/services/keyStore/index.d.ts +20 -0
- package/dist/cjs/services/keyStore/index.js +27 -0
- package/dist/cjs/services/launchParams/signer.d.ts +4 -6
- package/dist/cjs/services/launchParams/signer.js +6 -12
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/esm/index.d.ts +0 -1
- package/dist/esm/index.js +0 -1
- package/dist/esm/services/accountsGateway/index.d.ts +8 -4
- package/dist/esm/services/accountsGateway/index.js +8 -1
- package/dist/esm/services/authProvider/index.d.ts +6 -1
- package/dist/esm/services/httpMessage/index.d.ts +2 -0
- package/dist/esm/services/httpMessage/index.js +2 -0
- package/dist/esm/services/httpMessage/signer.d.ts +15 -0
- package/dist/esm/services/httpMessage/signer.js +38 -0
- package/dist/esm/services/keyStore/index.d.ts +20 -0
- package/dist/esm/services/keyStore/index.js +23 -0
- package/dist/esm/services/launchParams/signer.d.ts +4 -6
- package/dist/esm/services/launchParams/signer.js +6 -12
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
- package/package.json +10 -5
- /package/dist/cjs/services/{httpMessageVerifier/index.d.ts → httpMessage/verifier.d.ts} +0 -0
- /package/dist/cjs/services/{httpMessageVerifier/index.js → httpMessage/verifier.js} +0 -0
- /package/dist/esm/services/{httpMessageVerifier/index.d.ts → httpMessage/verifier.d.ts} +0 -0
- /package/dist/esm/services/{httpMessageVerifier/index.js → httpMessage/verifier.js} +0 -0
package/dist/esm/index.d.ts
CHANGED
package/dist/esm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ConfigProviderForConfig } from '../../config/index.js';
|
|
2
2
|
import { GenericFetch } from '../../fetch/index.js';
|
|
3
3
|
import { JsonCompatibleStruct } from '../../routing/index.js';
|
|
4
|
-
import { ApiUser } from '../authProvider/index.js';
|
|
4
|
+
import { ApiUser, ExternalId } from '../authProvider/index.js';
|
|
5
5
|
import { Logger } from '../logger/index.js';
|
|
6
6
|
export type Config = {
|
|
7
7
|
accountsBase: string;
|
|
@@ -36,12 +36,12 @@ export type FindOrCreateUserPayload = {
|
|
|
36
36
|
export type FindOrCreateUserResponse = {
|
|
37
37
|
id: number;
|
|
38
38
|
uuid: string;
|
|
39
|
-
external_ids:
|
|
39
|
+
external_ids: ExternalId[];
|
|
40
40
|
is_test: boolean;
|
|
41
41
|
sso: string;
|
|
42
42
|
};
|
|
43
43
|
export type FindUserResponse = (FindOrCreateUserResponse & {
|
|
44
|
-
external_ids:
|
|
44
|
+
external_ids: ExternalId[];
|
|
45
45
|
}) | undefined;
|
|
46
46
|
export type LinkUserPayload = {
|
|
47
47
|
userId: number;
|
|
@@ -59,7 +59,7 @@ export type SearchUsersPayload = {
|
|
|
59
59
|
};
|
|
60
60
|
export type SearchUsersResponse = {
|
|
61
61
|
items: Array<ApiUser & {
|
|
62
|
-
external_ids:
|
|
62
|
+
external_ids: ExternalId[];
|
|
63
63
|
} & JsonCompatibleStruct>;
|
|
64
64
|
total_count: number;
|
|
65
65
|
};
|
|
@@ -72,6 +72,10 @@ export type MappedUserInfo<T> = {
|
|
|
72
72
|
platformUserId?: string;
|
|
73
73
|
uuid: string;
|
|
74
74
|
};
|
|
75
|
+
/**
|
|
76
|
+
* Normalize an external_id to always return the string representation.
|
|
77
|
+
*/
|
|
78
|
+
export declare const normalizeExternalId: (externalId: ExternalId) => string;
|
|
75
79
|
export declare const accountsGateway: <C extends string = "accounts">(initializer: Initializer<C>) => (configProvider: { [_key in C]: ConfigProviderForConfig<Config>; }) => ({ logger }: {
|
|
76
80
|
logger: Logger;
|
|
77
81
|
}) => {
|
|
@@ -11,6 +11,12 @@ class ApiError extends Error {
|
|
|
11
11
|
this.status = status;
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Normalize an external_id to always return the string representation.
|
|
16
|
+
*/
|
|
17
|
+
export const normalizeExternalId = (externalId) => {
|
|
18
|
+
return typeof externalId === 'string' ? externalId : externalId.external_id;
|
|
19
|
+
};
|
|
14
20
|
export const accountsGateway = (initializer) => (configProvider) => {
|
|
15
21
|
const config = configProvider[ifDefined(initializer.configSpace, 'accounts')];
|
|
16
22
|
const accountsBase = once(() => resolveConfigValue(config.accountsBase));
|
|
@@ -59,7 +65,8 @@ export const accountsGateway = (initializer) => (configProvider) => {
|
|
|
59
65
|
const searchUsers = async (payload) => request(METHOD.GET, `users?${queryString.stringify(payload)}`, {});
|
|
60
66
|
const updateUser = async (token, body) => request(METHOD.PUT, 'user', { body, token });
|
|
61
67
|
const getPlatformUserId = (externalIds, platformId) => {
|
|
62
|
-
|
|
68
|
+
const normalizedIds = externalIds.map(normalizeExternalId);
|
|
69
|
+
for (const externalId of normalizedIds) {
|
|
63
70
|
const [userPlatformId, userId] = externalId.split('/', 2);
|
|
64
71
|
if (userPlatformId === platformId) {
|
|
65
72
|
return userId;
|
|
@@ -11,6 +11,11 @@ export type TokenUser = {
|
|
|
11
11
|
name: string;
|
|
12
12
|
uuid: string;
|
|
13
13
|
};
|
|
14
|
+
export type ExternalId = string | {
|
|
15
|
+
external_id: string;
|
|
16
|
+
user_id?: number;
|
|
17
|
+
role: string;
|
|
18
|
+
};
|
|
14
19
|
export type ApiUser = TokenUser & {
|
|
15
20
|
id: number;
|
|
16
21
|
first_name: string;
|
|
@@ -27,7 +32,7 @@ export type ApiUser = TokenUser & {
|
|
|
27
32
|
name: string;
|
|
28
33
|
roles: string[];
|
|
29
34
|
}>;
|
|
30
|
-
external_ids:
|
|
35
|
+
external_ids: ExternalId[];
|
|
31
36
|
faculty_status: string;
|
|
32
37
|
is_admin: boolean;
|
|
33
38
|
is_not_gdpr_location: boolean;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ConfigProviderForConfig } from '../../config/index.js';
|
|
2
|
+
import { KeyStore } from '../keyStore/index.js';
|
|
3
|
+
type Config = {
|
|
4
|
+
iss: string;
|
|
5
|
+
};
|
|
6
|
+
interface Initializer<C> {
|
|
7
|
+
configSpace?: C;
|
|
8
|
+
}
|
|
9
|
+
export declare const createHttpMessageSigner: <C extends string = "signer">({ configSpace }: Initializer<C>) => (configProvider: { [_key in C]: ConfigProviderForConfig<Config>; }, services: {
|
|
10
|
+
keyStore: KeyStore;
|
|
11
|
+
}) => {
|
|
12
|
+
signRequest: (method: string, url: string, bodyString: string) => Promise<Record<string, string>>;
|
|
13
|
+
};
|
|
14
|
+
export type HttpMessageSigner = ReturnType<ReturnType<typeof createHttpMessageSigner>>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* spell-checker: ignore httpbis meunier keyid */
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import { createSigner, httpbis } from 'http-message-signatures';
|
|
4
|
+
import { resolveConfigValue } from '../../config/index.js';
|
|
5
|
+
import { once } from '../../misc/helpers.js';
|
|
6
|
+
export const createHttpMessageSigner = ({ configSpace }) => (configProvider, services) => {
|
|
7
|
+
const config = configProvider[configSpace !== null && configSpace !== void 0 ? configSpace : 'signer'];
|
|
8
|
+
const getIss = once(async () => (await resolveConfigValue(config.iss)).replace(/\/+$/, ''));
|
|
9
|
+
const getKey = once(() => services.keyStore.getPrivateKey());
|
|
10
|
+
const getSigner = once(async () => {
|
|
11
|
+
const { pem } = await getKey();
|
|
12
|
+
return createSigner(pem, 'rsa-v1_5-sha256');
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
signRequest: async (method, url, bodyString) => {
|
|
16
|
+
const { kid } = await getKey();
|
|
17
|
+
const digest = createHash('sha256').update(bodyString).digest('base64');
|
|
18
|
+
const key = await getSigner();
|
|
19
|
+
const signed = await httpbis.signMessage({
|
|
20
|
+
key,
|
|
21
|
+
// draft-meunier-http-message-signatures-directory requires signature-agent
|
|
22
|
+
// to be a covered component alongside the standard RFC 9421 derived components
|
|
23
|
+
fields: ['@method', '@target-uri', 'content-digest', 'signature-agent'],
|
|
24
|
+
params: ['keyid', 'alg'],
|
|
25
|
+
paramValues: { keyid: kid, alg: 'rsa-v1_5-sha256' },
|
|
26
|
+
}, {
|
|
27
|
+
method,
|
|
28
|
+
url,
|
|
29
|
+
headers: {
|
|
30
|
+
// RFC 8941 byte sequence: sha-256=:base64value:
|
|
31
|
+
'Content-Digest': `sha-256=:${digest}:`,
|
|
32
|
+
'Signature-Agent': `${await getIss()}/.well-known/jwks.json`,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
return signed.headers;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { JWK } from 'node-jose';
|
|
2
|
+
import { ConfigProviderForConfig } from '../../config/index.js';
|
|
3
|
+
type Config = {
|
|
4
|
+
privateKey: string;
|
|
5
|
+
};
|
|
6
|
+
export type PrivateKey = {
|
|
7
|
+
kid: string;
|
|
8
|
+
pem: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const createKeyStore: (config: ConfigProviderForConfig<Config>) => {
|
|
11
|
+
getPrivateKey: () => Promise<{
|
|
12
|
+
kid: string;
|
|
13
|
+
pem: string;
|
|
14
|
+
}>;
|
|
15
|
+
jwks: () => Promise<{
|
|
16
|
+
keys: JWK.RawKey[];
|
|
17
|
+
}>;
|
|
18
|
+
};
|
|
19
|
+
export type KeyStore = ReturnType<typeof createKeyStore>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { JWK } from 'node-jose';
|
|
2
|
+
import { resolveConfigValue } from '../../config/index.js';
|
|
3
|
+
import { once } from '../../misc/helpers.js';
|
|
4
|
+
export const createKeyStore = (config) => {
|
|
5
|
+
const store = once(async () => {
|
|
6
|
+
const pem = await resolveConfigValue(config.privateKey);
|
|
7
|
+
const keyStore = JWK.createKeyStore();
|
|
8
|
+
const { kid } = await keyStore.add(pem, 'pem');
|
|
9
|
+
const keys = [{ kid, pem }];
|
|
10
|
+
return { keyStore, keys };
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
// this method only supports one key for now (always returns the first key)
|
|
14
|
+
getPrivateKey: async () => {
|
|
15
|
+
const { keys } = await store();
|
|
16
|
+
return keys[0];
|
|
17
|
+
},
|
|
18
|
+
jwks: async () => {
|
|
19
|
+
const { keyStore } = await store();
|
|
20
|
+
return keyStore.toJSON(false);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { JWK } from 'node-jose';
|
|
2
1
|
import { ConfigProviderForConfig } from '../../config/index.js';
|
|
3
2
|
import type { JsonCompatibleStruct } from '../../routing/index.js';
|
|
3
|
+
import { KeyStore } from '../keyStore/index.js';
|
|
4
4
|
type Config = {
|
|
5
5
|
alg: string;
|
|
6
6
|
expiresIn: string;
|
|
7
7
|
iss: string;
|
|
8
|
-
privateKey: string;
|
|
9
8
|
};
|
|
10
9
|
interface Initializer<C> {
|
|
11
10
|
configSpace?: C;
|
|
@@ -13,10 +12,9 @@ interface Initializer<C> {
|
|
|
13
12
|
/**
|
|
14
13
|
* Creates a class that can sign launch params
|
|
15
14
|
*/
|
|
16
|
-
export declare const createLaunchSigner: <C extends string = "launch">({ configSpace }: Initializer<C>) => (configProvider: { [_key in C]: ConfigProviderForConfig<Config>; }
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}>;
|
|
15
|
+
export declare const createLaunchSigner: <C extends string = "launch">({ configSpace }: Initializer<C>) => (configProvider: { [_key in C]: ConfigProviderForConfig<Config>; }, services: {
|
|
16
|
+
keyStore: KeyStore;
|
|
17
|
+
}) => {
|
|
20
18
|
sign: (data: JsonCompatibleStruct, subject: string, maxExp?: number | null) => Promise<string>;
|
|
21
19
|
};
|
|
22
20
|
export type LaunchSigner = ReturnType<ReturnType<typeof createLaunchSigner>>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import jwt from 'jsonwebtoken';
|
|
2
2
|
import ms from 'ms';
|
|
3
|
-
import { JWK } from 'node-jose';
|
|
4
3
|
import { resolveConfigValue } from '../../config/index.js';
|
|
5
4
|
import { ifDefined } from '../../guards/index.js';
|
|
6
5
|
import { once } from '../../index.js';
|
|
@@ -16,18 +15,12 @@ const assertAlg = (alg) => {
|
|
|
16
15
|
/**
|
|
17
16
|
* Creates a class that can sign launch params
|
|
18
17
|
*/
|
|
19
|
-
export const createLaunchSigner = ({ configSpace }) => (configProvider) => {
|
|
18
|
+
export const createLaunchSigner = ({ configSpace }) => (configProvider, services) => {
|
|
20
19
|
const config = configProvider[ifDefined(configSpace, 'launch')];
|
|
21
20
|
const getAlg = once(async () => assertAlg(await resolveConfigValue(config.alg)));
|
|
22
21
|
const getExpiresIn = once(() => resolveConfigValue(config.expiresIn));
|
|
23
22
|
const getIss = once(() => resolveConfigValue(config.iss));
|
|
24
|
-
const
|
|
25
|
-
const getKeyStore = once(async () => {
|
|
26
|
-
const keystore = JWK.createKeyStore();
|
|
27
|
-
await keystore.add(await getPrivateKey(), 'pem');
|
|
28
|
-
return keystore;
|
|
29
|
-
});
|
|
30
|
-
const jwks = async () => (await getKeyStore()).toJSON(false);
|
|
23
|
+
const getKey = once(() => services.keyStore.getPrivateKey());
|
|
31
24
|
const getExpiresInWithMax = async (maxExp) => {
|
|
32
25
|
const expiresIn = await getExpiresIn();
|
|
33
26
|
// The ms library used by jsonwebtoken can handle a value in seconds as well as a string like '1d'
|
|
@@ -40,12 +33,13 @@ export const createLaunchSigner = ({ configSpace }) => (configProvider) => {
|
|
|
40
33
|
return Math.min(expiresInSeconds, maxExpSeconds);
|
|
41
34
|
};
|
|
42
35
|
const sign = async (data, subject, maxExp) => {
|
|
36
|
+
const { kid, pem } = await getKey();
|
|
43
37
|
const alg = await getAlg();
|
|
44
38
|
// expiresIn can be a number of seconds or a string like '1h' or '1d'
|
|
45
39
|
const expiresIn = await getExpiresInWithMax(maxExp);
|
|
46
40
|
const iss = await getIss();
|
|
47
|
-
const header = { alg, iss };
|
|
48
|
-
return jwt.sign(data,
|
|
41
|
+
const header = { alg, iss, kid };
|
|
42
|
+
return jwt.sign(data, pem, { algorithm: alg, expiresIn, header, issuer: iss, subject });
|
|
49
43
|
};
|
|
50
|
-
return {
|
|
44
|
+
return { sign };
|
|
51
45
|
};
|