@nitrotool/jwt 0.0.8 → 1.0.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/README.md +115 -43
- package/dist/core/index.d.mts +11 -0
- package/dist/core/index.d.ts +11 -0
- package/dist/core/index.mjs +26 -0
- package/dist/{h3.d.mts → h3/index.d.mts} +4 -2
- package/dist/{h3.d.ts → h3/index.d.ts} +4 -2
- package/dist/{h3.mjs → h3/index.mjs} +9 -3
- package/dist/index.d.mts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.mjs +7 -4
- package/dist/module.d.mts +9 -0
- package/dist/module.d.ts +9 -0
- package/dist/module.mjs +72 -0
- package/dist/runtime/server/index.d.mts +16 -0
- package/dist/runtime/server/index.d.ts +16 -0
- package/dist/runtime/server/index.mjs +17 -0
- package/dist/runtime/server/nitro/plugin.d.mts +3 -0
- package/dist/runtime/server/nitro/plugin.d.ts +3 -0
- package/dist/runtime/server/nitro/plugin.mjs +10 -0
- package/dist/runtime/server/nitro/utils/index.d.mts +6 -0
- package/dist/runtime/server/nitro/utils/index.d.ts +6 -0
- package/dist/runtime/server/nitro/utils/index.mjs +8 -0
- package/dist/shared/jwt.Cd03UmUN.mjs +8 -0
- package/dist/shared/jwt.Cgq_6NMU.mjs +16 -0
- package/package.json +38 -7
- package/dist/jwt.d.mts +0 -16
- package/dist/jwt.d.ts +0 -16
- package/dist/jwt.mjs +0 -52
package/README.md
CHANGED
|
@@ -1,77 +1,149 @@
|
|
|
1
1
|
# @nitrotool/jwt
|
|
2
2
|
|
|
3
|
-
Lightweight JWT utilities for Nitro/UnJS environments with
|
|
3
|
+
Lightweight JWT utilities for Nuxt/Nitro/UnJS environments **with built-in h3 helpers**.
|
|
4
4
|
|
|
5
5
|
- Built on `@tsndr/cloudflare-worker-jwt`
|
|
6
6
|
- Helpers that read `jwtSecret` from your Nitro runtime config
|
|
7
|
-
- h3 utilities to extract tokens from requests and enforce authentication
|
|
7
|
+
- Batteries included with h3 utilities to extract tokens from requests and enforce authentication
|
|
8
|
+
|
|
9
|
+
Learn more about JWT and how it works:
|
|
10
|
+
|
|
11
|
+
- [What is a JWT?](https://www.jwt.io/introduction#what-is-json-web-token)
|
|
12
|
+
- [JWT.io](https://jwt.io/)
|
|
8
13
|
|
|
9
14
|
## Installation
|
|
10
15
|
|
|
16
|
+
```bash
|
|
17
|
+
pnpm install @nitrotool/jwt
|
|
18
|
+
```
|
|
19
|
+
|
|
11
20
|
```bash
|
|
12
21
|
npm install @nitrotool/jwt
|
|
13
22
|
```
|
|
14
23
|
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
Required peer dependency:
|
|
25
|
+
|
|
26
|
+
- `h3` (this package expects `h3` to be present)
|
|
27
|
+
|
|
28
|
+
## Nuxt setup
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
export default defineNuxtConfig({
|
|
32
|
+
modules: ['@nitrotool/jwt'],
|
|
33
|
+
//You can also
|
|
34
|
+
jwt: {
|
|
35
|
+
//default secret (only use this locally, and override it in prod)
|
|
36
|
+
secret: 'some-random-secret-yes-it-is-not-very-secure',
|
|
37
|
+
// Alternatively also available as NUXT_JWT_SECRET env var
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Usage with Nuxt 3/4
|
|
44
|
+
|
|
45
|
+
> This is available all places where auto-imported can be used on the server-side.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
type MyClaims = { tenantId: string };
|
|
49
|
+
|
|
50
|
+
const token = await encodeJwt<MyClaims>({tenantId: '123', sub: '123'});
|
|
51
|
+
|
|
52
|
+
// Later…
|
|
53
|
+
const isValid = await verifyJwt(token);
|
|
54
|
+
const payload = await decodeJwt<MyClaims>(token);
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
// or
|
|
58
|
+
export default defineEventHandler(async (event) => {
|
|
59
|
+
const jwt = await requireJwt(event);
|
|
60
|
+
// you now have access to a required JWT token via Authorization bearer header.
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
```
|
|
17
64
|
|
|
18
65
|
## Importing
|
|
19
66
|
|
|
20
|
-
|
|
67
|
+
Import from the main entry or subpaths:
|
|
21
68
|
|
|
22
69
|
```ts
|
|
23
|
-
//
|
|
24
|
-
import {
|
|
70
|
+
// Node-only helpers
|
|
71
|
+
import {encodeJwtRaw, verifyJwtRaw, decodeJwtRaw} from '@nitrotool/jwt/core';
|
|
72
|
+
|
|
73
|
+
// h3-only helpers
|
|
74
|
+
import {extractApiToken, requireApiToken} from '@nitrotool/jwt/h3';
|
|
75
|
+
```
|
|
25
76
|
|
|
26
|
-
|
|
27
|
-
|
|
77
|
+
## Quick Start Nitro
|
|
78
|
+
|
|
79
|
+
### 1. Create plugin to load secret.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// plugins/0.jwt.ts
|
|
83
|
+
import { defineNitroPlugin, useRuntimeConfig } from 'nitropack/runtime';
|
|
84
|
+
import { configureJwtRuntime } from '@nitrotool/jwt';
|
|
85
|
+
|
|
86
|
+
export default defineNitroPlugin(() => {
|
|
87
|
+
configureJwtRuntime(() => useRuntimeConfig() as any);
|
|
88
|
+
});
|
|
28
89
|
|
|
29
|
-
// Subpath (h3 helpers)
|
|
30
|
-
import { extractApiToken, requireApiToken } from '@nitrotool/jwt/h3';
|
|
31
90
|
```
|
|
32
91
|
|
|
33
|
-
|
|
92
|
+
### 2. Basic usage
|
|
34
93
|
|
|
35
94
|
```ts
|
|
36
|
-
import {
|
|
95
|
+
import {encodeJwt, verifyJwt, decodeJwt} from '@nitrotool/jwt';
|
|
37
96
|
|
|
38
|
-
type MyClaims = {
|
|
97
|
+
type MyClaims = { tenantId: string };
|
|
98
|
+
|
|
99
|
+
const token = await encodeJwt<MyClaims>({tenantId: '123', sub: '123'});
|
|
39
100
|
|
|
40
|
-
const token = await encodeJwt<MyClaims>({ userId: '123' });
|
|
41
101
|
// Later…
|
|
42
102
|
const isValid = await verifyJwt(token);
|
|
43
|
-
const payload = await decodeJwt<MyClaims>(token); // {
|
|
103
|
+
const payload = await decodeJwt<MyClaims>(token); // { sub: '123', tenantId: '123', exp: ... }
|
|
44
104
|
```
|
|
45
105
|
|
|
46
106
|
By default, non-`Raw` helpers read the secret from your runtime config:
|
|
107
|
+
|
|
47
108
|
- `useRuntimeConfig().jwtSecret`
|
|
48
109
|
|
|
49
110
|
If you need to pass a secret explicitly, use the `*Raw` variants.
|
|
50
111
|
|
|
51
|
-
|
|
112
|
+
### 3. In a route handler
|
|
52
113
|
|
|
53
114
|
Extract API tokens and enforce authentication in request handlers:
|
|
54
115
|
|
|
116
|
+
#### Require a JWT token
|
|
117
|
+
```ts
|
|
118
|
+
import {defineEventHandler} from 'h3';
|
|
119
|
+
import {requireJwt} from '@nitrotool/jwt';
|
|
120
|
+
|
|
121
|
+
export default defineEventHandler(async (event) => {
|
|
122
|
+
const decodedJwt = await requireJwt(event)
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### Extract a JWT token manually
|
|
55
127
|
```ts
|
|
56
128
|
import { defineEventHandler } from 'h3';
|
|
57
|
-
import { extractApiToken, requireApiToken } from '@nitrotool/jwt
|
|
58
|
-
import { decodeJwt } from '@nitrotool/jwt';
|
|
129
|
+
import { extractApiToken, requireApiToken, decodeJwt } from '@nitrotool/jwt';
|
|
59
130
|
|
|
60
131
|
export default defineEventHandler(async (event) => {
|
|
61
|
-
|
|
62
|
-
|
|
132
|
+
// Try to read token (Authorization: Bearer <token> or ?<queryKey>=<token>)
|
|
133
|
+
const token = extractApiToken(event, {queryKey: 'token'}); // queryKey defaults to 'token'
|
|
63
134
|
|
|
64
|
-
|
|
65
|
-
|
|
135
|
+
// Or strictly require it (throws UnauthenticatedError if missing)
|
|
136
|
+
const requiredToken = requireApiToken(event);
|
|
66
137
|
|
|
67
|
-
|
|
68
|
-
|
|
138
|
+
// Optionally decode/verify
|
|
139
|
+
const claims = await decodeJwt<{ userId: string }>(requiredToken);
|
|
69
140
|
|
|
70
|
-
|
|
141
|
+
return { ok: true, userId: claims.userId };
|
|
71
142
|
});
|
|
72
143
|
```
|
|
73
144
|
|
|
74
145
|
Supported token locations:
|
|
146
|
+
|
|
75
147
|
- Authorization header: `Authorization: Bearer <token>`
|
|
76
148
|
- Query string: `?token=<token>`
|
|
77
149
|
|
|
@@ -80,11 +152,11 @@ Supported token locations:
|
|
|
80
152
|
When using non-`Raw` helpers, ensure a secret is available at runtime:
|
|
81
153
|
|
|
82
154
|
```ts
|
|
83
|
-
//
|
|
155
|
+
// nuxt.config.ts (Nuxt/Nitro runtime config)
|
|
84
156
|
export default defineNuxtConfig({
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
157
|
+
runtimeConfig: {
|
|
158
|
+
jwtSecret: 'some-random-secret-yes-it-is-not-very-secure',
|
|
159
|
+
},
|
|
88
160
|
});
|
|
89
161
|
```
|
|
90
162
|
|
|
@@ -93,27 +165,27 @@ export default defineNuxtConfig({
|
|
|
93
165
|
Sign with custom TTL:
|
|
94
166
|
|
|
95
167
|
```ts
|
|
96
|
-
import {
|
|
168
|
+
import {encodeJwtRaw} from '@nitrotool/jwt';
|
|
97
169
|
|
|
98
170
|
const token = await encodeJwtRaw(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
171
|
+
{userId: '123', role: 'admin'},
|
|
172
|
+
process.env.JWT_SECRET!,
|
|
173
|
+
60 * 10 // 10 minutes
|
|
102
174
|
);
|
|
103
175
|
```
|
|
104
176
|
|
|
105
177
|
Decode without verifying signature (use only for non-sensitive scenarios):
|
|
106
178
|
|
|
107
179
|
```ts
|
|
108
|
-
import {
|
|
180
|
+
import {decodeJwt} from '@nitrotool/jwt';
|
|
109
181
|
|
|
110
|
-
const payload = await decodeJwt<{ userId: string }>(token, {
|
|
182
|
+
const payload = await decodeJwt<{ userId: string }>(token, {verify: false});
|
|
111
183
|
```
|
|
112
184
|
|
|
113
185
|
Verify with an explicit secret:
|
|
114
186
|
|
|
115
187
|
```ts
|
|
116
|
-
import {
|
|
188
|
+
import {verifyJwtRaw} from '@nitrotool/jwt';
|
|
117
189
|
|
|
118
190
|
const ok = await verifyJwtRaw(token, process.env.JWT_SECRET!);
|
|
119
191
|
```
|
|
@@ -139,9 +211,9 @@ All helpers are asynchronous.
|
|
|
139
211
|
- `encodeJwtRaw<T>(payload, secret, ttl = 60): Promise<string>`
|
|
140
212
|
- Signs a token with the provided `secret`.
|
|
141
213
|
- `ttl` is in seconds. Default: `60`.
|
|
142
|
-
- `exp` is set
|
|
214
|
+
- `exp` is set **automagically** from `ttl`.
|
|
143
215
|
|
|
144
|
-
- `encodeJwt<T>(payload): Promise<string>`
|
|
216
|
+
- `encodeJwt<T>(payload, ttl = 60): Promise<string>`
|
|
145
217
|
- Same as `encodeJwtRaw`, but uses `useRuntimeConfig().jwtSecret`.
|
|
146
218
|
|
|
147
219
|
- `verifyJwtRaw(token, secret): Promise<boolean>`
|
|
@@ -160,14 +232,15 @@ All helpers are asynchronous.
|
|
|
160
232
|
- Throws `UnauthorizedError('Invalid JWT token.')` if verification fails.
|
|
161
233
|
|
|
162
234
|
Types:
|
|
235
|
+
|
|
163
236
|
- `ExtendableJwtPayload<T>` lets you define custom claims merged with standard JWT claims.
|
|
164
237
|
|
|
165
|
-
### h3 helpers
|
|
238
|
+
### h3 helpers (required)
|
|
166
239
|
|
|
167
240
|
- `extractBearerToken(event): string | undefined`
|
|
168
241
|
- Reads `Authorization` header and returns the token without `Bearer `.
|
|
169
242
|
|
|
170
|
-
- `extractQueryToken(event): string | undefined`
|
|
243
|
+
- `extractQueryToken(event, key = 'token'): string | undefined`
|
|
171
244
|
- Reads `token` from the query string.
|
|
172
245
|
|
|
173
246
|
- `extractApiToken(event): string | undefined`
|
|
@@ -176,7 +249,6 @@ Types:
|
|
|
176
249
|
- `requireApiToken(event): string`
|
|
177
250
|
- Same as `extractApiToken`, but throws `UnauthenticatedError` if missing.
|
|
178
251
|
|
|
179
|
-
|
|
180
252
|
## License
|
|
181
253
|
|
|
182
|
-
MIT
|
|
254
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { JwtPayload } from '@tsndr/cloudflare-worker-jwt';
|
|
2
|
+
|
|
3
|
+
type ExtendableJwtPayload<T extends Record<string, any> = {}> = Partial<JwtPayload> & T;
|
|
4
|
+
declare const encodeJwtRaw: <T extends Record<string, any> = {}>(payload: ExtendableJwtPayload<T>, secret: string, ttlSec?: number) => Promise<string>;
|
|
5
|
+
declare const verifyJwtRaw: (token: string, secret: string) => Promise<boolean>;
|
|
6
|
+
declare const decodeJwtRaw: <T extends Record<string, any> = {}>(token: string, secret: string, opts?: {
|
|
7
|
+
verify?: boolean;
|
|
8
|
+
}) => Promise<ExtendableJwtPayload<T>>;
|
|
9
|
+
|
|
10
|
+
export { decodeJwtRaw, encodeJwtRaw, verifyJwtRaw };
|
|
11
|
+
export type { ExtendableJwtPayload };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { JwtPayload } from '@tsndr/cloudflare-worker-jwt';
|
|
2
|
+
|
|
3
|
+
type ExtendableJwtPayload<T extends Record<string, any> = {}> = Partial<JwtPayload> & T;
|
|
4
|
+
declare const encodeJwtRaw: <T extends Record<string, any> = {}>(payload: ExtendableJwtPayload<T>, secret: string, ttlSec?: number) => Promise<string>;
|
|
5
|
+
declare const verifyJwtRaw: (token: string, secret: string) => Promise<boolean>;
|
|
6
|
+
declare const decodeJwtRaw: <T extends Record<string, any> = {}>(token: string, secret: string, opts?: {
|
|
7
|
+
verify?: boolean;
|
|
8
|
+
}) => Promise<ExtendableJwtPayload<T>>;
|
|
9
|
+
|
|
10
|
+
export { decodeJwtRaw, encodeJwtRaw, verifyJwtRaw };
|
|
11
|
+
export type { ExtendableJwtPayload };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import jwt from '@tsndr/cloudflare-worker-jwt';
|
|
2
|
+
import { UnauthorizedError } from '@nitrotool/errors';
|
|
3
|
+
|
|
4
|
+
const encodeJwtRaw = async (payload, secret, ttlSec = 60) => {
|
|
5
|
+
return jwt.sign(
|
|
6
|
+
{ exp: Math.floor(Date.now() / 1e3) + ttlSec, ...payload },
|
|
7
|
+
secret
|
|
8
|
+
);
|
|
9
|
+
};
|
|
10
|
+
const verifyJwtRaw = async (token, secret) => {
|
|
11
|
+
return new Promise(async (resolve) => {
|
|
12
|
+
if (await jwt.verify(token, secret, { throwError: false })) {
|
|
13
|
+
return resolve(true);
|
|
14
|
+
}
|
|
15
|
+
return resolve(false);
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
const decodeJwtRaw = async (token, secret, opts = { verify: true }) => {
|
|
19
|
+
if (opts.verify) {
|
|
20
|
+
const ok = await verifyJwtRaw(token, secret);
|
|
21
|
+
if (!ok) throw UnauthorizedError("Invalid JWT token.");
|
|
22
|
+
}
|
|
23
|
+
return jwt.decode(token).payload;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export { decodeJwtRaw, encodeJwtRaw, verifyJwtRaw };
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { H3Event } from 'h3';
|
|
2
2
|
|
|
3
|
-
declare const extractBearerToken: (event: H3Event) => string;
|
|
3
|
+
declare const extractBearerToken: (event: H3Event) => string | undefined;
|
|
4
4
|
declare const extractQueryToken: (event: H3Event, key?: string) => string | undefined;
|
|
5
|
-
declare const extractApiToken: (event: H3Event
|
|
5
|
+
declare const extractApiToken: (event: H3Event, opts?: {
|
|
6
|
+
queryKey: string;
|
|
7
|
+
}) => string | undefined;
|
|
6
8
|
declare const requireApiToken: (event: H3Event) => string;
|
|
7
9
|
|
|
8
10
|
export { extractApiToken, extractBearerToken, extractQueryToken, requireApiToken };
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { H3Event } from 'h3';
|
|
2
2
|
|
|
3
|
-
declare const extractBearerToken: (event: H3Event) => string;
|
|
3
|
+
declare const extractBearerToken: (event: H3Event) => string | undefined;
|
|
4
4
|
declare const extractQueryToken: (event: H3Event, key?: string) => string | undefined;
|
|
5
|
-
declare const extractApiToken: (event: H3Event
|
|
5
|
+
declare const extractApiToken: (event: H3Event, opts?: {
|
|
6
|
+
queryKey: string;
|
|
7
|
+
}) => string | undefined;
|
|
6
8
|
declare const requireApiToken: (event: H3Event) => string;
|
|
7
9
|
|
|
8
10
|
export { extractApiToken, extractBearerToken, extractQueryToken, requireApiToken };
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getQuery, getHeader } from 'h3';
|
|
2
2
|
import { UnauthenticatedError } from '@nitrotool/errors';
|
|
3
3
|
|
|
4
|
-
const extractBearerToken = (event) =>
|
|
4
|
+
const extractBearerToken = (event) => {
|
|
5
|
+
const auth = getHeader(event, "authorization");
|
|
6
|
+
if (!auth?.startsWith("Bearer ")) {
|
|
7
|
+
return void 0;
|
|
8
|
+
}
|
|
9
|
+
return auth.slice(7);
|
|
10
|
+
};
|
|
5
11
|
const extractQueryToken = (event, key = "token") => getQuery(event)?.[key] || void 0;
|
|
6
|
-
const extractApiToken = (event) => extractBearerToken(event) || extractQueryToken(event);
|
|
12
|
+
const extractApiToken = (event, opts = { queryKey: "token" }) => extractBearerToken(event) || extractQueryToken(event, opts.queryKey);
|
|
7
13
|
const requireApiToken = (event) => {
|
|
8
14
|
const token = extractApiToken(event);
|
|
9
15
|
if (!token) {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
import 'h3';
|
|
1
|
+
export { configureJwtRuntime, decodeJwt, encodeJwt, getJwtSecret, verifyJwt } from './runtime/server/index.mjs';
|
|
2
|
+
export { requireApiToken } from './h3/index.mjs';
|
|
3
|
+
import { H3Event } from 'h3';
|
|
4
|
+
import './core/index.mjs';
|
|
4
5
|
import '@tsndr/cloudflare-worker-jwt';
|
|
6
|
+
|
|
7
|
+
declare const requireJwt: <T extends Record<string, any> = {}>(event: H3Event) => Promise<any>;
|
|
8
|
+
|
|
9
|
+
export { requireJwt };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
import 'h3';
|
|
1
|
+
export { configureJwtRuntime, decodeJwt, encodeJwt, getJwtSecret, verifyJwt } from './runtime/server/index.js';
|
|
2
|
+
export { requireApiToken } from './h3/index.js';
|
|
3
|
+
import { H3Event } from 'h3';
|
|
4
|
+
import './core/index.js';
|
|
4
5
|
import '@tsndr/cloudflare-worker-jwt';
|
|
6
|
+
|
|
7
|
+
declare const requireJwt: <T extends Record<string, any> = {}>(event: H3Event) => Promise<any>;
|
|
8
|
+
|
|
9
|
+
export { requireJwt };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { decodeJwt,
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export { r as requireJwt } from './shared/jwt.Cd03UmUN.mjs';
|
|
2
|
+
export { decodeJwt, encodeJwt, verifyJwt } from './runtime/server/index.mjs';
|
|
3
|
+
export { requireApiToken } from './h3/index.mjs';
|
|
4
|
+
export { c as configureJwtRuntime, g as getJwtSecret } from './shared/jwt.Cgq_6NMU.mjs';
|
|
5
|
+
import './core/index.mjs';
|
|
5
6
|
import '@tsndr/cloudflare-worker-jwt';
|
|
7
|
+
import '@nitrotool/errors';
|
|
8
|
+
import 'h3';
|
package/dist/module.d.ts
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { defineNuxtModule, createResolver, addServerScanDir, addServerPlugin, addServerTemplate } from '@nuxt/kit';
|
|
3
|
+
import { consola } from 'consola';
|
|
4
|
+
import { join } from 'pathe';
|
|
5
|
+
|
|
6
|
+
const version = "1.0.1";
|
|
7
|
+
|
|
8
|
+
async function getFilesInDirectory(dir) {
|
|
9
|
+
const files = await fs.promises.readdir(dir);
|
|
10
|
+
const filePaths = [];
|
|
11
|
+
for (const file of files) {
|
|
12
|
+
const fullPath = join(dir, file);
|
|
13
|
+
const stat = await fs.promises.stat(fullPath);
|
|
14
|
+
if (stat.isFile()) {
|
|
15
|
+
filePaths.push(fullPath);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return filePaths;
|
|
19
|
+
}
|
|
20
|
+
async function getFilesInDirectoryRecursive(dir) {
|
|
21
|
+
const files = await fs.promises.readdir(dir);
|
|
22
|
+
const filePaths = [];
|
|
23
|
+
for (const file of files) {
|
|
24
|
+
const fullPath = join(dir, file);
|
|
25
|
+
const stat = await fs.promises.stat(fullPath);
|
|
26
|
+
if (stat.isFile()) {
|
|
27
|
+
filePaths.push(fullPath);
|
|
28
|
+
} else if (stat.isDirectory()) {
|
|
29
|
+
const subdirectoryFiles = await getFilesInDirectory(fullPath);
|
|
30
|
+
filePaths.push(...subdirectoryFiles);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return filePaths;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const module = defineNuxtModule({
|
|
37
|
+
meta: {
|
|
38
|
+
name: "@nitrotool/jwt",
|
|
39
|
+
configKey: "jwt",
|
|
40
|
+
version
|
|
41
|
+
},
|
|
42
|
+
async setup(options, nuxt) {
|
|
43
|
+
const logger = consola.withTag("jwt");
|
|
44
|
+
const resolver = createResolver(import.meta.url);
|
|
45
|
+
if (nuxt.options._prepare) {
|
|
46
|
+
logger.debug("Skipping module init due to nuxt.options._prepare = true");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (nuxt.options.runtimeConfig.jwtSecret) {
|
|
50
|
+
options.secret = nuxt.options.runtimeConfig.jwtSecret;
|
|
51
|
+
}
|
|
52
|
+
addServerScanDir([resolver.resolve("./runtime/server/nitro")]);
|
|
53
|
+
addServerPlugin(resolver.resolve("./runtime/server/nitro/plugin"));
|
|
54
|
+
const runtimeFiles = await getFilesInDirectoryRecursive(
|
|
55
|
+
resolver.resolve("./runtime/server")
|
|
56
|
+
);
|
|
57
|
+
for (const file of runtimeFiles) {
|
|
58
|
+
const relativeName = file.replace(
|
|
59
|
+
resolver.resolve("./runtime/server"),
|
|
60
|
+
""
|
|
61
|
+
);
|
|
62
|
+
logger.debug(`Adding server template: ${relativeName}`);
|
|
63
|
+
addServerTemplate({
|
|
64
|
+
filename: relativeName,
|
|
65
|
+
getContents: () => fs.readFileSync(file, "utf-8")
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
nuxt.options.runtimeConfig.jwtSecret = options.secret;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export { module as default };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ExtendableJwtPayload } from '../../core/index.mjs';
|
|
2
|
+
import '@tsndr/cloudflare-worker-jwt';
|
|
3
|
+
|
|
4
|
+
type JwtRuntimeConfig = {
|
|
5
|
+
jwtSecret?: string;
|
|
6
|
+
};
|
|
7
|
+
declare const configureJwtRuntime: (getter: () => JwtRuntimeConfig) => void;
|
|
8
|
+
declare const getJwtSecret: () => string;
|
|
9
|
+
|
|
10
|
+
declare const encodeJwt: <T extends Record<string, any> = {}>(payload: ExtendableJwtPayload<T>, ttlSec?: number) => Promise<any>;
|
|
11
|
+
declare const verifyJwt: (token: string) => Promise<any>;
|
|
12
|
+
declare const decodeJwt: <T extends Record<string, any> = {}>(token: string, opts?: {
|
|
13
|
+
verify?: boolean;
|
|
14
|
+
}) => Promise<any>;
|
|
15
|
+
|
|
16
|
+
export { ExtendableJwtPayload, configureJwtRuntime, decodeJwt, encodeJwt, getJwtSecret, verifyJwt };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ExtendableJwtPayload } from '../../core/index.js';
|
|
2
|
+
import '@tsndr/cloudflare-worker-jwt';
|
|
3
|
+
|
|
4
|
+
type JwtRuntimeConfig = {
|
|
5
|
+
jwtSecret?: string;
|
|
6
|
+
};
|
|
7
|
+
declare const configureJwtRuntime: (getter: () => JwtRuntimeConfig) => void;
|
|
8
|
+
declare const getJwtSecret: () => string;
|
|
9
|
+
|
|
10
|
+
declare const encodeJwt: <T extends Record<string, any> = {}>(payload: ExtendableJwtPayload<T>, ttlSec?: number) => Promise<any>;
|
|
11
|
+
declare const verifyJwt: (token: string) => Promise<any>;
|
|
12
|
+
declare const decodeJwt: <T extends Record<string, any> = {}>(token: string, opts?: {
|
|
13
|
+
verify?: boolean;
|
|
14
|
+
}) => Promise<any>;
|
|
15
|
+
|
|
16
|
+
export { ExtendableJwtPayload, configureJwtRuntime, decodeJwt, encodeJwt, getJwtSecret, verifyJwt };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { g as getJwtSecret } from '../../shared/jwt.Cgq_6NMU.mjs';
|
|
2
|
+
export { c as configureJwtRuntime } from '../../shared/jwt.Cgq_6NMU.mjs';
|
|
3
|
+
import { encodeJwtRaw, decodeJwtRaw, verifyJwtRaw } from '../../core/index.mjs';
|
|
4
|
+
import '@tsndr/cloudflare-worker-jwt';
|
|
5
|
+
import '@nitrotool/errors';
|
|
6
|
+
|
|
7
|
+
const encodeJwt = async (payload, ttlSec = 60) => {
|
|
8
|
+
return encodeJwtRaw(payload, getJwtSecret(), ttlSec);
|
|
9
|
+
};
|
|
10
|
+
const verifyJwt = async (token) => {
|
|
11
|
+
return verifyJwtRaw(token, getJwtSecret());
|
|
12
|
+
};
|
|
13
|
+
const decodeJwt = async (token, opts = { verify: true }) => {
|
|
14
|
+
return decodeJwtRaw(token, getJwtSecret(), opts);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export { decodeJwt, encodeJwt, getJwtSecret, verifyJwt };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineNitroPlugin, useRuntimeConfig } from 'nitropack/runtime';
|
|
2
|
+
import { c as configureJwtRuntime } from '../../../shared/jwt.Cgq_6NMU.mjs';
|
|
3
|
+
import '@tsndr/cloudflare-worker-jwt';
|
|
4
|
+
import '@nitrotool/errors';
|
|
5
|
+
|
|
6
|
+
const plugin = defineNitroPlugin(() => {
|
|
7
|
+
configureJwtRuntime(() => useRuntimeConfig());
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export { plugin as default };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { requireJwt } from '../../../../index.mjs';
|
|
2
|
+
export { configureJwtRuntime, decodeJwt, encodeJwt, getJwtSecret, verifyJwt } from '../../index.mjs';
|
|
3
|
+
export { requireApiToken } from '../../../../h3/index.mjs';
|
|
4
|
+
import 'h3';
|
|
5
|
+
import '../../../../core/index.mjs';
|
|
6
|
+
import '@tsndr/cloudflare-worker-jwt';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { requireJwt } from '../../../../index.js';
|
|
2
|
+
export { configureJwtRuntime, decodeJwt, encodeJwt, getJwtSecret, verifyJwt } from '../../index.js';
|
|
3
|
+
export { requireApiToken } from '../../../../h3/index.js';
|
|
4
|
+
import 'h3';
|
|
5
|
+
import '../../../../core/index.js';
|
|
6
|
+
import '@tsndr/cloudflare-worker-jwt';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { r as requireJwt } from '../../../../shared/jwt.Cd03UmUN.mjs';
|
|
2
|
+
export { decodeJwt, encodeJwt, verifyJwt } from '../../index.mjs';
|
|
3
|
+
export { requireApiToken } from '../../../../h3/index.mjs';
|
|
4
|
+
export { c as configureJwtRuntime, g as getJwtSecret } from '../../../../shared/jwt.Cgq_6NMU.mjs';
|
|
5
|
+
import '../../../../core/index.mjs';
|
|
6
|
+
import '@tsndr/cloudflare-worker-jwt';
|
|
7
|
+
import '@nitrotool/errors';
|
|
8
|
+
import 'h3';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
let getRuntimeConfig = () => ({});
|
|
2
|
+
const configureJwtRuntime = (getter) => {
|
|
3
|
+
getRuntimeConfig = getter;
|
|
4
|
+
};
|
|
5
|
+
const getJwtSecret = () => {
|
|
6
|
+
const cfg = getRuntimeConfig();
|
|
7
|
+
if (cfg?.jwtSecret) return cfg.jwtSecret;
|
|
8
|
+
if (process?.env?.NUXT_JWT_SECRET) return process?.env?.NUXT_JWT_SECRET;
|
|
9
|
+
if (process?.env?.NITRO_JWT_SECRET) return process?.env?.NITRO_JWT_SECRET;
|
|
10
|
+
if (process?.env?.JWT_SECRET) return process.env.JWT_SECRET;
|
|
11
|
+
throw new Error(
|
|
12
|
+
"jwtSecret is not configured. configure using NUXT_JWT_SECRET or jwt.secret"
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export { configureJwtRuntime as c, getJwtSecret as g };
|
package/package.json
CHANGED
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nitrotool/jwt",
|
|
3
|
-
"version": "
|
|
4
|
-
"main": "dist/index.mjs",
|
|
3
|
+
"version": "1.0.1",
|
|
5
4
|
"type": "module",
|
|
5
|
+
"main": "dist/index.mjs",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
6
7
|
"exports": {
|
|
7
|
-
".":
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.mjs"
|
|
11
|
+
},
|
|
12
|
+
"./module": {
|
|
13
|
+
"types": "./dist/module.d.ts",
|
|
14
|
+
"import": "./dist/module.mjs"
|
|
15
|
+
},
|
|
16
|
+
"./core": {
|
|
17
|
+
"types": "./dist/core/index.d.ts",
|
|
18
|
+
"import": "./dist/core/index.mjs"
|
|
19
|
+
},
|
|
20
|
+
"./h3": {
|
|
21
|
+
"types": "./dist/h3/index.d.ts",
|
|
22
|
+
"import": "./dist/h3/index.mjs"
|
|
23
|
+
}
|
|
10
24
|
},
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
25
|
"dependencies": {
|
|
13
26
|
"@tsndr/cloudflare-worker-jwt": "^3.2.0",
|
|
14
|
-
"@nitrotool/errors": "
|
|
27
|
+
"@nitrotool/errors": "1.0.1",
|
|
28
|
+
"@nuxt/kit": "latest",
|
|
29
|
+
"pathe": "^2.0.3",
|
|
30
|
+
"@nuxt/schema": "^4.2.2",
|
|
31
|
+
"consola": "^3.4.2",
|
|
32
|
+
"nitropack": "^2.13.1"
|
|
15
33
|
},
|
|
16
34
|
"peerDependencies": {
|
|
17
35
|
"h3": "^1.15.3"
|
|
@@ -19,6 +37,19 @@
|
|
|
19
37
|
"devDependencies": {
|
|
20
38
|
"unbuild": "^3.5.0"
|
|
21
39
|
},
|
|
40
|
+
"unbuild": {
|
|
41
|
+
"entries": [
|
|
42
|
+
"src/index",
|
|
43
|
+
"src/module",
|
|
44
|
+
"src/core/index",
|
|
45
|
+
"src/h3/index",
|
|
46
|
+
"src/runtime/server/index",
|
|
47
|
+
"src/runtime/server/nitro/plugin",
|
|
48
|
+
"src/runtime/server/nitro/utils/index"
|
|
49
|
+
],
|
|
50
|
+
"declaration": true,
|
|
51
|
+
"clean": true
|
|
52
|
+
},
|
|
22
53
|
"scripts": {
|
|
23
54
|
"build": "unbuild ."
|
|
24
55
|
}
|
package/dist/jwt.d.mts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { JwtPayload } from '@tsndr/cloudflare-worker-jwt';
|
|
2
|
-
|
|
3
|
-
type ExtendableJwtPayload<T extends Record<string, any> = {}> = Partial<JwtPayload> & T;
|
|
4
|
-
declare const encodeJwtRaw: <T extends Record<string, any> = {}>(payload: ExtendableJwtPayload<T>, secret: string, ttl?: number) => Promise<string>;
|
|
5
|
-
declare const encodeJwt: <T extends Record<string, any> = {}>(payload: ExtendableJwtPayload<T>) => Promise<string>;
|
|
6
|
-
declare const verifyJwtRaw: (token: string, secret: string) => Promise<boolean>;
|
|
7
|
-
declare const verifyJwt: (token: string) => Promise<boolean>;
|
|
8
|
-
declare const decodeJwtRaw: <T extends Record<string, any> = {}>(token: string, secret: string, { verify }?: {
|
|
9
|
-
verify?: boolean;
|
|
10
|
-
}) => Promise<ExtendableJwtPayload<T>>;
|
|
11
|
-
declare const decodeJwt: <T extends Record<string, any> = {}>(token: string, { verify }?: {
|
|
12
|
-
verify?: boolean;
|
|
13
|
-
}) => Promise<ExtendableJwtPayload<T>>;
|
|
14
|
-
|
|
15
|
-
export { decodeJwt, decodeJwtRaw, encodeJwt, encodeJwtRaw, verifyJwt, verifyJwtRaw };
|
|
16
|
-
export type { ExtendableJwtPayload };
|
package/dist/jwt.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { JwtPayload } from '@tsndr/cloudflare-worker-jwt';
|
|
2
|
-
|
|
3
|
-
type ExtendableJwtPayload<T extends Record<string, any> = {}> = Partial<JwtPayload> & T;
|
|
4
|
-
declare const encodeJwtRaw: <T extends Record<string, any> = {}>(payload: ExtendableJwtPayload<T>, secret: string, ttl?: number) => Promise<string>;
|
|
5
|
-
declare const encodeJwt: <T extends Record<string, any> = {}>(payload: ExtendableJwtPayload<T>) => Promise<string>;
|
|
6
|
-
declare const verifyJwtRaw: (token: string, secret: string) => Promise<boolean>;
|
|
7
|
-
declare const verifyJwt: (token: string) => Promise<boolean>;
|
|
8
|
-
declare const decodeJwtRaw: <T extends Record<string, any> = {}>(token: string, secret: string, { verify }?: {
|
|
9
|
-
verify?: boolean;
|
|
10
|
-
}) => Promise<ExtendableJwtPayload<T>>;
|
|
11
|
-
declare const decodeJwt: <T extends Record<string, any> = {}>(token: string, { verify }?: {
|
|
12
|
-
verify?: boolean;
|
|
13
|
-
}) => Promise<ExtendableJwtPayload<T>>;
|
|
14
|
-
|
|
15
|
-
export { decodeJwt, decodeJwtRaw, encodeJwt, encodeJwtRaw, verifyJwt, verifyJwtRaw };
|
|
16
|
-
export type { ExtendableJwtPayload };
|
package/dist/jwt.mjs
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import jwt from '@tsndr/cloudflare-worker-jwt';
|
|
2
|
-
import { UnauthorizedError } from '@nitrotool/errors';
|
|
3
|
-
|
|
4
|
-
const encodeJwtRaw = async (payload, secret, ttl = 60) => {
|
|
5
|
-
return await jwt.sign(
|
|
6
|
-
{
|
|
7
|
-
exp: Date.now() / 1e3 + ttl,
|
|
8
|
-
...payload
|
|
9
|
-
},
|
|
10
|
-
secret
|
|
11
|
-
);
|
|
12
|
-
};
|
|
13
|
-
const encodeJwt = async (payload) => {
|
|
14
|
-
const secret = (
|
|
15
|
-
//@ts-expect-error Expected.
|
|
16
|
-
typeof useRuntimeConfig === "function" ? useRuntimeConfig()?.jwtSecret : ""
|
|
17
|
-
);
|
|
18
|
-
return encodeJwtRaw(payload, secret);
|
|
19
|
-
};
|
|
20
|
-
const verifyJwtRaw = async (token, secret) => {
|
|
21
|
-
return new Promise(async (resolve) => {
|
|
22
|
-
if (await jwt.verify(token, secret, { throwError: false })) {
|
|
23
|
-
return resolve(true);
|
|
24
|
-
}
|
|
25
|
-
return resolve(false);
|
|
26
|
-
});
|
|
27
|
-
};
|
|
28
|
-
const verifyJwt = async (token) => {
|
|
29
|
-
const secret = (
|
|
30
|
-
//@ts-expect-error Expected.
|
|
31
|
-
typeof useRuntimeConfig === "function" ? useRuntimeConfig()?.jwtSecret : ""
|
|
32
|
-
);
|
|
33
|
-
return verifyJwtRaw(token, secret);
|
|
34
|
-
};
|
|
35
|
-
const decodeJwtRaw = async (token, secret, { verify } = { verify: true }) => {
|
|
36
|
-
if (!secret && verify)
|
|
37
|
-
throw new Error("Cannot check signature without secret.");
|
|
38
|
-
if (secret && verify && !await verifyJwtRaw(token, secret)) {
|
|
39
|
-
throw UnauthorizedError("Invalid JWT token.");
|
|
40
|
-
}
|
|
41
|
-
const { payload } = jwt.decode(token);
|
|
42
|
-
return payload;
|
|
43
|
-
};
|
|
44
|
-
const decodeJwt = async (token, { verify } = { verify: true }) => {
|
|
45
|
-
if (verify && !await verifyJwt(token)) {
|
|
46
|
-
throw UnauthorizedError("Invalid JWT token.");
|
|
47
|
-
}
|
|
48
|
-
const { payload } = jwt.decode(token);
|
|
49
|
-
return payload;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export { decodeJwt, decodeJwtRaw, encodeJwt, encodeJwtRaw, verifyJwt, verifyJwtRaw };
|