-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.ts
103 lines (84 loc) · 2.63 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import { createHmac } from 'crypto';
import type {
DecodedData,
SignaturOptions,
SignaturReturnError,
UnknownRecord,
} from './custom_typings';
export class SignaturError extends Error {
public type: string;
constructor(type: string, message: string) {
super();
this.name = 'SignaturError';
this.message = message;
this.type = type;
}
public toJSON(): SignaturReturnError {
return {
error: {
type: this.type,
message: this.message,
},
};
}
}
function urlSafeBase64(s: string) {
return s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
export function signSync<T = UnknownRecord>(
data: T,
secret: string,
options?: SignaturOptions
): string {
const { separator = '.' }: SignaturOptions = options || {};
if (null == data) {
throw new TypeError(
`Expected 'data' to be defined, but received '${JSON.stringify(data)}'`);
}
if ('string' !== typeof(secret) || !secret.length) {
throw new TypeError(`Expected 'secret' to be defined, but received '${secret}'`);
}
const stringData = JSON.stringify({ data });
const encoded = Buffer.from(stringData, 'utf8').toString('base64');
const signature = createHmac('sha256', secret).update(stringData).digest('base64');
return urlSafeBase64(`${encoded}${separator}${signature}`);
}
export function unsignSync<T = UnknownRecord>(
signature: string,
secret: string,
options?: SignaturOptions
): T {
const { separator = '.' } = options || {} as SignaturOptions;
if ('string' !== typeof(signature) || !signature.length) {
throw new TypeError(`Expected 'signature' to be defined, but received '${signature}'`);
}
if ('string' !== typeof(secret) || !secret.length) {
throw new TypeError(`Expected 'secret' to be defined, but received '${secret}'`);
}
const [hash, enc] = signature.split(separator, 2);
const decoded = Buffer.from(
(hash + '==='.slice((hash.length + 3) % 4))
.replace(/-/g, '+')
.replace(/_/g, '/'), 'base64')
.toString('utf8');
const signedDecoded = urlSafeBase64(
createHmac('sha256', secret).update(decoded).digest('base64'));
if (enc !== signedDecoded) {
throw new SignaturError('invalid_signature', 'Signature not match');
}
return (JSON.parse(decoded) as DecodedData<T>).data;
}
export async function sign<T = UnknownRecord>(
data: T,
secret: string,
options?: SignaturOptions
): Promise<string> {
return signSync<T>(data, secret, options);
}
export async function unsign<T = UnknownRecord>(
signature: string,
secret: string,
options?: SignaturOptions
): Promise<T> {
return unsignSync<T>(signature, secret, options);
}