@atproto/jwk-jose 0.2.2 → 0.2.3

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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # @atproto/jwk-jose
2
2
 
3
+ ## 0.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#5099](https://github.com/bluesky-social/atproto/pull/5099) [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Update TypeScript build to rely on references to composite internal projects
8
+
9
+ - [#5099](https://github.com/bluesky-social/atproto/pull/5099) [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Bundle only necessary files in the NPM tarball, including the `CHANGELOG.md` and `README.md` files (if present).
10
+
11
+ - Updated dependencies [[`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07), [`b43ec31`](https://github.com/bluesky-social/atproto/commit/b43ec31f247f4461725b01226885f88bd430ca07)]:
12
+ - @atproto/jwk@0.7.3
13
+
3
14
  ## 0.2.2
4
15
 
5
16
  ### Patch Changes
package/package.json CHANGED
@@ -1,9 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/jwk-jose",
3
- "version": "0.2.2",
4
- "engines": {
5
- "node": ">=22"
6
- },
3
+ "version": "0.2.3",
7
4
  "license": "MIT",
8
5
  "description": "`jose` based implementation of @atproto/jwk Key's",
9
6
  "keywords": [
@@ -17,6 +14,10 @@
17
14
  "url": "https://github.com/bluesky-social/atproto",
18
15
  "directory": "packages/oauth/jwk-jose"
19
16
  },
17
+ "files": [
18
+ "./dist",
19
+ "./CHANGELOG.md"
20
+ ],
20
21
  "type": "module",
21
22
  "exports": {
22
23
  ".": {
@@ -24,11 +25,13 @@
24
25
  "default": "./dist/index.js"
25
26
  }
26
27
  },
28
+ "engines": {
29
+ "node": ">=22"
30
+ },
27
31
  "dependencies": {
28
32
  "jose": "^5.2.0",
29
- "@atproto/jwk": "^0.7.2"
33
+ "@atproto/jwk": "^0.7.3"
30
34
  },
31
- "devDependencies": {},
32
35
  "scripts": {
33
36
  "build": "tsgo --build tsconfig.build.json"
34
37
  }
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './jose-key.js'
package/src/jose-key.ts DELETED
@@ -1,255 +0,0 @@
1
- import {
2
- type GenerateKeyPairOptions,
3
- type GenerateKeyPairResult,
4
- type JWK,
5
- type JWTVerifyOptions,
6
- type KeyLike,
7
- SignJWT,
8
- errors,
9
- exportJWK,
10
- generateKeyPair,
11
- importJWK,
12
- importPKCS8,
13
- jwtVerify,
14
- } from 'jose'
15
- import {
16
- Jwk,
17
- JwkError,
18
- JwtCreateError,
19
- JwtHeader,
20
- JwtPayload,
21
- JwtVerifyError,
22
- Key,
23
- SignedJwt,
24
- VerifyOptions,
25
- VerifyResult,
26
- isPrivateJwk,
27
- jwkSchema,
28
- jwtHeaderSchema,
29
- jwtPayloadSchema,
30
- } from '@atproto/jwk'
31
- import { RequiredKey, either } from './util.js'
32
-
33
- const { JOSEError } = errors
34
-
35
- export {
36
- type GenerateKeyPairOptions,
37
- type GenerateKeyPairResult,
38
- type Jwk,
39
- type JwtHeader,
40
- type JwtPayload,
41
- type KeyLike,
42
- type SignedJwt,
43
- type VerifyOptions,
44
- }
45
-
46
- export class JoseKey<J extends Jwk = Jwk> extends Key<J> {
47
- /**
48
- * Some runtimes (e.g. Bun) require an `alg` second argument to be set when
49
- * invoking `importJWK`. In order to be compatible with these runtimes, we
50
- * provide the following method to ensure the `alg` is always set. We also
51
- * take the opportunity to ensure that the `alg` is compatible with this key.
52
- */
53
- protected async getKeyObj(alg: string) {
54
- if (!this.algorithms.includes(alg)) {
55
- throw new JwkError(`Key cannot be used with algorithm "${alg}"`)
56
- }
57
- try {
58
- return await importJWK(this.jwk as JWK, alg)
59
- } catch (cause) {
60
- throw new JwkError('Failed to import JWK', undefined, { cause })
61
- }
62
- }
63
-
64
- async createJwt(header: JwtHeader, payload: JwtPayload): Promise<SignedJwt> {
65
- try {
66
- const { kid } = header
67
- if (kid && kid !== this.kid) {
68
- throw new JwtCreateError(
69
- `Invalid "kid" (${kid}) used to sign with key "${this.kid}"`,
70
- )
71
- }
72
-
73
- const { alg } = header
74
- if (!alg) {
75
- throw new JwtCreateError('Missing "alg" in JWT header')
76
- }
77
-
78
- const keyObj = await this.getKeyObj(alg)
79
- const jwtBuilder = new SignJWT(payload).setProtectedHeader({
80
- ...header,
81
- alg,
82
- kid: this.kid,
83
- })
84
-
85
- const signedJwt = await jwtBuilder.sign(keyObj)
86
-
87
- return signedJwt as SignedJwt
88
- } catch (cause) {
89
- if (cause instanceof JOSEError) {
90
- throw new JwtCreateError(cause.message, cause.code, { cause })
91
- } else {
92
- throw JwtCreateError.from(cause)
93
- }
94
- }
95
- }
96
-
97
- async verifyJwt<C extends string = never>(
98
- token: SignedJwt,
99
- options?: VerifyOptions<C>,
100
- ): Promise<VerifyResult<C>> {
101
- try {
102
- const result = await jwtVerify(
103
- token,
104
- async ({ alg }) => this.getKeyObj(alg),
105
- { ...options, algorithms: this.algorithms } as JWTVerifyOptions,
106
- )
107
-
108
- // @NOTE if all tokens are signed exclusively through createJwt(), then
109
- // there should be no need to parse the payload and headers here. But
110
- // since the JWT could have been signed with the same key from somewhere
111
- // else, let's parse it to ensure the integrity (and type safety) of the
112
- // data.
113
- const headerParsed = jwtHeaderSchema.safeParse(result.protectedHeader)
114
- if (!headerParsed.success) {
115
- throw new JwtVerifyError('Invalid JWT header', undefined, {
116
- cause: headerParsed.error,
117
- })
118
- }
119
-
120
- const payloadParsed = jwtPayloadSchema.safeParse(result.payload)
121
- if (!payloadParsed.success) {
122
- throw new JwtVerifyError('Invalid JWT payload', undefined, {
123
- cause: payloadParsed.error,
124
- })
125
- }
126
-
127
- return {
128
- protectedHeader: headerParsed.data,
129
- // "requiredClaims" enforced by jwtVerify()
130
- payload: payloadParsed.data as RequiredKey<JwtPayload, C>,
131
- }
132
- } catch (cause) {
133
- if (cause instanceof JOSEError) {
134
- throw new JwtVerifyError(cause.message, cause.code, { cause })
135
- } else {
136
- throw JwtVerifyError.from(cause)
137
- }
138
- }
139
- }
140
-
141
- static async generateKeyPair(
142
- allowedAlgos: readonly string[] = ['ES256'],
143
- options?: GenerateKeyPairOptions,
144
- ) {
145
- if (!allowedAlgos.length) {
146
- throw new JwkError('No algorithms provided for key generation')
147
- }
148
-
149
- const errors: unknown[] = []
150
- for (const alg of allowedAlgos) {
151
- try {
152
- return await generateKeyPair(alg, options)
153
- } catch (err) {
154
- errors.push(err)
155
- }
156
- }
157
-
158
- throw new JwkError('Failed to generate key pair', undefined, {
159
- cause: new AggregateError(errors, 'None of the algorithms worked'),
160
- })
161
- }
162
-
163
- static async generate(
164
- allowedAlgos: string[] = ['ES256'],
165
- kid?: string,
166
- options?: Omit<GenerateKeyPairOptions, 'extractable'>,
167
- ): Promise<JoseKey> {
168
- const kp = await this.generateKeyPair(allowedAlgos, {
169
- ...options,
170
- extractable: true,
171
- })
172
- return this.fromKeyLike(kp.privateKey, kid)
173
- }
174
-
175
- static async fromImportable(
176
- input: string | KeyLike | Jwk,
177
- kid?: string,
178
- ): Promise<JoseKey> {
179
- if (typeof input === 'string') {
180
- // PKCS8
181
- if (input.startsWith('-----')) {
182
- // The "alg" is only needed in WebCrypto (NodeJS will be fine)
183
- return this.fromPKCS8(input, '', kid)
184
- }
185
-
186
- // Jwk (string)
187
- if (input.startsWith('{')) {
188
- return this.fromJWK(input, kid)
189
- }
190
-
191
- throw new JwkError('Invalid input')
192
- }
193
-
194
- if (typeof input === 'object') {
195
- // Jwk
196
- if ('kty' in input || 'alg' in input) {
197
- return this.fromJWK(input, kid)
198
- }
199
-
200
- // KeyLike
201
- return this.fromKeyLike(input, kid)
202
- }
203
-
204
- throw new JwkError('Invalid input')
205
- }
206
-
207
- /**
208
- * @see {@link exportJWK}
209
- */
210
- static async fromKeyLike(
211
- keyLike: KeyLike | Uint8Array,
212
- kid?: string,
213
- alg?: string,
214
- ): Promise<JoseKey> {
215
- const jwk = await exportJWK(keyLike)
216
- if (alg) {
217
- if (!jwk.alg) jwk.alg = alg
218
- else if (jwk.alg !== alg) throw new JwkError('Invalid "alg" in JWK')
219
- }
220
- return this.fromJWK(jwk, kid)
221
- }
222
-
223
- /**
224
- * @see {@link importPKCS8}
225
- */
226
- static async fromPKCS8(
227
- pem: string,
228
- alg: string,
229
- kid?: string,
230
- ): Promise<JoseKey> {
231
- const keyLike = await importPKCS8(pem, alg, { extractable: true })
232
- return this.fromKeyLike(keyLike, kid)
233
- }
234
-
235
- static async fromJWK(
236
- input: string | Record<string, unknown>,
237
- inputKid?: string,
238
- ): Promise<JoseKey> {
239
- const jwk = typeof input === 'string' ? JSON.parse(input) : input
240
- if (!jwk || typeof jwk !== 'object') throw new JwkError('Invalid JWK')
241
-
242
- const kid = either(jwk.kid, inputKid)
243
-
244
- // Backwards compatibility with old behavior
245
- if (jwk.use != null && isPrivateJwk(jwk)) {
246
- console.warn(
247
- 'Deprecation warning: Private JWK with a "use" property will be rejected in the future. Please remove replace "use" with (valid) "key_ops".',
248
- )
249
- jwk.key_ops ??= jwk.use === 'sig' ? ['sign'] : ['encrypt']
250
- delete jwk.use
251
- }
252
-
253
- return new JoseKey<Jwk>(jwkSchema.parse({ ...jwk, kid }))
254
- }
255
- }
package/src/util.ts DELETED
@@ -1,20 +0,0 @@
1
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
2
- export type Simplify<T> = { [K in keyof T]: T[K] } & {}
3
-
4
- export type RequiredKey<T, K extends keyof T = never> = Simplify<
5
- T & {
6
- [L in K]-?: unknown extends T[L]
7
- ? NonNullable<unknown> | null
8
- : Exclude<T[L], undefined>
9
- }
10
- >
11
-
12
- export function either<T extends string | number | boolean>(
13
- a?: T,
14
- b?: T,
15
- ): T | undefined {
16
- if (a != null && b != null && a !== b) {
17
- throw new TypeError(`Expected "${b}", got "${a}"`)
18
- }
19
- return a ?? b ?? undefined
20
- }
@@ -1,8 +0,0 @@
1
- {
2
- "extends": ["../../../tsconfig/nodenext.json"],
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "rootDir": "src",
6
- },
7
- "include": ["src"],
8
- }
@@ -1 +0,0 @@
1
- {"version":"7.0.0-dev.20260614.1","root":["./src/index.ts","./src/jose-key.ts","./src/util.ts"]}
package/tsconfig.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "include": [],
3
- "references": [{ "path": "./tsconfig.build.json" }],
4
- }