@nitrotool/jwt 0.0.9 → 1.0.2
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 +107 -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 +9 -3
- package/dist/index.d.ts +9 -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 +17 -0
- package/dist/runtime/server/index.d.ts +17 -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 +7 -0
- package/dist/runtime/server/nitro/utils/index.d.ts +7 -0
- package/dist/runtime/server/nitro/utils/index.mjs +8 -0
- package/dist/shared/jwt.Cd03UmUN.mjs +8 -0
- package/dist/shared/jwt.ftBB7-nW.mjs +14 -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,12 +1,13 @@
|
|
|
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:
|
|
8
10
|
|
|
9
|
-
Learn more about JWT and how it works here:
|
|
10
11
|
- [What is a JWT?](https://www.jwt.io/introduction#what-is-json-web-token)
|
|
11
12
|
- [JWT.io](https://jwt.io/)
|
|
12
13
|
|
|
@@ -20,66 +21,129 @@ pnpm install @nitrotool/jwt
|
|
|
20
21
|
npm install @nitrotool/jwt
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
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
|
+
```
|
|
25
64
|
|
|
26
65
|
## Importing
|
|
27
66
|
|
|
28
|
-
|
|
67
|
+
Import from the main entry or subpaths:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
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
|
+
```
|
|
76
|
+
|
|
77
|
+
## Quick Start Nitro
|
|
78
|
+
|
|
79
|
+
### 1. Create plugin to load secret.
|
|
29
80
|
|
|
30
81
|
```ts
|
|
31
|
-
//
|
|
32
|
-
import {
|
|
82
|
+
// plugins/0.jwt.ts
|
|
83
|
+
import { defineNitroPlugin, useRuntimeConfig } from 'nitropack/runtime';
|
|
84
|
+
import { configureJwtRuntime } from '@nitrotool/jwt';
|
|
33
85
|
|
|
34
|
-
|
|
35
|
-
|
|
86
|
+
export default defineNitroPlugin(() => {
|
|
87
|
+
configureJwtRuntime(() => useRuntimeConfig() as any);
|
|
88
|
+
});
|
|
36
89
|
|
|
37
|
-
// Subpath (h3 helpers)
|
|
38
|
-
import { extractApiToken, requireApiToken } from '@nitrotool/jwt/h3';
|
|
39
90
|
```
|
|
40
91
|
|
|
41
|
-
|
|
92
|
+
### 2. Basic usage
|
|
42
93
|
|
|
43
94
|
```ts
|
|
44
|
-
import {
|
|
95
|
+
import {encodeJwt, verifyJwt, decodeJwt} from '@nitrotool/jwt';
|
|
96
|
+
|
|
97
|
+
type MyClaims = { tenantId: string };
|
|
45
98
|
|
|
46
|
-
|
|
99
|
+
const token = await encodeJwt<MyClaims>({tenantId: '123', sub: '123'});
|
|
47
100
|
|
|
48
|
-
const token = await encodeJwt<MyClaims>({ userId: '123' });
|
|
49
101
|
// Later…
|
|
50
102
|
const isValid = await verifyJwt(token);
|
|
51
|
-
const payload = await decodeJwt<MyClaims>(token); // {
|
|
103
|
+
const payload = await decodeJwt<MyClaims>(token); // { sub: '123', tenantId: '123', exp: ... }
|
|
52
104
|
```
|
|
53
105
|
|
|
54
106
|
By default, non-`Raw` helpers read the secret from your runtime config:
|
|
107
|
+
|
|
55
108
|
- `useRuntimeConfig().jwtSecret`
|
|
56
109
|
|
|
57
110
|
If you need to pass a secret explicitly, use the `*Raw` variants.
|
|
58
111
|
|
|
59
|
-
|
|
112
|
+
### 3. In a route handler
|
|
60
113
|
|
|
61
114
|
Extract API tokens and enforce authentication in request handlers:
|
|
62
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
|
|
63
127
|
```ts
|
|
64
128
|
import { defineEventHandler } from 'h3';
|
|
65
|
-
import { extractApiToken, requireApiToken } from '@nitrotool/jwt
|
|
66
|
-
import { decodeJwt } from '@nitrotool/jwt';
|
|
129
|
+
import { extractApiToken, requireApiToken, decodeJwt } from '@nitrotool/jwt';
|
|
67
130
|
|
|
68
131
|
export default defineEventHandler(async (event) => {
|
|
69
|
-
|
|
70
|
-
|
|
132
|
+
// Try to read token (Authorization: Bearer <token> or ?<queryKey>=<token>)
|
|
133
|
+
const token = extractApiToken(event, {queryKey: 'token'}); // queryKey defaults to 'token'
|
|
71
134
|
|
|
72
|
-
|
|
73
|
-
|
|
135
|
+
// Or strictly require it (throws UnauthenticatedError if missing)
|
|
136
|
+
const requiredToken = requireApiToken(event);
|
|
74
137
|
|
|
75
|
-
|
|
76
|
-
|
|
138
|
+
// Optionally decode/verify
|
|
139
|
+
const claims = await decodeJwt<{ userId: string }>(requiredToken);
|
|
77
140
|
|
|
78
|
-
|
|
141
|
+
return { ok: true, userId: claims.userId };
|
|
79
142
|
});
|
|
80
143
|
```
|
|
81
144
|
|
|
82
145
|
Supported token locations:
|
|
146
|
+
|
|
83
147
|
- Authorization header: `Authorization: Bearer <token>`
|
|
84
148
|
- Query string: `?token=<token>`
|
|
85
149
|
|
|
@@ -88,11 +152,11 @@ Supported token locations:
|
|
|
88
152
|
When using non-`Raw` helpers, ensure a secret is available at runtime:
|
|
89
153
|
|
|
90
154
|
```ts
|
|
91
|
-
//
|
|
155
|
+
// nuxt.config.ts (Nuxt/Nitro runtime config)
|
|
92
156
|
export default defineNuxtConfig({
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
157
|
+
runtimeConfig: {
|
|
158
|
+
jwtSecret: 'some-random-secret-yes-it-is-not-very-secure',
|
|
159
|
+
},
|
|
96
160
|
});
|
|
97
161
|
```
|
|
98
162
|
|
|
@@ -101,27 +165,27 @@ export default defineNuxtConfig({
|
|
|
101
165
|
Sign with custom TTL:
|
|
102
166
|
|
|
103
167
|
```ts
|
|
104
|
-
import {
|
|
168
|
+
import {encodeJwtRaw} from '@nitrotool/jwt';
|
|
105
169
|
|
|
106
170
|
const token = await encodeJwtRaw(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
171
|
+
{userId: '123', role: 'admin'},
|
|
172
|
+
process.env.JWT_SECRET!,
|
|
173
|
+
60 * 10 // 10 minutes
|
|
110
174
|
);
|
|
111
175
|
```
|
|
112
176
|
|
|
113
177
|
Decode without verifying signature (use only for non-sensitive scenarios):
|
|
114
178
|
|
|
115
179
|
```ts
|
|
116
|
-
import {
|
|
180
|
+
import {decodeJwt} from '@nitrotool/jwt';
|
|
117
181
|
|
|
118
|
-
const payload = await decodeJwt<{ userId: string }>(token, {
|
|
182
|
+
const payload = await decodeJwt<{ userId: string }>(token, {verify: false});
|
|
119
183
|
```
|
|
120
184
|
|
|
121
185
|
Verify with an explicit secret:
|
|
122
186
|
|
|
123
187
|
```ts
|
|
124
|
-
import {
|
|
188
|
+
import {verifyJwtRaw} from '@nitrotool/jwt';
|
|
125
189
|
|
|
126
190
|
const ok = await verifyJwtRaw(token, process.env.JWT_SECRET!);
|
|
127
191
|
```
|
|
@@ -147,9 +211,9 @@ All helpers are asynchronous.
|
|
|
147
211
|
- `encodeJwtRaw<T>(payload, secret, ttl = 60): Promise<string>`
|
|
148
212
|
- Signs a token with the provided `secret`.
|
|
149
213
|
- `ttl` is in seconds. Default: `60`.
|
|
150
|
-
- `exp` is set
|
|
214
|
+
- `exp` is set **automagically** from `ttl`.
|
|
151
215
|
|
|
152
|
-
- `encodeJwt<T>(payload): Promise<string>`
|
|
216
|
+
- `encodeJwt<T>(payload, ttl = 60): Promise<string>`
|
|
153
217
|
- Same as `encodeJwtRaw`, but uses `useRuntimeConfig().jwtSecret`.
|
|
154
218
|
|
|
155
219
|
- `verifyJwtRaw(token, secret): Promise<boolean>`
|
|
@@ -168,9 +232,10 @@ All helpers are asynchronous.
|
|
|
168
232
|
- Throws `UnauthorizedError('Invalid JWT token.')` if verification fails.
|
|
169
233
|
|
|
170
234
|
Types:
|
|
235
|
+
|
|
171
236
|
- `ExtendableJwtPayload<T>` lets you define custom claims merged with standard JWT claims.
|
|
172
237
|
|
|
173
|
-
### h3 helpers
|
|
238
|
+
### h3 helpers (required)
|
|
174
239
|
|
|
175
240
|
- `extractBearerToken(event): string | undefined`
|
|
176
241
|
- Reads `Authorization` header and returns the token without `Bearer `.
|
|
@@ -184,7 +249,6 @@ Types:
|
|
|
184
249
|
- `requireApiToken(event): string`
|
|
185
250
|
- Same as `extractApiToken`, but throws `UnauthenticatedError` if missing.
|
|
186
251
|
|
|
187
|
-
|
|
188
252
|
## License
|
|
189
253
|
|
|
190
|
-
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,10 @@
|
|
|
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
|
+
import 'nitropack/types';
|
|
7
|
+
|
|
8
|
+
declare const requireJwt: <T extends Record<string, any> = {}>(event: H3Event) => Promise<any>;
|
|
9
|
+
|
|
10
|
+
export { requireJwt };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
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
|
+
import 'nitropack/types';
|
|
7
|
+
|
|
8
|
+
declare const requireJwt: <T extends Record<string, any> = {}>(event: H3Event) => Promise<any>;
|
|
9
|
+
|
|
10
|
+
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.ftBB7-nW.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.2";
|
|
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,17 @@
|
|
|
1
|
+
import { ExtendableJwtPayload } from '../../core/index.mjs';
|
|
2
|
+
import { NitroRuntimeConfig } from 'nitropack/types';
|
|
3
|
+
import '@tsndr/cloudflare-worker-jwt';
|
|
4
|
+
|
|
5
|
+
type JwtRuntimeConfig = NitroRuntimeConfig & {
|
|
6
|
+
jwtSecret?: string;
|
|
7
|
+
};
|
|
8
|
+
declare const configureJwtRuntime: (getter: () => JwtRuntimeConfig) => void;
|
|
9
|
+
declare const getJwtSecret: () => string;
|
|
10
|
+
|
|
11
|
+
declare const encodeJwt: <T extends Record<string, any> = {}>(payload: ExtendableJwtPayload<T>, ttlSec?: number) => Promise<any>;
|
|
12
|
+
declare const verifyJwt: (token: string) => Promise<any>;
|
|
13
|
+
declare const decodeJwt: <T extends Record<string, any> = {}>(token: string, opts?: {
|
|
14
|
+
verify?: boolean;
|
|
15
|
+
}) => Promise<ExtendableJwtPayload<T>>;
|
|
16
|
+
|
|
17
|
+
export { ExtendableJwtPayload, configureJwtRuntime, decodeJwt, encodeJwt, getJwtSecret, verifyJwt };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ExtendableJwtPayload } from '../../core/index.js';
|
|
2
|
+
import { NitroRuntimeConfig } from 'nitropack/types';
|
|
3
|
+
import '@tsndr/cloudflare-worker-jwt';
|
|
4
|
+
|
|
5
|
+
type JwtRuntimeConfig = NitroRuntimeConfig & {
|
|
6
|
+
jwtSecret?: string;
|
|
7
|
+
};
|
|
8
|
+
declare const configureJwtRuntime: (getter: () => JwtRuntimeConfig) => void;
|
|
9
|
+
declare const getJwtSecret: () => string;
|
|
10
|
+
|
|
11
|
+
declare const encodeJwt: <T extends Record<string, any> = {}>(payload: ExtendableJwtPayload<T>, ttlSec?: number) => Promise<any>;
|
|
12
|
+
declare const verifyJwt: (token: string) => Promise<any>;
|
|
13
|
+
declare const decodeJwt: <T extends Record<string, any> = {}>(token: string, opts?: {
|
|
14
|
+
verify?: boolean;
|
|
15
|
+
}) => Promise<ExtendableJwtPayload<T>>;
|
|
16
|
+
|
|
17
|
+
export { ExtendableJwtPayload, configureJwtRuntime, decodeJwt, encodeJwt, getJwtSecret, verifyJwt };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { g as getJwtSecret } from '../../shared/jwt.ftBB7-nW.mjs';
|
|
2
|
+
export { c as configureJwtRuntime } from '../../shared/jwt.ftBB7-nW.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.ftBB7-nW.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,7 @@
|
|
|
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';
|
|
7
|
+
import 'nitropack/types';
|
|
@@ -0,0 +1,7 @@
|
|
|
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';
|
|
7
|
+
import 'nitropack/types';
|
|
@@ -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.ftBB7-nW.mjs';
|
|
5
|
+
import '../../../../core/index.mjs';
|
|
6
|
+
import '@tsndr/cloudflare-worker-jwt';
|
|
7
|
+
import '@nitrotool/errors';
|
|
8
|
+
import 'h3';
|
|
@@ -0,0 +1,14 @@
|
|
|
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?.JWT_SECRET) return process.env.JWT_SECRET;
|
|
9
|
+
throw new Error(
|
|
10
|
+
"jwtSecret is not configured. configure using NUXT_JWT_SECRET or jwt.secret"
|
|
11
|
+
);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
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.2",
|
|
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.2",
|
|
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 };
|