weifuwu 0.32.0 → 0.32.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 +10 -5
- package/dist/index.js +8 -4
- package/dist/middleware/auth.d.ts +9 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -370,22 +370,27 @@ app.post('/api/chat', async (req, ctx) => {
|
|
|
370
370
|
|
|
371
371
|
### Auth
|
|
372
372
|
|
|
373
|
+
JWT extraction priority: `cookie` → `Authorization: Bearer` → `?access_token=`. All callbacks receive `ctx` so `ctx.sql` is directly available.
|
|
374
|
+
|
|
373
375
|
```ts
|
|
374
376
|
import { auth } from 'weifuwu'
|
|
375
377
|
|
|
376
|
-
// JWT
|
|
378
|
+
// JWT — cookie, header, or ?access_token=
|
|
377
379
|
app.use(auth({ jwt: { secret: process.env.JWT_SECRET } }))
|
|
378
380
|
|
|
379
|
-
// Session cookie
|
|
381
|
+
// Session cookie — loadUser gets ctx
|
|
380
382
|
app.use(auth({
|
|
381
383
|
session: {
|
|
382
384
|
secret: '...',
|
|
383
|
-
loadUser: async (data) =>
|
|
385
|
+
loadUser: async (data, ctx) => ctx.sql`SELECT * FROM users WHERE id = ${data.userId}`,
|
|
384
386
|
},
|
|
385
387
|
}))
|
|
386
388
|
|
|
387
|
-
// API key
|
|
388
|
-
app.use(auth({ apiKey: {
|
|
389
|
+
// API key — header or ?api_key=
|
|
390
|
+
app.use(auth({ apiKey: {
|
|
391
|
+
query: 'api_key',
|
|
392
|
+
validate: async (key, ctx) => ctx.sql`SELECT * FROM users WHERE api_key = ${key}`,
|
|
393
|
+
} }))
|
|
389
394
|
|
|
390
395
|
// ctx.user is now available
|
|
391
396
|
app.get('/me', (req, ctx) => {
|
package/dist/index.js
CHANGED
|
@@ -926,7 +926,7 @@ function auth(opts) {
|
|
|
926
926
|
return async (req, ctx, next) => {
|
|
927
927
|
if (opts.jwt) {
|
|
928
928
|
const cookie = opts.jwt.cookie ?? "token";
|
|
929
|
-
const token = parseCookie(req.headers.get("cookie"), cookie) ?? req.headers.get("authorization")?.replace(/^Bearer\s+/i, "");
|
|
929
|
+
const token = parseCookie(req.headers.get("cookie"), cookie) ?? req.headers.get("authorization")?.replace(/^Bearer\s+/i, "") ?? ctx.query.access_token;
|
|
930
930
|
if (token) {
|
|
931
931
|
try {
|
|
932
932
|
const [headerB64, payloadB64, sigB64] = token.split(".");
|
|
@@ -957,7 +957,7 @@ function auth(opts) {
|
|
|
957
957
|
const expectedBuf = Buffer.from(expectedSig, "base64url");
|
|
958
958
|
if (sigBuf.length === expectedBuf.length && timingSafeEqual(sigBuf, expectedBuf)) {
|
|
959
959
|
const data = JSON.parse(base64urlDecode(dataB64));
|
|
960
|
-
const user = await opts.session.loadUser(data);
|
|
960
|
+
const user = await opts.session.loadUser(data, ctx);
|
|
961
961
|
if (user) ctx.user = user;
|
|
962
962
|
}
|
|
963
963
|
} catch {
|
|
@@ -967,10 +967,14 @@ function auth(opts) {
|
|
|
967
967
|
if (opts.apiKey && !ctx.user) {
|
|
968
968
|
const header = opts.apiKey.header ?? "authorization";
|
|
969
969
|
const prefix = opts.apiKey.prefix ?? "Bearer ";
|
|
970
|
-
|
|
970
|
+
let raw = req.headers.get(header);
|
|
971
|
+
if (!raw && opts.apiKey.query) {
|
|
972
|
+
raw = ctx.query[opts.apiKey.query];
|
|
973
|
+
if (raw) raw = prefix + raw;
|
|
974
|
+
}
|
|
971
975
|
if (raw?.startsWith(prefix)) {
|
|
972
976
|
const key = raw.slice(prefix.length);
|
|
973
|
-
const user = await opts.apiKey.validate(key);
|
|
977
|
+
const user = await opts.apiKey.validate(key, ctx);
|
|
974
978
|
if (user) ctx.user = user;
|
|
975
979
|
}
|
|
976
980
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Middleware, User } from '../types.ts';
|
|
1
|
+
import type { Middleware, User, Context } from '../types.ts';
|
|
2
2
|
export interface AuthOptions {
|
|
3
3
|
/**
|
|
4
4
|
* JWT authentication.
|
|
@@ -23,8 +23,8 @@ export interface AuthOptions {
|
|
|
23
23
|
secret: string;
|
|
24
24
|
/** Cookie name (default: 'session'). */
|
|
25
25
|
cookie?: string;
|
|
26
|
-
/** Load user from session data. */
|
|
27
|
-
loadUser: (data: Record<string, unknown
|
|
26
|
+
/** Load user from session data. `ctx` has `ctx.sql`, `ctx.redis`, etc. */
|
|
27
|
+
loadUser: (data: Record<string, unknown>, ctx: Context) => Promise<User | null> | User | null;
|
|
28
28
|
};
|
|
29
29
|
/**
|
|
30
30
|
* API key authentication via header.
|
|
@@ -37,8 +37,10 @@ export interface AuthOptions {
|
|
|
37
37
|
header?: string;
|
|
38
38
|
/** Prefix to strip (default: 'Bearer '). */
|
|
39
39
|
prefix?: string;
|
|
40
|
-
/**
|
|
41
|
-
|
|
40
|
+
/** Query parameter name (e.g. 'api_key'). */
|
|
41
|
+
query?: string;
|
|
42
|
+
/** Validate an API key → User or null. `ctx` has `ctx.sql`, `ctx.redis`, etc. */
|
|
43
|
+
validate: (key: string, ctx: Context) => Promise<User | null> | User | null;
|
|
42
44
|
};
|
|
43
45
|
}
|
|
44
46
|
/**
|
|
@@ -57,12 +59,12 @@ export interface AuthOptions {
|
|
|
57
59
|
* app.use(auth({
|
|
58
60
|
* session: {
|
|
59
61
|
* secret: '...',
|
|
60
|
-
* loadUser: async (data) =>
|
|
62
|
+
* loadUser: async (data, ctx) => ctx.sql`SELECT * FROM users WHERE id = ${data.userId}`,
|
|
61
63
|
* },
|
|
62
64
|
* }))
|
|
63
65
|
*
|
|
64
66
|
* // API key
|
|
65
|
-
* app.use(auth({ apiKey: { validate: async (key) =>
|
|
67
|
+
* app.use(auth({ apiKey: { validate: async (key, ctx) => ctx.sql`...` } }))
|
|
66
68
|
* ```
|
|
67
69
|
*/
|
|
68
70
|
export declare function auth(opts: AuthOptions): Middleware;
|