@solvapay/auth 1.2.0 → 1.3.0-preview-3fd9d2205ea1113de9bbfb34ba7217629ccf40f0
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/dist/chunk-WVD7RQ6U.js +134 -0
- package/dist/index.cjs +81 -59
- package/dist/index.d.cts +43 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +7 -3
- package/dist/next-utils.cjs +76 -56
- package/dist/next-utils.d.cts +6 -7
- package/dist/next-utils.d.ts +6 -7
- package/dist/next-utils.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-ESD73BSC.js +0 -104
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// src/verify-token.ts
|
|
2
|
+
import { createRemoteJWKSet, decodeJwt, decodeProtectedHeader, jwtVerify } from "jose";
|
|
3
|
+
var ASYMMETRIC_ALGORITHMS = ["ES256", "ES384", "ES512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "EdDSA"];
|
|
4
|
+
var TOKEN_NOT_VERIFIABLE = "No way to verify the authentication token. Set SOLVAPAY_JWT_SECRET (HS256 tokens), SOLVAPAY_JWKS_URL (ES256/RS256 tokens; derived from SUPABASE_URL on Supabase), or SOLVAPAY_AUTH_TRUST_GATEWAY=true if a gateway verifies every token before this handler.";
|
|
5
|
+
function readEnv(name) {
|
|
6
|
+
const proc = globalThis.process;
|
|
7
|
+
const deno = globalThis.Deno;
|
|
8
|
+
const value = proc?.env?.[name] ?? deno?.env?.get(name);
|
|
9
|
+
return value && value.length > 0 ? value : void 0;
|
|
10
|
+
}
|
|
11
|
+
function supabaseJwksUrl() {
|
|
12
|
+
const base = readEnv("SUPABASE_URL");
|
|
13
|
+
return base ? `${base.replace(/\/+$/, "")}/auth/v1/.well-known/jwks.json` : void 0;
|
|
14
|
+
}
|
|
15
|
+
var jwksSets = /* @__PURE__ */ new Map();
|
|
16
|
+
function jwksFor(url) {
|
|
17
|
+
let set = jwksSets.get(url);
|
|
18
|
+
if (!set) {
|
|
19
|
+
set = createRemoteJWKSet(new URL(url));
|
|
20
|
+
jwksSets.set(url, set);
|
|
21
|
+
}
|
|
22
|
+
return set;
|
|
23
|
+
}
|
|
24
|
+
async function verifyBearerToken(token, options = {}) {
|
|
25
|
+
const strict = readEnv("SOLVAPAY_AUTH_STRICT") === "true";
|
|
26
|
+
const trustGateway = options.trustGateway ?? readEnv("SOLVAPAY_AUTH_TRUST_GATEWAY") === "true";
|
|
27
|
+
if (strict && trustGateway) {
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
reason: "SOLVAPAY_AUTH_STRICT=true forbids trusting a gateway, but gateway trust is on. Turn one of them off."
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
let alg;
|
|
34
|
+
try {
|
|
35
|
+
alg = decodeProtectedHeader(token).alg;
|
|
36
|
+
} catch {
|
|
37
|
+
return { ok: false, reason: "Malformed authentication token" };
|
|
38
|
+
}
|
|
39
|
+
if (alg === "HS256") {
|
|
40
|
+
const secret = options.jwtSecret || readEnv("SOLVAPAY_JWT_SECRET") || readEnv("SUPABASE_JWT_SECRET");
|
|
41
|
+
if (secret) {
|
|
42
|
+
try {
|
|
43
|
+
const { payload: payload2 } = await jwtVerify(token, new TextEncoder().encode(secret), {
|
|
44
|
+
algorithms: ["HS256"]
|
|
45
|
+
});
|
|
46
|
+
return { ok: true, payload: payload2, verifiedBy: "secret" };
|
|
47
|
+
} catch {
|
|
48
|
+
return { ok: false, reason: "Invalid or expired authentication token" };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} else if (typeof alg === "string" && ASYMMETRIC_ALGORITHMS.includes(alg)) {
|
|
52
|
+
const jwksUrl = options.jwksUrl || readEnv("SOLVAPAY_JWKS_URL") || supabaseJwksUrl();
|
|
53
|
+
if (jwksUrl) {
|
|
54
|
+
try {
|
|
55
|
+
const { payload: payload2 } = await jwtVerify(token, jwksFor(jwksUrl), {
|
|
56
|
+
algorithms: ASYMMETRIC_ALGORITHMS
|
|
57
|
+
});
|
|
58
|
+
return { ok: true, payload: payload2, verifiedBy: "jwks" };
|
|
59
|
+
} catch {
|
|
60
|
+
return { ok: false, reason: "Invalid or expired authentication token" };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
return { ok: false, reason: `Unsupported authentication token algorithm: ${String(alg)}` };
|
|
65
|
+
}
|
|
66
|
+
if (!trustGateway) {
|
|
67
|
+
return { ok: false, reason: TOKEN_NOT_VERIFIABLE };
|
|
68
|
+
}
|
|
69
|
+
let payload;
|
|
70
|
+
try {
|
|
71
|
+
payload = decodeJwt(token);
|
|
72
|
+
} catch {
|
|
73
|
+
return { ok: false, reason: "Malformed authentication token" };
|
|
74
|
+
}
|
|
75
|
+
if (typeof payload.exp === "number" && payload.exp * 1e3 <= Date.now()) {
|
|
76
|
+
return { ok: false, reason: "Invalid or expired authentication token" };
|
|
77
|
+
}
|
|
78
|
+
return { ok: true, payload, verifiedBy: "gateway" };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/next-utils.ts
|
|
82
|
+
function extractBearerToken(request) {
|
|
83
|
+
const authHeader = request.headers.get("authorization");
|
|
84
|
+
if (!authHeader || !authHeader.toLowerCase().startsWith("bearer ")) return null;
|
|
85
|
+
const token = authHeader.slice(7).trim();
|
|
86
|
+
return token.length > 0 ? token : null;
|
|
87
|
+
}
|
|
88
|
+
async function resolveJwtPayload(token, jwtSecret) {
|
|
89
|
+
const verified = await verifyBearerToken(token, { jwtSecret });
|
|
90
|
+
return verified.ok ? verified.payload : null;
|
|
91
|
+
}
|
|
92
|
+
function getUserIdFromRequest(request, options) {
|
|
93
|
+
const headerName = options?.headerName || "x-user-id";
|
|
94
|
+
return request.headers.get(headerName);
|
|
95
|
+
}
|
|
96
|
+
async function getUserEmailFromRequest(request, options) {
|
|
97
|
+
const token = extractBearerToken(request);
|
|
98
|
+
if (!token) return null;
|
|
99
|
+
const payload = await resolveJwtPayload(token, options?.jwtSecret);
|
|
100
|
+
if (!payload) return null;
|
|
101
|
+
return typeof payload.email === "string" ? payload.email : null;
|
|
102
|
+
}
|
|
103
|
+
async function getUserNameFromRequest(request, options) {
|
|
104
|
+
const token = extractBearerToken(request);
|
|
105
|
+
if (!token) return null;
|
|
106
|
+
const payload = await resolveJwtPayload(token, options?.jwtSecret);
|
|
107
|
+
if (!payload) return null;
|
|
108
|
+
const metadataFullName = typeof payload.user_metadata?.full_name === "string" ? payload.user_metadata.full_name : null;
|
|
109
|
+
const metadataName = typeof payload.user_metadata?.name === "string" ? payload.user_metadata.name : null;
|
|
110
|
+
const claimName = typeof payload.name === "string" ? payload.name : null;
|
|
111
|
+
return metadataFullName || metadataName || claimName || null;
|
|
112
|
+
}
|
|
113
|
+
function requireUserId(request, options) {
|
|
114
|
+
const headerName = options?.headerName || "x-user-id";
|
|
115
|
+
const errorMessage = options?.errorMessage || "Unauthorized";
|
|
116
|
+
const errorDetails = options?.errorDetails || "User ID not found. Ensure middleware is configured.";
|
|
117
|
+
const userId = request.headers.get(headerName);
|
|
118
|
+
if (!userId) {
|
|
119
|
+
return new Response(JSON.stringify({ error: errorMessage, details: errorDetails }), {
|
|
120
|
+
status: 401,
|
|
121
|
+
headers: { "Content-Type": "application/json" }
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return userId;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export {
|
|
128
|
+
TOKEN_NOT_VERIFIABLE,
|
|
129
|
+
verifyBearerToken,
|
|
130
|
+
getUserIdFromRequest,
|
|
131
|
+
getUserEmailFromRequest,
|
|
132
|
+
getUserNameFromRequest,
|
|
133
|
+
requireUserId
|
|
134
|
+
};
|
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
|
|
30
20
|
// src/index.ts
|
|
@@ -35,10 +25,12 @@ __export(index_exports, {
|
|
|
35
25
|
SOLVAPAY_USER_ID_HEADER: () => SOLVAPAY_USER_ID_HEADER,
|
|
36
26
|
SolvapayAuthAdapter: () => SolvapayAuthAdapter,
|
|
37
27
|
SolvapayOAuthClient: () => SolvapayOAuthClient,
|
|
28
|
+
TOKEN_NOT_VERIFIABLE: () => TOKEN_NOT_VERIFIABLE,
|
|
38
29
|
getUserEmailFromRequest: () => getUserEmailFromRequest,
|
|
39
30
|
getUserIdFromRequest: () => getUserIdFromRequest,
|
|
40
31
|
getUserNameFromRequest: () => getUserNameFromRequest,
|
|
41
|
-
requireUserId: () => requireUserId
|
|
32
|
+
requireUserId: () => requireUserId,
|
|
33
|
+
verifyBearerToken: () => verifyBearerToken
|
|
42
34
|
});
|
|
43
35
|
module.exports = __toCommonJS(index_exports);
|
|
44
36
|
|
|
@@ -180,68 +172,96 @@ var SolvapayOAuthClient = class {
|
|
|
180
172
|
}
|
|
181
173
|
};
|
|
182
174
|
|
|
183
|
-
// src/
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
175
|
+
// src/verify-token.ts
|
|
176
|
+
var import_jose = require("jose");
|
|
177
|
+
var ASYMMETRIC_ALGORITHMS = ["ES256", "ES384", "ES512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "EdDSA"];
|
|
178
|
+
var TOKEN_NOT_VERIFIABLE = "No way to verify the authentication token. Set SOLVAPAY_JWT_SECRET (HS256 tokens), SOLVAPAY_JWKS_URL (ES256/RS256 tokens; derived from SUPABASE_URL on Supabase), or SOLVAPAY_AUTH_TRUST_GATEWAY=true if a gateway verifies every token before this handler.";
|
|
179
|
+
function readEnv(name) {
|
|
180
|
+
const proc = globalThis.process;
|
|
181
|
+
const deno = globalThis.Deno;
|
|
182
|
+
const value = proc?.env?.[name] ?? deno?.env?.get(name);
|
|
183
|
+
return value && value.length > 0 ? value : void 0;
|
|
184
|
+
}
|
|
185
|
+
function supabaseJwksUrl() {
|
|
186
|
+
const base = readEnv("SUPABASE_URL");
|
|
187
|
+
return base ? `${base.replace(/\/+$/, "")}/auth/v1/.well-known/jwks.json` : void 0;
|
|
188
|
+
}
|
|
189
|
+
var jwksSets = /* @__PURE__ */ new Map();
|
|
190
|
+
function jwksFor(url) {
|
|
191
|
+
let set = jwksSets.get(url);
|
|
192
|
+
if (!set) {
|
|
193
|
+
set = (0, import_jose.createRemoteJWKSet)(new URL(url));
|
|
194
|
+
jwksSets.set(url, set);
|
|
195
|
+
}
|
|
196
|
+
return set;
|
|
197
|
+
}
|
|
198
|
+
async function verifyBearerToken(token, options = {}) {
|
|
199
|
+
const strict = readEnv("SOLVAPAY_AUTH_STRICT") === "true";
|
|
200
|
+
const trustGateway = options.trustGateway ?? readEnv("SOLVAPAY_AUTH_TRUST_GATEWAY") === "true";
|
|
201
|
+
if (strict && trustGateway) {
|
|
202
|
+
return {
|
|
203
|
+
ok: false,
|
|
204
|
+
reason: "SOLVAPAY_AUTH_STRICT=true forbids trusting a gateway, but gateway trust is on. Turn one of them off."
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
let alg;
|
|
208
|
+
try {
|
|
209
|
+
alg = (0, import_jose.decodeProtectedHeader)(token).alg;
|
|
210
|
+
} catch {
|
|
211
|
+
return { ok: false, reason: "Malformed authentication token" };
|
|
212
|
+
}
|
|
213
|
+
if (alg === "HS256") {
|
|
214
|
+
const secret = options.jwtSecret || readEnv("SOLVAPAY_JWT_SECRET") || readEnv("SUPABASE_JWT_SECRET");
|
|
215
|
+
if (secret) {
|
|
216
|
+
try {
|
|
217
|
+
const { payload: payload2 } = await (0, import_jose.jwtVerify)(token, new TextEncoder().encode(secret), {
|
|
218
|
+
algorithms: ["HS256"]
|
|
219
|
+
});
|
|
220
|
+
return { ok: true, payload: payload2, verifiedBy: "secret" };
|
|
221
|
+
} catch {
|
|
222
|
+
return { ok: false, reason: "Invalid or expired authentication token" };
|
|
223
|
+
}
|
|
193
224
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
225
|
+
} else if (typeof alg === "string" && ASYMMETRIC_ALGORITHMS.includes(alg)) {
|
|
226
|
+
const jwksUrl = options.jwksUrl || readEnv("SOLVAPAY_JWKS_URL") || supabaseJwksUrl();
|
|
227
|
+
if (jwksUrl) {
|
|
228
|
+
try {
|
|
229
|
+
const { payload: payload2 } = await (0, import_jose.jwtVerify)(token, jwksFor(jwksUrl), {
|
|
230
|
+
algorithms: ASYMMETRIC_ALGORITHMS
|
|
231
|
+
});
|
|
232
|
+
return { ok: true, payload: payload2, verifiedBy: "jwks" };
|
|
233
|
+
} catch {
|
|
234
|
+
return { ok: false, reason: "Invalid or expired authentication token" };
|
|
235
|
+
}
|
|
198
236
|
}
|
|
237
|
+
} else {
|
|
238
|
+
return { ok: false, reason: `Unsupported authentication token algorithm: ${String(alg)}` };
|
|
199
239
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
return BufferCtor.from(base64, "base64").toString("utf-8");
|
|
240
|
+
if (!trustGateway) {
|
|
241
|
+
return { ok: false, reason: TOKEN_NOT_VERIFIABLE };
|
|
203
242
|
}
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
function decodeJwtUnverified(token) {
|
|
207
|
-
const parts = token.split(".");
|
|
208
|
-
if (parts.length !== 3) return null;
|
|
243
|
+
let payload;
|
|
209
244
|
try {
|
|
210
|
-
|
|
211
|
-
const payload = JSON.parse(json);
|
|
212
|
-
if (typeof payload !== "object" || payload === null) return null;
|
|
213
|
-
return payload;
|
|
245
|
+
payload = (0, import_jose.decodeJwt)(token);
|
|
214
246
|
} catch {
|
|
215
|
-
return
|
|
247
|
+
return { ok: false, reason: "Malformed authentication token" };
|
|
216
248
|
}
|
|
249
|
+
if (typeof payload.exp === "number" && payload.exp * 1e3 <= Date.now()) {
|
|
250
|
+
return { ok: false, reason: "Invalid or expired authentication token" };
|
|
251
|
+
}
|
|
252
|
+
return { ok: true, payload, verifiedBy: "gateway" };
|
|
217
253
|
}
|
|
254
|
+
|
|
255
|
+
// src/next-utils.ts
|
|
218
256
|
function extractBearerToken(request) {
|
|
219
257
|
const authHeader = request.headers.get("authorization");
|
|
220
258
|
if (!authHeader || !authHeader.toLowerCase().startsWith("bearer ")) return null;
|
|
221
259
|
const token = authHeader.slice(7).trim();
|
|
222
260
|
return token.length > 0 ? token : null;
|
|
223
261
|
}
|
|
224
|
-
function readEnv(name) {
|
|
225
|
-
const proc = globalThis.process;
|
|
226
|
-
return proc?.env?.[name];
|
|
227
|
-
}
|
|
228
|
-
function isStrictMode() {
|
|
229
|
-
return readEnv("SOLVAPAY_AUTH_STRICT") === "true";
|
|
230
|
-
}
|
|
231
262
|
async function resolveJwtPayload(token, jwtSecret) {
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
const { jwtVerify } = await import("jose");
|
|
236
|
-
const key = new TextEncoder().encode(secret);
|
|
237
|
-
const { payload } = await jwtVerify(token, key, { algorithms: ["HS256"] });
|
|
238
|
-
return payload;
|
|
239
|
-
} catch {
|
|
240
|
-
return null;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
if (isStrictMode()) return null;
|
|
244
|
-
return decodeJwtUnverified(token);
|
|
263
|
+
const verified = await verifyBearerToken(token, { jwtSecret });
|
|
264
|
+
return verified.ok ? verified.payload : null;
|
|
245
265
|
}
|
|
246
266
|
function getUserIdFromRequest(request, options) {
|
|
247
267
|
const headerName = options?.headerName || "x-user-id";
|
|
@@ -284,8 +304,10 @@ function requireUserId(request, options) {
|
|
|
284
304
|
SOLVAPAY_USER_ID_HEADER,
|
|
285
305
|
SolvapayAuthAdapter,
|
|
286
306
|
SolvapayOAuthClient,
|
|
307
|
+
TOKEN_NOT_VERIFIABLE,
|
|
287
308
|
getUserEmailFromRequest,
|
|
288
309
|
getUserIdFromRequest,
|
|
289
310
|
getUserNameFromRequest,
|
|
290
|
-
requireUserId
|
|
311
|
+
requireUserId,
|
|
312
|
+
verifyBearerToken
|
|
291
313
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { A as AuthAdapter, R as RequestLike } from './adapter-B2LYWIJl.cjs';
|
|
2
2
|
export { a as AuthRequestHandleResult, S as ServerIdentity } from './adapter-B2LYWIJl.cjs';
|
|
3
3
|
export { MockAuthAdapter, MockAuthAdapter as MockAuthAdapterType } from './mock.cjs';
|
|
4
|
+
import { JWTPayload } from 'jose';
|
|
4
5
|
export { getUserEmailFromRequest, getUserIdFromRequest, getUserNameFromRequest, requireUserId } from './next-utils.cjs';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -78,4 +79,45 @@ declare class SolvapayOAuthClient {
|
|
|
78
79
|
getUserInfo(accessToken: string, userInfoUrl?: string): Promise<Record<string, unknown>>;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Verifies a bearer token before a route trusts who it names.
|
|
84
|
+
*
|
|
85
|
+
* Every SolvaPay route helper that reads the user from `Authorization: Bearer`
|
|
86
|
+
* goes through `verifyBearerToken`. A token is trusted only when one of these
|
|
87
|
+
* says so, chosen by the token's `alg`:
|
|
88
|
+
*
|
|
89
|
+
* - **HS256** with a shared secret: `jwtSecret`, else `SOLVAPAY_JWT_SECRET`,
|
|
90
|
+
* else `SUPABASE_JWT_SECRET`.
|
|
91
|
+
* - **ES256 / RS256 and other asymmetric algorithms** against a JWKS:
|
|
92
|
+
* `jwksUrl`, else `SOLVAPAY_JWKS_URL`, else, on Supabase,
|
|
93
|
+
* `${SUPABASE_URL}/auth/v1/.well-known/jwks.json`.
|
|
94
|
+
* - **A gateway that already verified it**, only when you say so:
|
|
95
|
+
* `trustGateway`, or `SOLVAPAY_AUTH_TRUST_GATEWAY=true` (for example
|
|
96
|
+
* Supabase Edge Functions with `verify_jwt = true`). The token is then read
|
|
97
|
+
* without checking its signature, so turn this on only when nothing can
|
|
98
|
+
* reach the handler without passing that gateway.
|
|
99
|
+
*
|
|
100
|
+
* With none of these, the token is refused: there is no unverified default.
|
|
101
|
+
* `SOLVAPAY_AUTH_STRICT=true` forbids gateway trust outright.
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
type TokenVerification = {
|
|
105
|
+
ok: true;
|
|
106
|
+
payload: JWTPayload;
|
|
107
|
+
verifiedBy: 'secret' | 'jwks' | 'gateway';
|
|
108
|
+
} | {
|
|
109
|
+
ok: false;
|
|
110
|
+
reason: string;
|
|
111
|
+
};
|
|
112
|
+
interface VerifyBearerTokenOptions {
|
|
113
|
+
/** HS256 secret; defaults to SOLVAPAY_JWT_SECRET, then SUPABASE_JWT_SECRET. */
|
|
114
|
+
jwtSecret?: string;
|
|
115
|
+
/** JWKS URL for ES256/RS256 tokens; defaults to SOLVAPAY_JWKS_URL, then Supabase's from SUPABASE_URL. */
|
|
116
|
+
jwksUrl?: string;
|
|
117
|
+
/** Trust a gateway that verified the token already; defaults to SOLVAPAY_AUTH_TRUST_GATEWAY=true. */
|
|
118
|
+
trustGateway?: boolean;
|
|
119
|
+
}
|
|
120
|
+
declare const TOKEN_NOT_VERIFIABLE: string;
|
|
121
|
+
declare function verifyBearerToken(token: string, options?: VerifyBearerTokenOptions): Promise<TokenVerification>;
|
|
122
|
+
|
|
123
|
+
export { AuthAdapter, RequestLike, SOLVAPAY_AUTHORIZATION_HEADER, SOLVAPAY_USER_ID_HEADER, SolvapayAuthAdapter, type SolvapayAuthAdapterConfig, SolvapayOAuthClient, type SolvapayOAuthConfig, TOKEN_NOT_VERIFIABLE, type TokenResponse, type TokenVerification, type VerifyBearerTokenOptions, verifyBearerToken };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { A as AuthAdapter, R as RequestLike } from './adapter-B2LYWIJl.js';
|
|
2
2
|
export { a as AuthRequestHandleResult, S as ServerIdentity } from './adapter-B2LYWIJl.js';
|
|
3
3
|
export { MockAuthAdapter, MockAuthAdapter as MockAuthAdapterType } from './mock.js';
|
|
4
|
+
import { JWTPayload } from 'jose';
|
|
4
5
|
export { getUserEmailFromRequest, getUserIdFromRequest, getUserNameFromRequest, requireUserId } from './next-utils.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -78,4 +79,45 @@ declare class SolvapayOAuthClient {
|
|
|
78
79
|
getUserInfo(accessToken: string, userInfoUrl?: string): Promise<Record<string, unknown>>;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Verifies a bearer token before a route trusts who it names.
|
|
84
|
+
*
|
|
85
|
+
* Every SolvaPay route helper that reads the user from `Authorization: Bearer`
|
|
86
|
+
* goes through `verifyBearerToken`. A token is trusted only when one of these
|
|
87
|
+
* says so, chosen by the token's `alg`:
|
|
88
|
+
*
|
|
89
|
+
* - **HS256** with a shared secret: `jwtSecret`, else `SOLVAPAY_JWT_SECRET`,
|
|
90
|
+
* else `SUPABASE_JWT_SECRET`.
|
|
91
|
+
* - **ES256 / RS256 and other asymmetric algorithms** against a JWKS:
|
|
92
|
+
* `jwksUrl`, else `SOLVAPAY_JWKS_URL`, else, on Supabase,
|
|
93
|
+
* `${SUPABASE_URL}/auth/v1/.well-known/jwks.json`.
|
|
94
|
+
* - **A gateway that already verified it**, only when you say so:
|
|
95
|
+
* `trustGateway`, or `SOLVAPAY_AUTH_TRUST_GATEWAY=true` (for example
|
|
96
|
+
* Supabase Edge Functions with `verify_jwt = true`). The token is then read
|
|
97
|
+
* without checking its signature, so turn this on only when nothing can
|
|
98
|
+
* reach the handler without passing that gateway.
|
|
99
|
+
*
|
|
100
|
+
* With none of these, the token is refused: there is no unverified default.
|
|
101
|
+
* `SOLVAPAY_AUTH_STRICT=true` forbids gateway trust outright.
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
type TokenVerification = {
|
|
105
|
+
ok: true;
|
|
106
|
+
payload: JWTPayload;
|
|
107
|
+
verifiedBy: 'secret' | 'jwks' | 'gateway';
|
|
108
|
+
} | {
|
|
109
|
+
ok: false;
|
|
110
|
+
reason: string;
|
|
111
|
+
};
|
|
112
|
+
interface VerifyBearerTokenOptions {
|
|
113
|
+
/** HS256 secret; defaults to SOLVAPAY_JWT_SECRET, then SUPABASE_JWT_SECRET. */
|
|
114
|
+
jwtSecret?: string;
|
|
115
|
+
/** JWKS URL for ES256/RS256 tokens; defaults to SOLVAPAY_JWKS_URL, then Supabase's from SUPABASE_URL. */
|
|
116
|
+
jwksUrl?: string;
|
|
117
|
+
/** Trust a gateway that verified the token already; defaults to SOLVAPAY_AUTH_TRUST_GATEWAY=true. */
|
|
118
|
+
trustGateway?: boolean;
|
|
119
|
+
}
|
|
120
|
+
declare const TOKEN_NOT_VERIFIABLE: string;
|
|
121
|
+
declare function verifyBearerToken(token: string, options?: VerifyBearerTokenOptions): Promise<TokenVerification>;
|
|
122
|
+
|
|
123
|
+
export { AuthAdapter, RequestLike, SOLVAPAY_AUTHORIZATION_HEADER, SOLVAPAY_USER_ID_HEADER, SolvapayAuthAdapter, type SolvapayAuthAdapterConfig, SolvapayOAuthClient, type SolvapayOAuthConfig, TOKEN_NOT_VERIFIABLE, type TokenResponse, type TokenVerification, type VerifyBearerTokenOptions, verifyBearerToken };
|
package/dist/index.js
CHANGED
|
@@ -2,11 +2,13 @@ import {
|
|
|
2
2
|
MockAuthAdapter
|
|
3
3
|
} from "./chunk-OETEFHW6.js";
|
|
4
4
|
import {
|
|
5
|
+
TOKEN_NOT_VERIFIABLE,
|
|
5
6
|
getUserEmailFromRequest,
|
|
6
7
|
getUserIdFromRequest,
|
|
7
8
|
getUserNameFromRequest,
|
|
8
|
-
requireUserId
|
|
9
|
-
|
|
9
|
+
requireUserId,
|
|
10
|
+
verifyBearerToken
|
|
11
|
+
} from "./chunk-WVD7RQ6U.js";
|
|
10
12
|
|
|
11
13
|
// src/constants.ts
|
|
12
14
|
var SOLVAPAY_USER_ID_HEADER = "x-user-id";
|
|
@@ -136,8 +138,10 @@ export {
|
|
|
136
138
|
SOLVAPAY_USER_ID_HEADER,
|
|
137
139
|
SolvapayAuthAdapter,
|
|
138
140
|
SolvapayOAuthClient,
|
|
141
|
+
TOKEN_NOT_VERIFIABLE,
|
|
139
142
|
getUserEmailFromRequest,
|
|
140
143
|
getUserIdFromRequest,
|
|
141
144
|
getUserNameFromRequest,
|
|
142
|
-
requireUserId
|
|
145
|
+
requireUserId,
|
|
146
|
+
verifyBearerToken
|
|
143
147
|
};
|
package/dist/next-utils.cjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
|
|
30
20
|
// src/next-utils.ts
|
|
@@ -36,67 +26,97 @@ __export(next_utils_exports, {
|
|
|
36
26
|
requireUserId: () => requireUserId
|
|
37
27
|
});
|
|
38
28
|
module.exports = __toCommonJS(next_utils_exports);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
29
|
+
|
|
30
|
+
// src/verify-token.ts
|
|
31
|
+
var import_jose = require("jose");
|
|
32
|
+
var ASYMMETRIC_ALGORITHMS = ["ES256", "ES384", "ES512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "EdDSA"];
|
|
33
|
+
var TOKEN_NOT_VERIFIABLE = "No way to verify the authentication token. Set SOLVAPAY_JWT_SECRET (HS256 tokens), SOLVAPAY_JWKS_URL (ES256/RS256 tokens; derived from SUPABASE_URL on Supabase), or SOLVAPAY_AUTH_TRUST_GATEWAY=true if a gateway verifies every token before this handler.";
|
|
34
|
+
function readEnv(name) {
|
|
35
|
+
const proc = globalThis.process;
|
|
36
|
+
const deno = globalThis.Deno;
|
|
37
|
+
const value = proc?.env?.[name] ?? deno?.env?.get(name);
|
|
38
|
+
return value && value.length > 0 ? value : void 0;
|
|
39
|
+
}
|
|
40
|
+
function supabaseJwksUrl() {
|
|
41
|
+
const base = readEnv("SUPABASE_URL");
|
|
42
|
+
return base ? `${base.replace(/\/+$/, "")}/auth/v1/.well-known/jwks.json` : void 0;
|
|
43
|
+
}
|
|
44
|
+
var jwksSets = /* @__PURE__ */ new Map();
|
|
45
|
+
function jwksFor(url) {
|
|
46
|
+
let set = jwksSets.get(url);
|
|
47
|
+
if (!set) {
|
|
48
|
+
set = (0, import_jose.createRemoteJWKSet)(new URL(url));
|
|
49
|
+
jwksSets.set(url, set);
|
|
50
|
+
}
|
|
51
|
+
return set;
|
|
52
|
+
}
|
|
53
|
+
async function verifyBearerToken(token, options = {}) {
|
|
54
|
+
const strict = readEnv("SOLVAPAY_AUTH_STRICT") === "true";
|
|
55
|
+
const trustGateway = options.trustGateway ?? readEnv("SOLVAPAY_AUTH_TRUST_GATEWAY") === "true";
|
|
56
|
+
if (strict && trustGateway) {
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
reason: "SOLVAPAY_AUTH_STRICT=true forbids trusting a gateway, but gateway trust is on. Turn one of them off."
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
let alg;
|
|
63
|
+
try {
|
|
64
|
+
alg = (0, import_jose.decodeProtectedHeader)(token).alg;
|
|
65
|
+
} catch {
|
|
66
|
+
return { ok: false, reason: "Malformed authentication token" };
|
|
67
|
+
}
|
|
68
|
+
if (alg === "HS256") {
|
|
69
|
+
const secret = options.jwtSecret || readEnv("SOLVAPAY_JWT_SECRET") || readEnv("SUPABASE_JWT_SECRET");
|
|
70
|
+
if (secret) {
|
|
71
|
+
try {
|
|
72
|
+
const { payload: payload2 } = await (0, import_jose.jwtVerify)(token, new TextEncoder().encode(secret), {
|
|
73
|
+
algorithms: ["HS256"]
|
|
74
|
+
});
|
|
75
|
+
return { ok: true, payload: payload2, verifiedBy: "secret" };
|
|
76
|
+
} catch {
|
|
77
|
+
return { ok: false, reason: "Invalid or expired authentication token" };
|
|
78
|
+
}
|
|
48
79
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
80
|
+
} else if (typeof alg === "string" && ASYMMETRIC_ALGORITHMS.includes(alg)) {
|
|
81
|
+
const jwksUrl = options.jwksUrl || readEnv("SOLVAPAY_JWKS_URL") || supabaseJwksUrl();
|
|
82
|
+
if (jwksUrl) {
|
|
83
|
+
try {
|
|
84
|
+
const { payload: payload2 } = await (0, import_jose.jwtVerify)(token, jwksFor(jwksUrl), {
|
|
85
|
+
algorithms: ASYMMETRIC_ALGORITHMS
|
|
86
|
+
});
|
|
87
|
+
return { ok: true, payload: payload2, verifiedBy: "jwks" };
|
|
88
|
+
} catch {
|
|
89
|
+
return { ok: false, reason: "Invalid or expired authentication token" };
|
|
90
|
+
}
|
|
53
91
|
}
|
|
92
|
+
} else {
|
|
93
|
+
return { ok: false, reason: `Unsupported authentication token algorithm: ${String(alg)}` };
|
|
54
94
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return BufferCtor.from(base64, "base64").toString("utf-8");
|
|
95
|
+
if (!trustGateway) {
|
|
96
|
+
return { ok: false, reason: TOKEN_NOT_VERIFIABLE };
|
|
58
97
|
}
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
function decodeJwtUnverified(token) {
|
|
62
|
-
const parts = token.split(".");
|
|
63
|
-
if (parts.length !== 3) return null;
|
|
98
|
+
let payload;
|
|
64
99
|
try {
|
|
65
|
-
|
|
66
|
-
const payload = JSON.parse(json);
|
|
67
|
-
if (typeof payload !== "object" || payload === null) return null;
|
|
68
|
-
return payload;
|
|
100
|
+
payload = (0, import_jose.decodeJwt)(token);
|
|
69
101
|
} catch {
|
|
70
|
-
return
|
|
102
|
+
return { ok: false, reason: "Malformed authentication token" };
|
|
103
|
+
}
|
|
104
|
+
if (typeof payload.exp === "number" && payload.exp * 1e3 <= Date.now()) {
|
|
105
|
+
return { ok: false, reason: "Invalid or expired authentication token" };
|
|
71
106
|
}
|
|
107
|
+
return { ok: true, payload, verifiedBy: "gateway" };
|
|
72
108
|
}
|
|
109
|
+
|
|
110
|
+
// src/next-utils.ts
|
|
73
111
|
function extractBearerToken(request) {
|
|
74
112
|
const authHeader = request.headers.get("authorization");
|
|
75
113
|
if (!authHeader || !authHeader.toLowerCase().startsWith("bearer ")) return null;
|
|
76
114
|
const token = authHeader.slice(7).trim();
|
|
77
115
|
return token.length > 0 ? token : null;
|
|
78
116
|
}
|
|
79
|
-
function readEnv(name) {
|
|
80
|
-
const proc = globalThis.process;
|
|
81
|
-
return proc?.env?.[name];
|
|
82
|
-
}
|
|
83
|
-
function isStrictMode() {
|
|
84
|
-
return readEnv("SOLVAPAY_AUTH_STRICT") === "true";
|
|
85
|
-
}
|
|
86
117
|
async function resolveJwtPayload(token, jwtSecret) {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
const { jwtVerify } = await import("jose");
|
|
91
|
-
const key = new TextEncoder().encode(secret);
|
|
92
|
-
const { payload } = await jwtVerify(token, key, { algorithms: ["HS256"] });
|
|
93
|
-
return payload;
|
|
94
|
-
} catch {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if (isStrictMode()) return null;
|
|
99
|
-
return decodeJwtUnverified(token);
|
|
118
|
+
const verified = await verifyBearerToken(token, { jwtSecret });
|
|
119
|
+
return verified.ok ? verified.payload : null;
|
|
100
120
|
}
|
|
101
121
|
function getUserIdFromRequest(request, options) {
|
|
102
122
|
const headerName = options?.headerName || "x-user-id";
|
package/dist/next-utils.d.cts
CHANGED
|
@@ -46,13 +46,12 @@ declare function getUserIdFromRequest(request: Request, options?: {
|
|
|
46
46
|
/**
|
|
47
47
|
* Extract user email from a Bearer JWT in the Authorization header.
|
|
48
48
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* unverified-decode fallback.
|
|
49
|
+
* The token is verified first, as `verifyBearerToken` does: HS256 with
|
|
50
|
+
* `options.jwtSecret` / `SOLVAPAY_JWT_SECRET` / `SUPABASE_JWT_SECRET`,
|
|
51
|
+
* ES256/RS256 against `SOLVAPAY_JWKS_URL` (or Supabase's JWKS from
|
|
52
|
+
* `SUPABASE_URL`), or a gateway you trust with
|
|
53
|
+
* `SOLVAPAY_AUTH_TRUST_GATEWAY=true`. A token none of these verifies gives
|
|
54
|
+
* `null`.
|
|
56
55
|
*
|
|
57
56
|
* @param request - Request object (works with NextRequest from next/server)
|
|
58
57
|
* @param options - Configuration options
|
package/dist/next-utils.d.ts
CHANGED
|
@@ -46,13 +46,12 @@ declare function getUserIdFromRequest(request: Request, options?: {
|
|
|
46
46
|
/**
|
|
47
47
|
* Extract user email from a Bearer JWT in the Authorization header.
|
|
48
48
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* unverified-decode fallback.
|
|
49
|
+
* The token is verified first, as `verifyBearerToken` does: HS256 with
|
|
50
|
+
* `options.jwtSecret` / `SOLVAPAY_JWT_SECRET` / `SUPABASE_JWT_SECRET`,
|
|
51
|
+
* ES256/RS256 against `SOLVAPAY_JWKS_URL` (or Supabase's JWKS from
|
|
52
|
+
* `SUPABASE_URL`), or a gateway you trust with
|
|
53
|
+
* `SOLVAPAY_AUTH_TRUST_GATEWAY=true`. A token none of these verifies gives
|
|
54
|
+
* `null`.
|
|
56
55
|
*
|
|
57
56
|
* @param request - Request object (works with NextRequest from next/server)
|
|
58
57
|
* @param options - Configuration options
|
package/dist/next-utils.js
CHANGED
package/package.json
CHANGED
package/dist/chunk-ESD73BSC.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
// src/next-utils.ts
|
|
2
|
-
function base64UrlDecode(input) {
|
|
3
|
-
const padded = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
4
|
-
const padding = padded.length % 4 === 0 ? "" : "=".repeat(4 - padded.length % 4);
|
|
5
|
-
const base64 = padded + padding;
|
|
6
|
-
if (typeof atob === "function") {
|
|
7
|
-
const binary = atob(base64);
|
|
8
|
-
let result = "";
|
|
9
|
-
for (let i = 0; i < binary.length; i++) {
|
|
10
|
-
result += String.fromCharCode(binary.charCodeAt(i));
|
|
11
|
-
}
|
|
12
|
-
try {
|
|
13
|
-
return decodeURIComponent(escape(result));
|
|
14
|
-
} catch {
|
|
15
|
-
return result;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
const BufferCtor = globalThis.Buffer;
|
|
19
|
-
if (BufferCtor) {
|
|
20
|
-
return BufferCtor.from(base64, "base64").toString("utf-8");
|
|
21
|
-
}
|
|
22
|
-
throw new Error("No base64 decoder available in this runtime");
|
|
23
|
-
}
|
|
24
|
-
function decodeJwtUnverified(token) {
|
|
25
|
-
const parts = token.split(".");
|
|
26
|
-
if (parts.length !== 3) return null;
|
|
27
|
-
try {
|
|
28
|
-
const json = base64UrlDecode(parts[1]);
|
|
29
|
-
const payload = JSON.parse(json);
|
|
30
|
-
if (typeof payload !== "object" || payload === null) return null;
|
|
31
|
-
return payload;
|
|
32
|
-
} catch {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
function extractBearerToken(request) {
|
|
37
|
-
const authHeader = request.headers.get("authorization");
|
|
38
|
-
if (!authHeader || !authHeader.toLowerCase().startsWith("bearer ")) return null;
|
|
39
|
-
const token = authHeader.slice(7).trim();
|
|
40
|
-
return token.length > 0 ? token : null;
|
|
41
|
-
}
|
|
42
|
-
function readEnv(name) {
|
|
43
|
-
const proc = globalThis.process;
|
|
44
|
-
return proc?.env?.[name];
|
|
45
|
-
}
|
|
46
|
-
function isStrictMode() {
|
|
47
|
-
return readEnv("SOLVAPAY_AUTH_STRICT") === "true";
|
|
48
|
-
}
|
|
49
|
-
async function resolveJwtPayload(token, jwtSecret) {
|
|
50
|
-
const secret = jwtSecret || readEnv("SOLVAPAY_JWT_SECRET") || readEnv("SUPABASE_JWT_SECRET");
|
|
51
|
-
if (secret) {
|
|
52
|
-
try {
|
|
53
|
-
const { jwtVerify } = await import("jose");
|
|
54
|
-
const key = new TextEncoder().encode(secret);
|
|
55
|
-
const { payload } = await jwtVerify(token, key, { algorithms: ["HS256"] });
|
|
56
|
-
return payload;
|
|
57
|
-
} catch {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (isStrictMode()) return null;
|
|
62
|
-
return decodeJwtUnverified(token);
|
|
63
|
-
}
|
|
64
|
-
function getUserIdFromRequest(request, options) {
|
|
65
|
-
const headerName = options?.headerName || "x-user-id";
|
|
66
|
-
return request.headers.get(headerName);
|
|
67
|
-
}
|
|
68
|
-
async function getUserEmailFromRequest(request, options) {
|
|
69
|
-
const token = extractBearerToken(request);
|
|
70
|
-
if (!token) return null;
|
|
71
|
-
const payload = await resolveJwtPayload(token, options?.jwtSecret);
|
|
72
|
-
if (!payload) return null;
|
|
73
|
-
return typeof payload.email === "string" ? payload.email : null;
|
|
74
|
-
}
|
|
75
|
-
async function getUserNameFromRequest(request, options) {
|
|
76
|
-
const token = extractBearerToken(request);
|
|
77
|
-
if (!token) return null;
|
|
78
|
-
const payload = await resolveJwtPayload(token, options?.jwtSecret);
|
|
79
|
-
if (!payload) return null;
|
|
80
|
-
const metadataFullName = typeof payload.user_metadata?.full_name === "string" ? payload.user_metadata.full_name : null;
|
|
81
|
-
const metadataName = typeof payload.user_metadata?.name === "string" ? payload.user_metadata.name : null;
|
|
82
|
-
const claimName = typeof payload.name === "string" ? payload.name : null;
|
|
83
|
-
return metadataFullName || metadataName || claimName || null;
|
|
84
|
-
}
|
|
85
|
-
function requireUserId(request, options) {
|
|
86
|
-
const headerName = options?.headerName || "x-user-id";
|
|
87
|
-
const errorMessage = options?.errorMessage || "Unauthorized";
|
|
88
|
-
const errorDetails = options?.errorDetails || "User ID not found. Ensure middleware is configured.";
|
|
89
|
-
const userId = request.headers.get(headerName);
|
|
90
|
-
if (!userId) {
|
|
91
|
-
return new Response(JSON.stringify({ error: errorMessage, details: errorDetails }), {
|
|
92
|
-
status: 401,
|
|
93
|
-
headers: { "Content-Type": "application/json" }
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
return userId;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export {
|
|
100
|
-
getUserIdFromRequest,
|
|
101
|
-
getUserEmailFromRequest,
|
|
102
|
-
getUserNameFromRequest,
|
|
103
|
-
requireUserId
|
|
104
|
-
};
|