jose 1.27.1 → 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/CHANGELOG.md +36 -1
- package/lib/help/key_utils.js +15 -3
- package/lib/jwa/aes_cbc_hmac_sha2.js +5 -1
- package/lib/jwe/encrypt.js +5 -1
- package/lib/jws/serializers.js +11 -0
- package/lib/jws/sign.js +15 -26
- package/lib/jwt/verify.js +6 -3
- package/package.json +4 -4
- package/types/index.d.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,42 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Changelog
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [1.28.1](https://github.com/panva/jose/compare/v1.28.0...v1.28.1) (2021-04-09)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* defer AES CBC w/ HMAC decryption after tag verification passes ([08e1bc5](https://github.com/panva/jose/commit/08e1bc5ce7120eac78377577acba2f811c3a0b2b))
|
|
11
|
+
|
|
12
|
+
# [1.28.0](https://github.com/panva/jose/compare/v1.27.3...v1.28.0) (2020-08-10)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* support for validating issuer from a list of values ([#91](https://github.com/panva/jose/issues/91)) ([ce6836a](https://github.com/panva/jose/commit/ce6836af88c9e73c29560233f15ed1760c7dcc13))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## [1.27.3](https://github.com/panva/jose/compare/v1.27.2...v1.27.3) (2020-08-04)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* do not mutate unencoded payload when signing for multiple parties ([1695423](https://github.com/panva/jose/commit/169542363f884e4028db9f80086d631e626eb469)), closes [#89](https://github.com/panva/jose/issues/89)
|
|
27
|
+
* ensure "b64" is the same for all recipients edge cases ([d56ec9f](https://github.com/panva/jose/commit/d56ec9f5ddc2612e5ff21fe35d45a56e7153e0e4))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
## [1.27.2](https://github.com/panva/jose/compare/v1.27.1...v1.27.2) (2020-07-01)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Bug Fixes
|
|
35
|
+
|
|
36
|
+
* handle private EC keys without public component ([#86](https://github.com/panva/jose/issues/86)) ([e8ad389](https://github.com/panva/jose/commit/e8ad38993e29747098f7fd1594dde4ce893ba802)), closes [#85](https://github.com/panva/jose/issues/85)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
5
40
|
## [1.27.1](https://github.com/panva/jose/compare/v1.27.0...v1.27.1) (2020-06-01)
|
|
6
41
|
|
|
7
42
|
|
package/lib/help/key_utils.js
CHANGED
|
@@ -4,6 +4,7 @@ const { EOL } = require('os')
|
|
|
4
4
|
|
|
5
5
|
const errors = require('../errors')
|
|
6
6
|
|
|
7
|
+
const { keyObjectSupported } = require('./runtime_support')
|
|
7
8
|
const { createPublicKey } = require('./key_object')
|
|
8
9
|
const base64url = require('./base64url')
|
|
9
10
|
const asn1 = require('./asn1')
|
|
@@ -89,10 +90,21 @@ const keyObjectToJWK = {
|
|
|
89
90
|
const ECPrivateKey = asn1.get('ECPrivateKey')
|
|
90
91
|
|
|
91
92
|
const { privateKey, algorithm: { parameters: { value: crv } } } = PrivateKeyInfo.decode(der)
|
|
92
|
-
const { privateKey: d, publicKey
|
|
93
|
+
const { privateKey: d, publicKey } = ECPrivateKey.decode(privateKey)
|
|
94
|
+
|
|
95
|
+
if (typeof publicKey === 'undefined') {
|
|
96
|
+
if (keyObjectSupported) {
|
|
97
|
+
return {
|
|
98
|
+
...keyObjectToJWK.ec.public(createPublicKey(keyObject)),
|
|
99
|
+
d: base64url.encodeBuffer(d)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
93
102
|
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
throw new errors.JOSENotSupported('Private EC keys without the public key embedded are not supported in your Node.js runtime version')
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const x = publicKey.data.slice(1, ((publicKey.data.length - 1) / 2) + 1)
|
|
107
|
+
const y = publicKey.data.slice(((publicKey.data.length - 1) / 2) + 1)
|
|
96
108
|
|
|
97
109
|
return {
|
|
98
110
|
kty: 'EC',
|
|
@@ -44,13 +44,17 @@ const decrypt = (size, sign, { [KEYOBJECT]: keyObject }, ciphertext, { iv, tag =
|
|
|
44
44
|
const expectedTag = sign({ [KEYOBJECT]: macKey }, macData, tag).slice(0, keySize)
|
|
45
45
|
const macCheckPassed = timingSafeEqual(tag, expectedTag)
|
|
46
46
|
|
|
47
|
+
if (!macCheckPassed) {
|
|
48
|
+
throw new JWEDecryptionFailed()
|
|
49
|
+
}
|
|
50
|
+
|
|
47
51
|
let cleartext
|
|
48
52
|
try {
|
|
49
53
|
const cipher = createDecipheriv(`aes-${size}-cbc`, encKey, iv)
|
|
50
54
|
cleartext = Buffer.concat([cipher.update(ciphertext), cipher.final()])
|
|
51
55
|
} catch (err) {}
|
|
52
56
|
|
|
53
|
-
if (!cleartext
|
|
57
|
+
if (!cleartext) {
|
|
54
58
|
throw new JWEDecryptionFailed()
|
|
55
59
|
}
|
|
56
60
|
|
package/lib/jwe/encrypt.js
CHANGED
|
@@ -190,7 +190,11 @@ class Encrypt {
|
|
|
190
190
|
let aad
|
|
191
191
|
if (this._aad) {
|
|
192
192
|
final.aad = base64url.encode(this._aad)
|
|
193
|
-
aad = Buffer.concat([
|
|
193
|
+
aad = Buffer.concat([
|
|
194
|
+
Buffer.from(final.protected || ''),
|
|
195
|
+
Buffer.from('.'),
|
|
196
|
+
Buffer.from(final.aad)
|
|
197
|
+
])
|
|
194
198
|
} else {
|
|
195
199
|
aad = Buffer.from(final.protected || '')
|
|
196
200
|
}
|
package/lib/jws/serializers.js
CHANGED
|
@@ -44,9 +44,20 @@ const generalSerializer = (payload, recipients) => {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
generalSerializer.validate = (jws, recipients) => {
|
|
47
|
+
let validateB64 = false
|
|
47
48
|
recipients.forEach(({ protectedHeader, unprotectedHeader }) => {
|
|
49
|
+
if (protectedHeader && !validateB64 && 'b64' in protectedHeader) {
|
|
50
|
+
validateB64 = true
|
|
51
|
+
}
|
|
48
52
|
validateCrit(protectedHeader, unprotectedHeader, protectedHeader ? protectedHeader.crit : undefined)
|
|
49
53
|
})
|
|
54
|
+
|
|
55
|
+
if (validateB64) {
|
|
56
|
+
const values = recipients.map(({ protectedHeader }) => protectedHeader && protectedHeader.b64)
|
|
57
|
+
if (!values.every((actual, i, [expected]) => actual === expected)) {
|
|
58
|
+
throw new JWSInvalid('the "b64" Header Parameter value MUST be the same for all recipients')
|
|
59
|
+
}
|
|
60
|
+
}
|
|
50
61
|
}
|
|
51
62
|
|
|
52
63
|
const isJSON = (input) => {
|
package/lib/jws/sign.js
CHANGED
|
@@ -57,7 +57,7 @@ class Sign {
|
|
|
57
57
|
/*
|
|
58
58
|
* @private
|
|
59
59
|
*/
|
|
60
|
-
[PROCESS_RECIPIENT] (recipient) {
|
|
60
|
+
[PROCESS_RECIPIENT] (recipient, first) {
|
|
61
61
|
const { key, protectedHeader, unprotectedHeader } = recipient
|
|
62
62
|
|
|
63
63
|
if (key.use === 'enc') {
|
|
@@ -87,32 +87,21 @@ class Sign {
|
|
|
87
87
|
recipient.header = unprotectedHeader
|
|
88
88
|
recipient.protected = Object.keys(joseHeader.protected).length ? base64url.JSON.encode(joseHeader.protected) : ''
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
throw new JWSInvalid('the "b64" Header Parameter value MUST be the same for all recipients')
|
|
90
|
+
if (first && joseHeader.protected.crit && joseHeader.protected.crit.includes('b64') && joseHeader.protected.b64 === false) {
|
|
91
|
+
if (this._binary) {
|
|
92
|
+
this._payload = base64url.decodeToBuffer(this._payload)
|
|
94
93
|
} else {
|
|
95
|
-
this.
|
|
94
|
+
this._payload = base64url.decode(this._payload)
|
|
96
95
|
}
|
|
97
|
-
|
|
98
|
-
if (!joseHeader.protected.b64) {
|
|
99
|
-
if (this._binary) {
|
|
100
|
-
this._payload = base64url.decodeToBuffer(this._payload)
|
|
101
|
-
} else {
|
|
102
|
-
this._payload = base64url.decode(this._payload)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
toBeSigned = Buffer.concat([
|
|
107
|
-
Buffer.from(recipient.protected || ''),
|
|
108
|
-
Buffer.from('.'),
|
|
109
|
-
Buffer.isBuffer(this._payload) ? this._payload : Buffer.from(this._payload)
|
|
110
|
-
])
|
|
111
|
-
} else {
|
|
112
|
-
toBeSigned = `${recipient.protected || ''}.${this._payload}`
|
|
113
96
|
}
|
|
114
97
|
|
|
115
|
-
|
|
98
|
+
const data = Buffer.concat([
|
|
99
|
+
Buffer.from(recipient.protected || ''),
|
|
100
|
+
Buffer.from('.'),
|
|
101
|
+
Buffer.from(this._payload)
|
|
102
|
+
])
|
|
103
|
+
|
|
104
|
+
recipient.signature = base64url.encodeBuffer(sign(alg, key, data))
|
|
116
105
|
}
|
|
117
106
|
|
|
118
107
|
/*
|
|
@@ -130,9 +119,9 @@ class Sign {
|
|
|
130
119
|
|
|
131
120
|
serializer.validate(this, this._recipients)
|
|
132
121
|
|
|
133
|
-
|
|
134
|
-
this[PROCESS_RECIPIENT](recipient)
|
|
135
|
-
}
|
|
122
|
+
this._recipients.forEach((recipient, i) => {
|
|
123
|
+
this[PROCESS_RECIPIENT](recipient, i === 0)
|
|
124
|
+
})
|
|
136
125
|
|
|
137
126
|
return serializer(this._payload, this._recipients)
|
|
138
127
|
}
|
package/lib/jwt/verify.js
CHANGED
|
@@ -63,12 +63,15 @@ const validateOptions = ({
|
|
|
63
63
|
|
|
64
64
|
isOptionString(maxTokenAge, 'options.maxTokenAge')
|
|
65
65
|
isOptionString(subject, 'options.subject')
|
|
66
|
-
isOptionString(issuer, 'options.issuer')
|
|
67
66
|
isOptionString(maxAuthAge, 'options.maxAuthAge')
|
|
68
67
|
isOptionString(jti, 'options.jti')
|
|
69
68
|
isOptionString(clockTolerance, 'options.clockTolerance')
|
|
70
69
|
isOptionString(typ, 'options.typ')
|
|
71
70
|
|
|
71
|
+
if (issuer !== undefined && (isNotString(issuer) && isNotArrayOfStrings(issuer))) {
|
|
72
|
+
throw new TypeError('options.issuer must be a string or an array of strings')
|
|
73
|
+
}
|
|
74
|
+
|
|
72
75
|
if (audience !== undefined && (isNotString(audience) && isNotArrayOfStrings(audience))) {
|
|
73
76
|
throw new TypeError('options.audience must be a string or an array of strings')
|
|
74
77
|
}
|
|
@@ -161,7 +164,7 @@ const validateTypes = ({ header, payload }, profile, options) => {
|
|
|
161
164
|
isPayloadString(payload.jti, '"jti" claim', 'jti', profile === LOGOUTTOKEN || profile === ATJWT || !!options.jti)
|
|
162
165
|
isPayloadString(payload.acr, '"acr" claim', 'acr')
|
|
163
166
|
isPayloadString(payload.nonce, '"nonce" claim', 'nonce', !!options.nonce)
|
|
164
|
-
|
|
167
|
+
isStringOrArrayOfStrings(payload.iss, 'iss', !!options.issuer)
|
|
165
168
|
isPayloadString(payload.sub, '"sub" claim', 'sub', profile === IDTOKEN || profile === ATJWT || !!options.subject)
|
|
166
169
|
isStringOrArrayOfStrings(payload.aud, 'aud', !!options.audience)
|
|
167
170
|
isPayloadString(payload.azp, '"azp" claim', 'azp', profile === IDTOKEN && Array.isArray(payload.aud) && payload.aud.length > 1)
|
|
@@ -235,7 +238,7 @@ module.exports = (token, key, options = {}) => {
|
|
|
235
238
|
const unix = epoch(now)
|
|
236
239
|
validateTypes(decoded, profile, options)
|
|
237
240
|
|
|
238
|
-
if (issuer && decoded.payload.iss !== issuer) {
|
|
241
|
+
if (issuer && (typeof decoded.payload.iss !== 'string' || !(typeof issuer === 'string' ? [issuer] : issuer).includes(decoded.payload.iss))) {
|
|
239
242
|
throw new JWTClaimInvalid('unexpected "iss" claim value', 'iss', 'check_failed')
|
|
240
243
|
}
|
|
241
244
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jose",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.28.1",
|
|
4
4
|
"description": "JSON Web Almost Everything - JWA, JWS, JWE, JWK, JWT, JWKS for Node.js with minimal dependencies",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"access token",
|
|
@@ -81,11 +81,11 @@
|
|
|
81
81
|
"@panva/asn1.js": "^1.0.0"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
|
-
"@commitlint/cli": "^
|
|
85
|
-
"@commitlint/config-conventional": "^
|
|
84
|
+
"@commitlint/cli": "^9.1.1",
|
|
85
|
+
"@commitlint/config-conventional": "^9.1.1",
|
|
86
86
|
"ava": "^2.4.0",
|
|
87
87
|
"babel-eslint": "^10.0.3",
|
|
88
|
-
"c8": "^7.
|
|
88
|
+
"c8": "^7.2.1",
|
|
89
89
|
"husky": "^4.0.0",
|
|
90
90
|
"standard": "^14.3.1"
|
|
91
91
|
},
|
package/types/index.d.ts
CHANGED
|
@@ -444,7 +444,7 @@ export namespace JWT {
|
|
|
444
444
|
ignoreIat?: boolean;
|
|
445
445
|
maxTokenAge?: string;
|
|
446
446
|
subject?: string;
|
|
447
|
-
issuer?: string;
|
|
447
|
+
issuer?: string | string[];
|
|
448
448
|
maxAuthAge?: string;
|
|
449
449
|
jti?: string;
|
|
450
450
|
clockTolerance?: string;
|
|
@@ -479,7 +479,7 @@ export namespace JWT {
|
|
|
479
479
|
function sign(payload: object, key: ProduceKeyInputWithNone, options?: SignOptions): string;
|
|
480
480
|
|
|
481
481
|
interface VerifyProfileOptions<profile> {
|
|
482
|
-
issuer: string;
|
|
482
|
+
issuer: string | string[];
|
|
483
483
|
audience: string | string[];
|
|
484
484
|
profile?: profile;
|
|
485
485
|
}
|