@openstax/ts-utils 1.1.33 → 1.1.34
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/services/authProvider/decryption.d.ts +4 -1
- package/dist/cjs/services/authProvider/decryption.js +2 -26
- package/dist/cjs/services/authProvider/utils/decryptAndVerify.d.ts +2 -0
- package/dist/cjs/services/authProvider/utils/decryptAndVerify.js +66 -0
- package/dist/cjs/tsconfig.withoutspecs.cjs.tsbuildinfo +1 -1
- package/dist/esm/services/authProvider/decryption.d.ts +4 -1
- package/dist/esm/services/authProvider/decryption.js +2 -26
- package/dist/esm/services/authProvider/utils/decryptAndVerify.d.ts +2 -0
- package/dist/esm/services/authProvider/utils/decryptAndVerify.js +36 -0
- package/dist/esm/tsconfig.withoutspecs.esm.tsbuildinfo +1 -1
- package/package.json +3 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ConfigProviderForConfig } from '../../config';
|
|
2
|
-
import { CookieAuthProvider } from '.';
|
|
2
|
+
import { CookieAuthProvider, User } from '.';
|
|
3
3
|
declare type Config = {
|
|
4
4
|
cookieName: string;
|
|
5
5
|
encryptionPrivateKey: string;
|
|
@@ -8,6 +8,9 @@ declare type Config = {
|
|
|
8
8
|
interface Initializer<C> {
|
|
9
9
|
configSpace?: C;
|
|
10
10
|
}
|
|
11
|
+
export declare type Jwt = {
|
|
12
|
+
sub?: User;
|
|
13
|
+
};
|
|
11
14
|
export declare const decryptionAuthProvider: <C extends string = "decryption">(initializer: Initializer<C>) => (configProvider: { [key in C]: {
|
|
12
15
|
cookieName: import("../../config").ConfigValueProvider<string>;
|
|
13
16
|
encryptionPrivateKey: import("../../config").ConfigValueProvider<string>;
|
|
@@ -1,26 +1,13 @@
|
|
|
1
|
-
import { compactDecrypt, compactVerify, importSPKI } from 'jose';
|
|
2
1
|
import { resolveConfigValue } from '../../config/resolveConfigValue';
|
|
3
2
|
import { ifDefined } from '../../guards';
|
|
4
3
|
import { once } from '../../misc/helpers';
|
|
4
|
+
import { decryptAndVerify } from './utils/decryptAndVerify';
|
|
5
5
|
import { getAuthTokenOrCookie } from '.';
|
|
6
6
|
export const decryptionAuthProvider = (initializer) => (configProvider) => {
|
|
7
7
|
const config = configProvider[ifDefined(initializer.configSpace, 'decryption')];
|
|
8
8
|
const cookieName = once(() => resolveConfigValue(config.cookieName));
|
|
9
9
|
const encryptionPrivateKey = once(() => resolveConfigValue(config.encryptionPrivateKey));
|
|
10
10
|
const signaturePublicKey = once(() => resolveConfigValue(config.signaturePublicKey));
|
|
11
|
-
const decryptAndVerify = async (jwt) => {
|
|
12
|
-
try {
|
|
13
|
-
// Decrypt SSO cookie
|
|
14
|
-
const { plaintext } = await compactDecrypt(jwt, Buffer.from(await encryptionPrivateKey()), // Note: Buffer.from() is node-js only
|
|
15
|
-
{ contentEncryptionAlgorithms: ['A256GCM'], keyManagementAlgorithms: ['dir'] });
|
|
16
|
-
// Verify SSO cookie signature
|
|
17
|
-
const { payload } = await compactVerify(plaintext, await importSPKI(await signaturePublicKey(), 'RS256'), { algorithms: ['RS256'] });
|
|
18
|
-
return payload;
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return undefined;
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
11
|
return ({ request, profile }) => {
|
|
25
12
|
let user;
|
|
26
13
|
const getAuthorizedFetchConfig = profile.track('getAuthorizedFetchConfig', () => async () => {
|
|
@@ -35,18 +22,7 @@ export const decryptionAuthProvider = (initializer) => (configProvider) => {
|
|
|
35
22
|
if (!token) {
|
|
36
23
|
return undefined;
|
|
37
24
|
}
|
|
38
|
-
|
|
39
|
-
if (!payload) {
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
// Note: Uint8Array.toString() returns text in node-js only
|
|
43
|
-
// The browser version is new TextDecoder().decode(payload)
|
|
44
|
-
const jwt = JSON.parse(payload.toString());
|
|
45
|
-
// Allow clock skew up to 5 minutes
|
|
46
|
-
if (!jwt.sub || !jwt.sub.uuid || (jwt.exp && jwt.exp < Math.floor(Date.now() / 1000) - 300)) {
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
|
-
return jwt.sub;
|
|
25
|
+
return decryptAndVerify(token, await encryptionPrivateKey(), await signaturePublicKey());
|
|
50
26
|
});
|
|
51
27
|
return {
|
|
52
28
|
getAuthorizedFetchConfig,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
import { TextEncoder } from 'util';
|
|
3
|
+
import jwt from 'jsonwebtoken';
|
|
4
|
+
import { isPlainObject } from '../../../guards';
|
|
5
|
+
const decrypt = (input, key) => {
|
|
6
|
+
const splitInput = input.split('.');
|
|
7
|
+
const aad = new TextEncoder().encode(splitInput[0]);
|
|
8
|
+
const iv = Buffer.from(splitInput[2], 'base64');
|
|
9
|
+
const ciphertext = Buffer.from(splitInput[3], 'base64');
|
|
10
|
+
const tag = Buffer.from(splitInput[4], 'base64');
|
|
11
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', Buffer.from(key), iv, { authTagLength: 16 });
|
|
12
|
+
decipher.setAAD(aad, { plaintextLength: ciphertext.length });
|
|
13
|
+
decipher.setAuthTag(tag);
|
|
14
|
+
const result = Buffer.concat([
|
|
15
|
+
decipher.update(ciphertext),
|
|
16
|
+
decipher.final(),
|
|
17
|
+
]);
|
|
18
|
+
return result.toString('utf-8');
|
|
19
|
+
};
|
|
20
|
+
export const decryptAndVerify = (token, encryptionPrivateKey, signaturePublicKey) => {
|
|
21
|
+
try {
|
|
22
|
+
// Decrypt SSO cookie
|
|
23
|
+
const plaintext = decrypt(token, encryptionPrivateKey);
|
|
24
|
+
const payload = jwt.verify(plaintext, signaturePublicKey, {
|
|
25
|
+
clockTolerance: 300 // 5 minutes
|
|
26
|
+
});
|
|
27
|
+
if (!isPlainObject(payload) || !isPlainObject(payload.sub) || !payload.sub.uuid) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
// TS is confused because the library types the `sub` as a string
|
|
31
|
+
return payload.sub;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
};
|