@arcblock/jwt 1.27.16 → 1.28.1
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/esm/index.d.ts +28 -24
- package/esm/index.js +170 -180
- package/lib/_virtual/rolldown_runtime.js +29 -0
- package/lib/index.d.ts +28 -24
- package/lib/index.js +175 -187
- package/package.json +18 -12
package/esm/index.d.ts
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import { BytesType } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { BytesType } from "@ocap/util";
|
|
2
|
+
|
|
3
|
+
//#region src/index.d.ts
|
|
4
|
+
type JwtBody = {
|
|
5
|
+
iss: string;
|
|
6
|
+
iat: string;
|
|
7
|
+
nbf: string;
|
|
8
|
+
exp: string;
|
|
9
|
+
version: string;
|
|
10
|
+
[key: string]: any;
|
|
9
11
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
type JwtHeader = {
|
|
13
|
+
alg: string;
|
|
14
|
+
type: 'JWT';
|
|
13
15
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
type JwtToken = {
|
|
17
|
+
header: JwtHeader;
|
|
18
|
+
body: JwtBody;
|
|
19
|
+
signature: string;
|
|
18
20
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
type JwtVerifyOptions = Partial<{
|
|
22
|
+
tolerance: number;
|
|
23
|
+
enforceTimestamp: boolean;
|
|
24
|
+
signerKey: string;
|
|
23
25
|
}>;
|
|
24
26
|
/**
|
|
25
27
|
*
|
|
@@ -31,10 +33,10 @@ export type JwtVerifyOptions = Partial<{
|
|
|
31
33
|
* @param {string} [version='1.0.0']
|
|
32
34
|
* @return {*} {string} - hex encoded signature
|
|
33
35
|
*/
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
declare function sign(signer: string, sk?: BytesType, payload?: {}, doSign?: boolean, version?: string): Promise<string>;
|
|
37
|
+
declare function signV2(signer: string, sk?: BytesType, payload?: any): Promise<string>;
|
|
38
|
+
declare function decode(token: string, bodyOnly?: true): JwtBody;
|
|
39
|
+
declare function decode(token: string, bodyOnly?: false): JwtToken;
|
|
38
40
|
/**
|
|
39
41
|
* Verify a jwt token
|
|
40
42
|
*
|
|
@@ -55,4 +57,6 @@ export declare function decode(token: string, bodyOnly?: false): JwtToken;
|
|
|
55
57
|
* }]
|
|
56
58
|
* @return {*} {boolean}
|
|
57
59
|
*/
|
|
58
|
-
|
|
60
|
+
declare function verify(token: string, signerPk: BytesType, options?: JwtVerifyOptions): Promise<boolean>;
|
|
61
|
+
//#endregion
|
|
62
|
+
export { JwtBody, JwtHeader, JwtToken, JwtVerifyOptions, decode, sign, signV2, verify };
|
package/esm/index.js
CHANGED
|
@@ -1,187 +1,177 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const JWT_VERSION_REQUIRE_HASH_BEFORE_SIGN =
|
|
11
|
-
// since ios requires a fixed length of input to sign, we use sha3 here before sign
|
|
1
|
+
import stringify from "json-stable-stringify";
|
|
2
|
+
import semver from "semver";
|
|
3
|
+
import { fromBase64, toBase64, toHex } from "@ocap/util";
|
|
4
|
+
import { isFromPublicKey, isValid, toDid, toStrictHex, toTypeInfo } from "@arcblock/did";
|
|
5
|
+
import { Hasher, getSigner, types } from "@ocap/mcrypto";
|
|
6
|
+
import Debug from "debug";
|
|
7
|
+
|
|
8
|
+
//#region src/index.ts
|
|
9
|
+
const debug = Debug("@arcblock/jwt");
|
|
10
|
+
const JWT_VERSION_REQUIRE_HASH_BEFORE_SIGN = "1.1.0";
|
|
12
11
|
const hasher = Hasher.SHA3.hash256;
|
|
13
12
|
/**
|
|
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
|
-
// @ts-ignore make signature
|
|
69
|
-
const msgHex = toHex(`${headerB64}.${bodyB64}`);
|
|
70
|
-
const msgHash = semver.gte(semver.coerce(version).version, JWT_VERSION_REQUIRE_HASH_BEFORE_SIGN)
|
|
71
|
-
? hasher(msgHex)
|
|
72
|
-
: msgHex;
|
|
73
|
-
// istanbul ignore if
|
|
74
|
-
if (!doSign) {
|
|
75
|
-
return `${headerB64}.${bodyB64}`;
|
|
76
|
-
}
|
|
77
|
-
const sigHex = getSigner(type.pk).sign(msgHash, sk);
|
|
78
|
-
const sigB64 = toBase64(sigHex);
|
|
79
|
-
return [headerB64, bodyB64, sigB64].join('.');
|
|
13
|
+
*
|
|
14
|
+
*
|
|
15
|
+
* @param {string} signer - address string
|
|
16
|
+
* @param {string} sk - hex encoded secret key
|
|
17
|
+
* @param {*} [payload={}] - data to be included before signing
|
|
18
|
+
* @param {boolean} [doSign=true] - do we need to sign the payload or just return the content to be signed
|
|
19
|
+
* @param {string} [version='1.0.0']
|
|
20
|
+
* @return {*} {string} - hex encoded signature
|
|
21
|
+
*/
|
|
22
|
+
async function sign(signer, sk, payload = {}, doSign = true, version = "1.0.0") {
|
|
23
|
+
if (isValid(signer) === false) throw new Error("Cannot do sign with invalid signer");
|
|
24
|
+
const type = toTypeInfo(signer);
|
|
25
|
+
const header = {
|
|
26
|
+
[types.KeyType.SECP256K1]: {
|
|
27
|
+
alg: "ES256K",
|
|
28
|
+
type: "JWT"
|
|
29
|
+
},
|
|
30
|
+
[types.KeyType.ED25519]: {
|
|
31
|
+
alg: "Ed25519",
|
|
32
|
+
type: "JWT"
|
|
33
|
+
},
|
|
34
|
+
[types.KeyType.ETHEREUM]: {
|
|
35
|
+
alg: "Ethereum",
|
|
36
|
+
type: "JWT"
|
|
37
|
+
},
|
|
38
|
+
[types.KeyType.PASSKEY]: {
|
|
39
|
+
alg: "Passkey",
|
|
40
|
+
type: "JWT"
|
|
41
|
+
}
|
|
42
|
+
}[type.pk];
|
|
43
|
+
const headerB64 = toBase64(stringify(header));
|
|
44
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
45
|
+
const body = {
|
|
46
|
+
iss: toDid(signer),
|
|
47
|
+
iat: String(now),
|
|
48
|
+
nbf: String(now),
|
|
49
|
+
exp: String(now + 300),
|
|
50
|
+
version,
|
|
51
|
+
...payload || {}
|
|
52
|
+
};
|
|
53
|
+
Object.keys(body).forEach((x) => {
|
|
54
|
+
if (typeof body[x] === "undefined" || body[x] == null || body[x] === "") delete body[x];
|
|
55
|
+
});
|
|
56
|
+
const bodyB64 = toBase64(stringify(body));
|
|
57
|
+
debug("sign.body", body);
|
|
58
|
+
const msgHex = toHex(`${headerB64}.${bodyB64}`);
|
|
59
|
+
const msgHash = semver.gte(semver.coerce(version).version, JWT_VERSION_REQUIRE_HASH_BEFORE_SIGN) ? hasher(msgHex) : msgHex;
|
|
60
|
+
// istanbul ignore if
|
|
61
|
+
if (!doSign) return `${headerB64}.${bodyB64}`;
|
|
62
|
+
return [
|
|
63
|
+
headerB64,
|
|
64
|
+
bodyB64,
|
|
65
|
+
toBase64(getSigner(type.pk).sign(msgHash, sk))
|
|
66
|
+
].join(".");
|
|
80
67
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return sign(signer, sk, payload, !!sk, '1.1.0');
|
|
68
|
+
async function signV2(signer, sk, payload = {}) {
|
|
69
|
+
return sign(signer, sk, payload, !!sk, "1.1.0");
|
|
84
70
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
71
|
+
function decode(token, bodyOnly = true) {
|
|
72
|
+
const [headerB64, bodyB64, sigB64] = token.split(".");
|
|
73
|
+
const header = JSON.parse(fromBase64(headerB64).toString());
|
|
74
|
+
const body = JSON.parse(fromBase64(bodyB64).toString());
|
|
75
|
+
const sig = Buffer.from(fromBase64(sigB64)).toString("hex");
|
|
76
|
+
if (bodyOnly) return body;
|
|
77
|
+
return {
|
|
78
|
+
header,
|
|
79
|
+
body,
|
|
80
|
+
signature: `0x${toStrictHex(sig)}`
|
|
81
|
+
};
|
|
94
82
|
}
|
|
95
83
|
/**
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
84
|
+
* Verify a jwt token
|
|
85
|
+
*
|
|
86
|
+
* @param {string} token - the jwt token
|
|
87
|
+
* @param {string} signerPk - signer public key
|
|
88
|
+
* @param {{
|
|
89
|
+
* tolerance: number; - number of seconds to tolerant expire
|
|
90
|
+
* enforceTimestamp: boolean; - whether should be verify timestamps?
|
|
91
|
+
* signerKey: string; - which field should be used to pick the signer
|
|
92
|
+
* }} [{
|
|
93
|
+
* tolerance,
|
|
94
|
+
* enforceTimestamp,
|
|
95
|
+
* signerKey,
|
|
96
|
+
* }={
|
|
97
|
+
* tolerance: 5,
|
|
98
|
+
* enforceTimestamp: true,
|
|
99
|
+
* signerKey: 'iss',
|
|
100
|
+
* }]
|
|
101
|
+
* @return {*} {boolean}
|
|
102
|
+
*/
|
|
103
|
+
async function verify(token, signerPk, options) {
|
|
104
|
+
const { tolerance, enforceTimestamp, signerKey } = Object.assign({
|
|
105
|
+
tolerance: 5,
|
|
106
|
+
enforceTimestamp: true,
|
|
107
|
+
signerKey: "iss"
|
|
108
|
+
}, options);
|
|
109
|
+
try {
|
|
110
|
+
const [headerB64, bodyB64] = token.split(".");
|
|
111
|
+
const { header, body, signature } = decode(token, false);
|
|
112
|
+
if (!signature) {
|
|
113
|
+
debug("verify.error.emptySig");
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if (!header.alg) {
|
|
117
|
+
debug("verify.error.emptyAlg");
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
const signerDid = body[signerKey];
|
|
121
|
+
if (!signerDid) {
|
|
122
|
+
debug("verify.error.emptySignerDid");
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if (isFromPublicKey(signerDid, signerPk) === false) {
|
|
126
|
+
debug("verify.error.signerDidAndPkNotMatch");
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
if (enforceTimestamp) {
|
|
130
|
+
const now = Math.ceil(Date.now() / 1e3);
|
|
131
|
+
const exp = Number(body.exp) || 0;
|
|
132
|
+
const iat = Number(body.iat) || 0;
|
|
133
|
+
const nbf = Number(body.nbf) || 0;
|
|
134
|
+
debug("verify.enforceTimestamp", {
|
|
135
|
+
now,
|
|
136
|
+
exp,
|
|
137
|
+
iat,
|
|
138
|
+
nbf
|
|
139
|
+
});
|
|
140
|
+
if (exp && exp + tolerance < now) {
|
|
141
|
+
debug("verify.error.expired");
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
if (iat && iat > now && iat - now > tolerance) {
|
|
145
|
+
debug("verify.error.issuedAt");
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
if (nbf && nbf > now && nbf - now > tolerance) {
|
|
149
|
+
debug("verify.error.notBefore");
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const signers = {
|
|
154
|
+
secp256k1: getSigner(types.KeyType.SECP256K1),
|
|
155
|
+
es256k: getSigner(types.KeyType.SECP256K1),
|
|
156
|
+
ed25519: getSigner(types.KeyType.ED25519),
|
|
157
|
+
ethereum: getSigner(types.KeyType.ETHEREUM),
|
|
158
|
+
passkey: getSigner(types.KeyType.PASSKEY)
|
|
159
|
+
};
|
|
160
|
+
const alg = header.alg.toLowerCase();
|
|
161
|
+
if (signers[alg]) {
|
|
162
|
+
const msgHex = toHex(`${headerB64}.${bodyB64}`);
|
|
163
|
+
const version = body.version && semver.coerce(body.version) ? semver.coerce(body.version).version : "";
|
|
164
|
+
if (version && version === JWT_VERSION_REQUIRE_HASH_BEFORE_SIGN) return signers[alg].verify(hasher(msgHex), signature, signerPk);
|
|
165
|
+
return signers[alg].verify(msgHex, signature, signerPk);
|
|
166
|
+
}
|
|
167
|
+
debug("verify.error.crypto");
|
|
168
|
+
return false;
|
|
169
|
+
} catch (err) {
|
|
170
|
+
debug("verify.error.exception");
|
|
171
|
+
debug(err);
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
187
174
|
}
|
|
175
|
+
|
|
176
|
+
//#endregion
|
|
177
|
+
export { decode, sign, signV2, verify };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
|
|
29
|
+
exports.__toESM = __toESM;
|
package/lib/index.d.ts
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import { BytesType } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { BytesType } from "@ocap/util";
|
|
2
|
+
|
|
3
|
+
//#region src/index.d.ts
|
|
4
|
+
type JwtBody = {
|
|
5
|
+
iss: string;
|
|
6
|
+
iat: string;
|
|
7
|
+
nbf: string;
|
|
8
|
+
exp: string;
|
|
9
|
+
version: string;
|
|
10
|
+
[key: string]: any;
|
|
9
11
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
type JwtHeader = {
|
|
13
|
+
alg: string;
|
|
14
|
+
type: 'JWT';
|
|
13
15
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
type JwtToken = {
|
|
17
|
+
header: JwtHeader;
|
|
18
|
+
body: JwtBody;
|
|
19
|
+
signature: string;
|
|
18
20
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
type JwtVerifyOptions = Partial<{
|
|
22
|
+
tolerance: number;
|
|
23
|
+
enforceTimestamp: boolean;
|
|
24
|
+
signerKey: string;
|
|
23
25
|
}>;
|
|
24
26
|
/**
|
|
25
27
|
*
|
|
@@ -31,10 +33,10 @@ export type JwtVerifyOptions = Partial<{
|
|
|
31
33
|
* @param {string} [version='1.0.0']
|
|
32
34
|
* @return {*} {string} - hex encoded signature
|
|
33
35
|
*/
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
declare function sign(signer: string, sk?: BytesType, payload?: {}, doSign?: boolean, version?: string): Promise<string>;
|
|
37
|
+
declare function signV2(signer: string, sk?: BytesType, payload?: any): Promise<string>;
|
|
38
|
+
declare function decode(token: string, bodyOnly?: true): JwtBody;
|
|
39
|
+
declare function decode(token: string, bodyOnly?: false): JwtToken;
|
|
38
40
|
/**
|
|
39
41
|
* Verify a jwt token
|
|
40
42
|
*
|
|
@@ -55,4 +57,6 @@ export declare function decode(token: string, bodyOnly?: false): JwtToken;
|
|
|
55
57
|
* }]
|
|
56
58
|
* @return {*} {boolean}
|
|
57
59
|
*/
|
|
58
|
-
|
|
60
|
+
declare function verify(token: string, signerPk: BytesType, options?: JwtVerifyOptions): Promise<boolean>;
|
|
61
|
+
//#endregion
|
|
62
|
+
export { JwtBody, JwtHeader, JwtToken, JwtVerifyOptions, decode, sign, signV2, verify };
|
package/lib/index.js
CHANGED
|
@@ -1,196 +1,184 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const debug_1 = __importDefault(require("debug"));
|
|
17
|
-
const debug = (0, debug_1.default)('@arcblock/jwt');
|
|
18
|
-
// we start hashing before signing after 1.1
|
|
19
|
-
const JWT_VERSION_REQUIRE_HASH_BEFORE_SIGN = '1.1.0';
|
|
20
|
-
// since ios requires a fixed length of input to sign, we use sha3 here before sign
|
|
21
|
-
const hasher = mcrypto_1.Hasher.SHA3.hash256;
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.js');
|
|
2
|
+
let json_stable_stringify = require("json-stable-stringify");
|
|
3
|
+
json_stable_stringify = require_rolldown_runtime.__toESM(json_stable_stringify);
|
|
4
|
+
let semver = require("semver");
|
|
5
|
+
semver = require_rolldown_runtime.__toESM(semver);
|
|
6
|
+
let _ocap_util = require("@ocap/util");
|
|
7
|
+
let _arcblock_did = require("@arcblock/did");
|
|
8
|
+
let _ocap_mcrypto = require("@ocap/mcrypto");
|
|
9
|
+
let debug = require("debug");
|
|
10
|
+
debug = require_rolldown_runtime.__toESM(debug);
|
|
11
|
+
|
|
12
|
+
//#region src/index.ts
|
|
13
|
+
const debug$1 = (0, debug.default)("@arcblock/jwt");
|
|
14
|
+
const JWT_VERSION_REQUIRE_HASH_BEFORE_SIGN = "1.1.0";
|
|
15
|
+
const hasher = _ocap_mcrypto.Hasher.SHA3.hash256;
|
|
22
16
|
/**
|
|
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
|
-
// @ts-ignore make signature
|
|
78
|
-
const msgHex = (0, util_1.toHex)(`${headerB64}.${bodyB64}`);
|
|
79
|
-
const msgHash = semver_1.default.gte(semver_1.default.coerce(version).version, JWT_VERSION_REQUIRE_HASH_BEFORE_SIGN)
|
|
80
|
-
? hasher(msgHex)
|
|
81
|
-
: msgHex;
|
|
82
|
-
// istanbul ignore if
|
|
83
|
-
if (!doSign) {
|
|
84
|
-
return `${headerB64}.${bodyB64}`;
|
|
85
|
-
}
|
|
86
|
-
const sigHex = (0, mcrypto_1.getSigner)(type.pk).sign(msgHash, sk);
|
|
87
|
-
const sigB64 = (0, util_1.toBase64)(sigHex);
|
|
88
|
-
return [headerB64, bodyB64, sigB64].join('.');
|
|
17
|
+
*
|
|
18
|
+
*
|
|
19
|
+
* @param {string} signer - address string
|
|
20
|
+
* @param {string} sk - hex encoded secret key
|
|
21
|
+
* @param {*} [payload={}] - data to be included before signing
|
|
22
|
+
* @param {boolean} [doSign=true] - do we need to sign the payload or just return the content to be signed
|
|
23
|
+
* @param {string} [version='1.0.0']
|
|
24
|
+
* @return {*} {string} - hex encoded signature
|
|
25
|
+
*/
|
|
26
|
+
async function sign(signer, sk, payload = {}, doSign = true, version = "1.0.0") {
|
|
27
|
+
if ((0, _arcblock_did.isValid)(signer) === false) throw new Error("Cannot do sign with invalid signer");
|
|
28
|
+
const type = (0, _arcblock_did.toTypeInfo)(signer);
|
|
29
|
+
const header = {
|
|
30
|
+
[_ocap_mcrypto.types.KeyType.SECP256K1]: {
|
|
31
|
+
alg: "ES256K",
|
|
32
|
+
type: "JWT"
|
|
33
|
+
},
|
|
34
|
+
[_ocap_mcrypto.types.KeyType.ED25519]: {
|
|
35
|
+
alg: "Ed25519",
|
|
36
|
+
type: "JWT"
|
|
37
|
+
},
|
|
38
|
+
[_ocap_mcrypto.types.KeyType.ETHEREUM]: {
|
|
39
|
+
alg: "Ethereum",
|
|
40
|
+
type: "JWT"
|
|
41
|
+
},
|
|
42
|
+
[_ocap_mcrypto.types.KeyType.PASSKEY]: {
|
|
43
|
+
alg: "Passkey",
|
|
44
|
+
type: "JWT"
|
|
45
|
+
}
|
|
46
|
+
}[type.pk];
|
|
47
|
+
const headerB64 = (0, _ocap_util.toBase64)((0, json_stable_stringify.default)(header));
|
|
48
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
49
|
+
const body = {
|
|
50
|
+
iss: (0, _arcblock_did.toDid)(signer),
|
|
51
|
+
iat: String(now),
|
|
52
|
+
nbf: String(now),
|
|
53
|
+
exp: String(now + 300),
|
|
54
|
+
version,
|
|
55
|
+
...payload || {}
|
|
56
|
+
};
|
|
57
|
+
Object.keys(body).forEach((x) => {
|
|
58
|
+
if (typeof body[x] === "undefined" || body[x] == null || body[x] === "") delete body[x];
|
|
59
|
+
});
|
|
60
|
+
const bodyB64 = (0, _ocap_util.toBase64)((0, json_stable_stringify.default)(body));
|
|
61
|
+
debug$1("sign.body", body);
|
|
62
|
+
const msgHex = (0, _ocap_util.toHex)(`${headerB64}.${bodyB64}`);
|
|
63
|
+
const msgHash = semver.default.gte(semver.default.coerce(version).version, JWT_VERSION_REQUIRE_HASH_BEFORE_SIGN) ? hasher(msgHex) : msgHex;
|
|
64
|
+
// istanbul ignore if
|
|
65
|
+
if (!doSign) return `${headerB64}.${bodyB64}`;
|
|
66
|
+
return [
|
|
67
|
+
headerB64,
|
|
68
|
+
bodyB64,
|
|
69
|
+
(0, _ocap_util.toBase64)((0, _ocap_mcrypto.getSigner)(type.pk).sign(msgHash, sk))
|
|
70
|
+
].join(".");
|
|
89
71
|
}
|
|
90
|
-
// eslint-disable-next-line require-await
|
|
91
72
|
async function signV2(signer, sk, payload = {}) {
|
|
92
|
-
|
|
73
|
+
return sign(signer, sk, payload, !!sk, "1.1.0");
|
|
93
74
|
}
|
|
94
75
|
function decode(token, bodyOnly = true) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
76
|
+
const [headerB64, bodyB64, sigB64] = token.split(".");
|
|
77
|
+
const header = JSON.parse((0, _ocap_util.fromBase64)(headerB64).toString());
|
|
78
|
+
const body = JSON.parse((0, _ocap_util.fromBase64)(bodyB64).toString());
|
|
79
|
+
const sig = Buffer.from((0, _ocap_util.fromBase64)(sigB64)).toString("hex");
|
|
80
|
+
if (bodyOnly) return body;
|
|
81
|
+
return {
|
|
82
|
+
header,
|
|
83
|
+
body,
|
|
84
|
+
signature: `0x${(0, _arcblock_did.toStrictHex)(sig)}`
|
|
85
|
+
};
|
|
103
86
|
}
|
|
104
87
|
/**
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// eslint-disable-next-line require-await
|
|
88
|
+
* Verify a jwt token
|
|
89
|
+
*
|
|
90
|
+
* @param {string} token - the jwt token
|
|
91
|
+
* @param {string} signerPk - signer public key
|
|
92
|
+
* @param {{
|
|
93
|
+
* tolerance: number; - number of seconds to tolerant expire
|
|
94
|
+
* enforceTimestamp: boolean; - whether should be verify timestamps?
|
|
95
|
+
* signerKey: string; - which field should be used to pick the signer
|
|
96
|
+
* }} [{
|
|
97
|
+
* tolerance,
|
|
98
|
+
* enforceTimestamp,
|
|
99
|
+
* signerKey,
|
|
100
|
+
* }={
|
|
101
|
+
* tolerance: 5,
|
|
102
|
+
* enforceTimestamp: true,
|
|
103
|
+
* signerKey: 'iss',
|
|
104
|
+
* }]
|
|
105
|
+
* @return {*} {boolean}
|
|
106
|
+
*/
|
|
125
107
|
async function verify(token, signerPk, options) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
108
|
+
const { tolerance, enforceTimestamp, signerKey } = Object.assign({
|
|
109
|
+
tolerance: 5,
|
|
110
|
+
enforceTimestamp: true,
|
|
111
|
+
signerKey: "iss"
|
|
112
|
+
}, options);
|
|
113
|
+
try {
|
|
114
|
+
const [headerB64, bodyB64] = token.split(".");
|
|
115
|
+
const { header, body, signature } = decode(token, false);
|
|
116
|
+
if (!signature) {
|
|
117
|
+
debug$1("verify.error.emptySig");
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
if (!header.alg) {
|
|
121
|
+
debug$1("verify.error.emptyAlg");
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
const signerDid = body[signerKey];
|
|
125
|
+
if (!signerDid) {
|
|
126
|
+
debug$1("verify.error.emptySignerDid");
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
if ((0, _arcblock_did.isFromPublicKey)(signerDid, signerPk) === false) {
|
|
130
|
+
debug$1("verify.error.signerDidAndPkNotMatch");
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
if (enforceTimestamp) {
|
|
134
|
+
const now = Math.ceil(Date.now() / 1e3);
|
|
135
|
+
const exp = Number(body.exp) || 0;
|
|
136
|
+
const iat = Number(body.iat) || 0;
|
|
137
|
+
const nbf = Number(body.nbf) || 0;
|
|
138
|
+
debug$1("verify.enforceTimestamp", {
|
|
139
|
+
now,
|
|
140
|
+
exp,
|
|
141
|
+
iat,
|
|
142
|
+
nbf
|
|
143
|
+
});
|
|
144
|
+
if (exp && exp + tolerance < now) {
|
|
145
|
+
debug$1("verify.error.expired");
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
if (iat && iat > now && iat - now > tolerance) {
|
|
149
|
+
debug$1("verify.error.issuedAt");
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
if (nbf && nbf > now && nbf - now > tolerance) {
|
|
153
|
+
debug$1("verify.error.notBefore");
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const signers = {
|
|
158
|
+
secp256k1: (0, _ocap_mcrypto.getSigner)(_ocap_mcrypto.types.KeyType.SECP256K1),
|
|
159
|
+
es256k: (0, _ocap_mcrypto.getSigner)(_ocap_mcrypto.types.KeyType.SECP256K1),
|
|
160
|
+
ed25519: (0, _ocap_mcrypto.getSigner)(_ocap_mcrypto.types.KeyType.ED25519),
|
|
161
|
+
ethereum: (0, _ocap_mcrypto.getSigner)(_ocap_mcrypto.types.KeyType.ETHEREUM),
|
|
162
|
+
passkey: (0, _ocap_mcrypto.getSigner)(_ocap_mcrypto.types.KeyType.PASSKEY)
|
|
163
|
+
};
|
|
164
|
+
const alg = header.alg.toLowerCase();
|
|
165
|
+
if (signers[alg]) {
|
|
166
|
+
const msgHex = (0, _ocap_util.toHex)(`${headerB64}.${bodyB64}`);
|
|
167
|
+
const version = body.version && semver.default.coerce(body.version) ? semver.default.coerce(body.version).version : "";
|
|
168
|
+
if (version && version === JWT_VERSION_REQUIRE_HASH_BEFORE_SIGN) return signers[alg].verify(hasher(msgHex), signature, signerPk);
|
|
169
|
+
return signers[alg].verify(msgHex, signature, signerPk);
|
|
170
|
+
}
|
|
171
|
+
debug$1("verify.error.crypto");
|
|
172
|
+
return false;
|
|
173
|
+
} catch (err) {
|
|
174
|
+
debug$1("verify.error.exception");
|
|
175
|
+
debug$1(err);
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
196
178
|
}
|
|
179
|
+
|
|
180
|
+
//#endregion
|
|
181
|
+
exports.decode = decode;
|
|
182
|
+
exports.sign = sign;
|
|
183
|
+
exports.signV2 = signV2;
|
|
184
|
+
exports.verify = verify;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcblock/jwt",
|
|
3
3
|
"description": "JSON Web Token variant for arcblock DID solutions",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.28.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "wangshijun",
|
|
7
7
|
"email": "shijun@arcblock.io",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"debug": "^4.3.6",
|
|
22
22
|
"json-stable-stringify": "^1.0.1",
|
|
23
23
|
"semver": "^7.6.3",
|
|
24
|
-
"@arcblock/did": "1.
|
|
25
|
-
"@ocap/
|
|
26
|
-
"@ocap/
|
|
27
|
-
"@ocap/
|
|
24
|
+
"@arcblock/did": "1.28.1",
|
|
25
|
+
"@ocap/util": "1.28.1",
|
|
26
|
+
"@ocap/wallet": "1.28.1",
|
|
27
|
+
"@ocap/mcrypto": "1.28.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@arcblock/eslint-config-ts": "0.3.3",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"eslint": "^8.57.0",
|
|
36
36
|
"jest": "^29.7.0",
|
|
37
37
|
"ts-jest": "^29.2.5",
|
|
38
|
+
"tsdown": "^0.18.4",
|
|
38
39
|
"tslib": "^2.4.0",
|
|
39
40
|
"typescript": "^5.6.2"
|
|
40
41
|
},
|
|
@@ -46,8 +47,17 @@
|
|
|
46
47
|
"nodejs"
|
|
47
48
|
],
|
|
48
49
|
"license": "Apache-2.0",
|
|
50
|
+
"sideEffects": false,
|
|
49
51
|
"main": "./lib/index.js",
|
|
50
|
-
"
|
|
52
|
+
"module": "./esm/index.js",
|
|
53
|
+
"types": "./esm/index.d.ts",
|
|
54
|
+
"exports": {
|
|
55
|
+
".": {
|
|
56
|
+
"types": "./esm/index.d.ts",
|
|
57
|
+
"import": "./esm/index.js",
|
|
58
|
+
"default": "./lib/index.js"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
51
61
|
"files": [
|
|
52
62
|
"lib",
|
|
53
63
|
"esm"
|
|
@@ -62,11 +72,7 @@
|
|
|
62
72
|
"lint:fix": "eslint src tests --fix",
|
|
63
73
|
"test": "jest --forceExit --detectOpenHandles",
|
|
64
74
|
"coverage": "npm run test -- --coverage",
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"build:watch": "npm run build -- -w",
|
|
68
|
-
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
69
|
-
"build:esm": "tsc -p tsconfig.esm.json",
|
|
70
|
-
"build": "npm run build:cjs && npm run build:esm"
|
|
75
|
+
"build": "tsdown",
|
|
76
|
+
"build:watch": "tsdown --watch"
|
|
71
77
|
}
|
|
72
78
|
}
|