@solvapay/auth 1.0.0-preview.10
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 +247 -0
- package/dist/adapter-4HTpALyc.d.cts +32 -0
- package/dist/adapter-4HTpALyc.d.ts +32 -0
- package/dist/chunk-2P77RLPB.js +77 -0
- package/dist/chunk-OETEFHW6.js +18 -0
- package/dist/index.cjs +133 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +16 -0
- package/dist/mock.cjs +42 -0
- package/dist/mock.d.cts +33 -0
- package/dist/mock.d.ts +33 -0
- package/dist/mock.js +6 -0
- package/dist/next-utils.cjs +114 -0
- package/dist/next-utils.d.cts +85 -0
- package/dist/next-utils.d.ts +85 -0
- package/dist/next-utils.js +12 -0
- package/dist/supabase.cjs +69 -0
- package/dist/supabase.d.cts +42 -0
- package/dist/supabase.d.ts +42 -0
- package/dist/supabase.js +34 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# @solvapay/auth
|
|
2
|
+
|
|
3
|
+
Authentication adapters for extracting user IDs from requests. Provides adapters for Supabase and mock/testing scenarios.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @solvapay/auth
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
If using SupabaseAuthAdapter, also install `jose`:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add jose
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### SupabaseAuthAdapter
|
|
20
|
+
|
|
21
|
+
Extract user ID from Supabase JWT tokens:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { SupabaseAuthAdapter } from '@solvapay/auth/supabase';
|
|
25
|
+
|
|
26
|
+
const auth = new SupabaseAuthAdapter({
|
|
27
|
+
jwtSecret: process.env.SUPABASE_JWT_SECRET!
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// In your API route
|
|
31
|
+
export async function POST(request: Request) {
|
|
32
|
+
const userId = await auth.getUserIdFromRequest(request);
|
|
33
|
+
|
|
34
|
+
if (!userId) {
|
|
35
|
+
return new Response('Unauthorized', { status: 401 });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Use userId as cache key and externalRef
|
|
39
|
+
// The ensureCustomer method returns the SolvaPay backend customer reference
|
|
40
|
+
const solvaPay = createSolvaPay({ apiKey: process.env.SOLVAPAY_SECRET_KEY! });
|
|
41
|
+
const ensuredCustomerRef = await solvaPay.ensureCustomer(userId, userId);
|
|
42
|
+
const customer = await solvaPay.getCustomer({ customerRef: ensuredCustomerRef });
|
|
43
|
+
|
|
44
|
+
return Response.json({ userId, customer });
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### MockAuthAdapter
|
|
49
|
+
|
|
50
|
+
For testing and examples:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { MockAuthAdapter } from '@solvapay/auth/mock';
|
|
54
|
+
|
|
55
|
+
const auth = new MockAuthAdapter();
|
|
56
|
+
|
|
57
|
+
// Set header: x-mock-user-id: user_123
|
|
58
|
+
// Or set env: MOCK_USER_ID=user_123
|
|
59
|
+
|
|
60
|
+
const userId = await auth.getUserIdFromRequest(request);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Integration with SolvaPay Server SDK
|
|
64
|
+
|
|
65
|
+
Use auth adapters with the `getCustomerRef` option:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { createSolvaPay } from '@solvapay/server';
|
|
69
|
+
import { SupabaseAuthAdapter } from '@solvapay/auth/supabase';
|
|
70
|
+
|
|
71
|
+
const auth = new SupabaseAuthAdapter({
|
|
72
|
+
jwtSecret: process.env.SUPABASE_JWT_SECRET!
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const solvaPay = createSolvaPay({ apiKey: process.env.SOLVAPAY_SECRET_KEY! });
|
|
76
|
+
|
|
77
|
+
// Use with Next.js adapter
|
|
78
|
+
export const POST = solvaPay.payable({ agent: 'my-api' }).next(
|
|
79
|
+
async (args) => {
|
|
80
|
+
return { result: 'success' };
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
getCustomerRef: async (req) => {
|
|
84
|
+
const userId = await auth.getUserIdFromRequest(req);
|
|
85
|
+
return userId ?? 'anonymous';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Next.js Route Utilities
|
|
92
|
+
|
|
93
|
+
The package also provides helper functions for extracting user information in Next.js API routes:
|
|
94
|
+
|
|
95
|
+
### getUserIdFromRequest
|
|
96
|
+
|
|
97
|
+
Extract user ID from request headers (commonly set by middleware):
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { getUserIdFromRequest } from '@solvapay/auth';
|
|
101
|
+
|
|
102
|
+
export async function GET(request: Request) {
|
|
103
|
+
const userId = getUserIdFromRequest(request);
|
|
104
|
+
|
|
105
|
+
if (!userId) {
|
|
106
|
+
return new Response('Unauthorized', { status: 401 });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Use userId...
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### requireUserId
|
|
114
|
+
|
|
115
|
+
Require user ID or return error response:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { requireUserId } from '@solvapay/auth';
|
|
119
|
+
|
|
120
|
+
export async function GET(request: Request) {
|
|
121
|
+
const userIdOrError = requireUserId(request);
|
|
122
|
+
|
|
123
|
+
if (userIdOrError instanceof Response) {
|
|
124
|
+
return userIdOrError; // Returns 401 error
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// userIdOrError is now a string
|
|
128
|
+
const userId = userIdOrError;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### getUserEmailFromRequest
|
|
133
|
+
|
|
134
|
+
Extract email from Supabase JWT token in Authorization header:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { getUserEmailFromRequest } from '@solvapay/auth';
|
|
138
|
+
|
|
139
|
+
export async function GET(request: Request) {
|
|
140
|
+
const email = await getUserEmailFromRequest(request);
|
|
141
|
+
// Returns email string or null
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### getUserNameFromRequest
|
|
146
|
+
|
|
147
|
+
Extract name from Supabase JWT token in Authorization header:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { getUserNameFromRequest } from '@solvapay/auth';
|
|
151
|
+
|
|
152
|
+
export async function GET(request: Request) {
|
|
153
|
+
const name = await getUserNameFromRequest(request);
|
|
154
|
+
// Returns name string or null
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Note:** `getUserEmailFromRequest` and `getUserNameFromRequest` require the `SUPABASE_JWT_SECRET` environment variable to be set, or you can pass it as an option:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
const email = await getUserEmailFromRequest(request, {
|
|
162
|
+
jwtSecret: process.env.SUPABASE_JWT_SECRET!
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## API Reference
|
|
167
|
+
|
|
168
|
+
### AuthAdapter Interface
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
interface AuthAdapter {
|
|
172
|
+
getUserIdFromRequest(req: Request | RequestLike): Promise<string | null>;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### SupabaseAuthAdapter
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
class SupabaseAuthAdapter implements AuthAdapter {
|
|
180
|
+
constructor(config: { jwtSecret: string });
|
|
181
|
+
getUserIdFromRequest(req: Request | RequestLike): Promise<string | null>;
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Configuration:**
|
|
186
|
+
- `jwtSecret`: Supabase JWT secret from dashboard (Settings → API → JWT Secret)
|
|
187
|
+
|
|
188
|
+
**Returns:** User ID from `sub` claim or `null` if missing/invalid
|
|
189
|
+
|
|
190
|
+
### MockAuthAdapter
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
class MockAuthAdapter implements AuthAdapter {
|
|
194
|
+
getUserIdFromRequest(req: Request | RequestLike): Promise<string | null>;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Behavior:**
|
|
199
|
+
1. Checks `x-mock-user-id` header
|
|
200
|
+
2. Falls back to `MOCK_USER_ID` environment variable
|
|
201
|
+
3. Returns `null` if neither is found
|
|
202
|
+
|
|
203
|
+
## Requirements
|
|
204
|
+
|
|
205
|
+
- **Node.js**: >=18.17
|
|
206
|
+
- **jose**: >=5.0.0 (peer dependency, required only for SupabaseAuthAdapter)
|
|
207
|
+
|
|
208
|
+
## Edge Runtime Support
|
|
209
|
+
|
|
210
|
+
Both adapters and Next.js utilities work in Edge runtimes (Vercel Edge Functions, Cloudflare Workers, etc.). SupabaseAuthAdapter and JWT utilities use dynamic imports for `jose` to ensure Edge compatibility.
|
|
211
|
+
|
|
212
|
+
## Next.js Integration
|
|
213
|
+
|
|
214
|
+
These utilities work seamlessly with Next.js middleware. Typically, you'll set the `x-user-id` header in your middleware after authentication:
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
// middleware.ts
|
|
218
|
+
import { NextResponse } from 'next/server';
|
|
219
|
+
import type { NextRequest } from 'next/server';
|
|
220
|
+
|
|
221
|
+
export async function middleware(request: NextRequest) {
|
|
222
|
+
// Extract user ID from your auth system (Supabase, Auth0, etc.)
|
|
223
|
+
const userId = await getUserIdFromAuth(request);
|
|
224
|
+
|
|
225
|
+
// Clone the request and add user ID header
|
|
226
|
+
const requestHeaders = new Headers(request.headers);
|
|
227
|
+
requestHeaders.set('x-user-id', userId);
|
|
228
|
+
|
|
229
|
+
return NextResponse.next({
|
|
230
|
+
request: {
|
|
231
|
+
headers: requestHeaders,
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Then in your API routes, use the utilities:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { requireUserId } from '@solvapay/auth';
|
|
241
|
+
|
|
242
|
+
export async function GET(request: Request) {
|
|
243
|
+
const userId = requireUserId(request);
|
|
244
|
+
// userId is guaranteed to exist or an error response is returned
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Adapter Interface
|
|
3
|
+
*
|
|
4
|
+
* Defines the contract for authentication adapters that extract user IDs from requests.
|
|
5
|
+
* Implementations should never throw - return null if authentication fails or is missing.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Request-like object that has headers
|
|
9
|
+
*/
|
|
10
|
+
interface RequestLike {
|
|
11
|
+
headers: Headers | {
|
|
12
|
+
get(name: string): string | null;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Auth adapter interface for extracting user IDs from requests
|
|
17
|
+
*/
|
|
18
|
+
interface AuthAdapter {
|
|
19
|
+
/**
|
|
20
|
+
* Extract the authenticated user ID from a request
|
|
21
|
+
*
|
|
22
|
+
* @param req - Request object or object with headers
|
|
23
|
+
* @returns The user ID string if authenticated, null otherwise
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* This method should never throw. If authentication fails or is missing,
|
|
27
|
+
* return null and let the caller decide how to handle unauthenticated requests.
|
|
28
|
+
*/
|
|
29
|
+
getUserIdFromRequest(req: Request | RequestLike): Promise<string | null>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type { AuthAdapter as A, RequestLike as R };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Adapter Interface
|
|
3
|
+
*
|
|
4
|
+
* Defines the contract for authentication adapters that extract user IDs from requests.
|
|
5
|
+
* Implementations should never throw - return null if authentication fails or is missing.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Request-like object that has headers
|
|
9
|
+
*/
|
|
10
|
+
interface RequestLike {
|
|
11
|
+
headers: Headers | {
|
|
12
|
+
get(name: string): string | null;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Auth adapter interface for extracting user IDs from requests
|
|
17
|
+
*/
|
|
18
|
+
interface AuthAdapter {
|
|
19
|
+
/**
|
|
20
|
+
* Extract the authenticated user ID from a request
|
|
21
|
+
*
|
|
22
|
+
* @param req - Request object or object with headers
|
|
23
|
+
* @returns The user ID string if authenticated, null otherwise
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* This method should never throw. If authentication fails or is missing,
|
|
27
|
+
* return null and let the caller decide how to handle unauthenticated requests.
|
|
28
|
+
*/
|
|
29
|
+
getUserIdFromRequest(req: Request | RequestLike): Promise<string | null>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type { AuthAdapter as A, RequestLike as R };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// src/next-utils.ts
|
|
2
|
+
function getUserIdFromRequest(request, options) {
|
|
3
|
+
const headerName = options?.headerName || "x-user-id";
|
|
4
|
+
return request.headers.get(headerName);
|
|
5
|
+
}
|
|
6
|
+
async function getUserEmailFromRequest(request, options) {
|
|
7
|
+
const authHeader = request.headers.get("authorization");
|
|
8
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const token = authHeader.slice(7);
|
|
12
|
+
if (!token) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const { jwtVerify } = await import("jose");
|
|
17
|
+
const jwtSecret = options?.jwtSecret || process.env.SUPABASE_JWT_SECRET;
|
|
18
|
+
if (!jwtSecret) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const secret = new TextEncoder().encode(jwtSecret);
|
|
22
|
+
const { payload } = await jwtVerify(token, secret, {
|
|
23
|
+
algorithms: ["HS256"]
|
|
24
|
+
});
|
|
25
|
+
return payload.email ? String(payload.email) : null;
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function getUserNameFromRequest(request, options) {
|
|
31
|
+
const authHeader = request.headers.get("authorization");
|
|
32
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const token = authHeader.slice(7);
|
|
36
|
+
if (!token) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const { jwtVerify } = await import("jose");
|
|
41
|
+
const jwtSecret = options?.jwtSecret || process.env.SUPABASE_JWT_SECRET;
|
|
42
|
+
if (!jwtSecret) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const secret = new TextEncoder().encode(jwtSecret);
|
|
46
|
+
const { payload } = await jwtVerify(token, secret, {
|
|
47
|
+
algorithms: ["HS256"]
|
|
48
|
+
});
|
|
49
|
+
const name = payload.user_metadata?.full_name || payload.user_metadata?.name || payload.name || null;
|
|
50
|
+
return name ? String(name) : null;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function requireUserId(request, options) {
|
|
56
|
+
const headerName = options?.headerName || "x-user-id";
|
|
57
|
+
const errorMessage = options?.errorMessage || "Unauthorized";
|
|
58
|
+
const errorDetails = options?.errorDetails || "User ID not found. Ensure middleware is configured.";
|
|
59
|
+
const userId = request.headers.get(headerName);
|
|
60
|
+
if (!userId) {
|
|
61
|
+
return new Response(
|
|
62
|
+
JSON.stringify({ error: errorMessage, details: errorDetails }),
|
|
63
|
+
{
|
|
64
|
+
status: 401,
|
|
65
|
+
headers: { "Content-Type": "application/json" }
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return userId;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
getUserIdFromRequest,
|
|
74
|
+
getUserEmailFromRequest,
|
|
75
|
+
getUserNameFromRequest,
|
|
76
|
+
requireUserId
|
|
77
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/mock.ts
|
|
2
|
+
var MockAuthAdapter = class {
|
|
3
|
+
async getUserIdFromRequest(req) {
|
|
4
|
+
const headers = "headers" in req ? req.headers : req;
|
|
5
|
+
const headerUserId = headers.get("x-mock-user-id");
|
|
6
|
+
if (headerUserId) {
|
|
7
|
+
return headerUserId;
|
|
8
|
+
}
|
|
9
|
+
if (typeof process !== "undefined" && process.env.MOCK_USER_ID) {
|
|
10
|
+
return process.env.MOCK_USER_ID;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
MockAuthAdapter
|
|
18
|
+
};
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
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
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
MockAuthAdapter: () => MockAuthAdapter,
|
|
34
|
+
getUserEmailFromRequest: () => getUserEmailFromRequest,
|
|
35
|
+
getUserIdFromRequest: () => getUserIdFromRequest,
|
|
36
|
+
getUserNameFromRequest: () => getUserNameFromRequest,
|
|
37
|
+
requireUserId: () => requireUserId
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/mock.ts
|
|
42
|
+
var MockAuthAdapter = class {
|
|
43
|
+
async getUserIdFromRequest(req) {
|
|
44
|
+
const headers = "headers" in req ? req.headers : req;
|
|
45
|
+
const headerUserId = headers.get("x-mock-user-id");
|
|
46
|
+
if (headerUserId) {
|
|
47
|
+
return headerUserId;
|
|
48
|
+
}
|
|
49
|
+
if (typeof process !== "undefined" && process.env.MOCK_USER_ID) {
|
|
50
|
+
return process.env.MOCK_USER_ID;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// src/next-utils.ts
|
|
57
|
+
function getUserIdFromRequest(request, options) {
|
|
58
|
+
const headerName = options?.headerName || "x-user-id";
|
|
59
|
+
return request.headers.get(headerName);
|
|
60
|
+
}
|
|
61
|
+
async function getUserEmailFromRequest(request, options) {
|
|
62
|
+
const authHeader = request.headers.get("authorization");
|
|
63
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const token = authHeader.slice(7);
|
|
67
|
+
if (!token) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const { jwtVerify } = await import("jose");
|
|
72
|
+
const jwtSecret = options?.jwtSecret || process.env.SUPABASE_JWT_SECRET;
|
|
73
|
+
if (!jwtSecret) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const secret = new TextEncoder().encode(jwtSecret);
|
|
77
|
+
const { payload } = await jwtVerify(token, secret, {
|
|
78
|
+
algorithms: ["HS256"]
|
|
79
|
+
});
|
|
80
|
+
return payload.email ? String(payload.email) : null;
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function getUserNameFromRequest(request, options) {
|
|
86
|
+
const authHeader = request.headers.get("authorization");
|
|
87
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const token = authHeader.slice(7);
|
|
91
|
+
if (!token) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const { jwtVerify } = await import("jose");
|
|
96
|
+
const jwtSecret = options?.jwtSecret || process.env.SUPABASE_JWT_SECRET;
|
|
97
|
+
if (!jwtSecret) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const secret = new TextEncoder().encode(jwtSecret);
|
|
101
|
+
const { payload } = await jwtVerify(token, secret, {
|
|
102
|
+
algorithms: ["HS256"]
|
|
103
|
+
});
|
|
104
|
+
const name = payload.user_metadata?.full_name || payload.user_metadata?.name || payload.name || null;
|
|
105
|
+
return name ? String(name) : null;
|
|
106
|
+
} catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function requireUserId(request, options) {
|
|
111
|
+
const headerName = options?.headerName || "x-user-id";
|
|
112
|
+
const errorMessage = options?.errorMessage || "Unauthorized";
|
|
113
|
+
const errorDetails = options?.errorDetails || "User ID not found. Ensure middleware is configured.";
|
|
114
|
+
const userId = request.headers.get(headerName);
|
|
115
|
+
if (!userId) {
|
|
116
|
+
return new Response(
|
|
117
|
+
JSON.stringify({ error: errorMessage, details: errorDetails }),
|
|
118
|
+
{
|
|
119
|
+
status: 401,
|
|
120
|
+
headers: { "Content-Type": "application/json" }
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return userId;
|
|
125
|
+
}
|
|
126
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
127
|
+
0 && (module.exports = {
|
|
128
|
+
MockAuthAdapter,
|
|
129
|
+
getUserEmailFromRequest,
|
|
130
|
+
getUserIdFromRequest,
|
|
131
|
+
getUserNameFromRequest,
|
|
132
|
+
requireUserId
|
|
133
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { A as AuthAdapter, R as RequestLike } from './adapter-4HTpALyc.cjs';
|
|
2
|
+
export { MockAuthAdapter, MockAuthAdapter as MockAuthAdapterType } from './mock.cjs';
|
|
3
|
+
export { getUserEmailFromRequest, getUserIdFromRequest, getUserNameFromRequest, requireUserId } from './next-utils.cjs';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { A as AuthAdapter, R as RequestLike } from './adapter-4HTpALyc.js';
|
|
2
|
+
export { MockAuthAdapter, MockAuthAdapter as MockAuthAdapterType } from './mock.js';
|
|
3
|
+
export { getUserEmailFromRequest, getUserIdFromRequest, getUserNameFromRequest, requireUserId } from './next-utils.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MockAuthAdapter
|
|
3
|
+
} from "./chunk-OETEFHW6.js";
|
|
4
|
+
import {
|
|
5
|
+
getUserEmailFromRequest,
|
|
6
|
+
getUserIdFromRequest,
|
|
7
|
+
getUserNameFromRequest,
|
|
8
|
+
requireUserId
|
|
9
|
+
} from "./chunk-2P77RLPB.js";
|
|
10
|
+
export {
|
|
11
|
+
MockAuthAdapter,
|
|
12
|
+
getUserEmailFromRequest,
|
|
13
|
+
getUserIdFromRequest,
|
|
14
|
+
getUserNameFromRequest,
|
|
15
|
+
requireUserId
|
|
16
|
+
};
|
package/dist/mock.cjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/mock.ts
|
|
21
|
+
var mock_exports = {};
|
|
22
|
+
__export(mock_exports, {
|
|
23
|
+
MockAuthAdapter: () => MockAuthAdapter
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(mock_exports);
|
|
26
|
+
var MockAuthAdapter = class {
|
|
27
|
+
async getUserIdFromRequest(req) {
|
|
28
|
+
const headers = "headers" in req ? req.headers : req;
|
|
29
|
+
const headerUserId = headers.get("x-mock-user-id");
|
|
30
|
+
if (headerUserId) {
|
|
31
|
+
return headerUserId;
|
|
32
|
+
}
|
|
33
|
+
if (typeof process !== "undefined" && process.env.MOCK_USER_ID) {
|
|
34
|
+
return process.env.MOCK_USER_ID;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
40
|
+
0 && (module.exports = {
|
|
41
|
+
MockAuthAdapter
|
|
42
|
+
});
|
package/dist/mock.d.cts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { A as AuthAdapter, R as RequestLike } from './adapter-4HTpALyc.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mock Auth Adapter
|
|
5
|
+
*
|
|
6
|
+
* Simple adapter for testing and examples that extracts user ID from headers
|
|
7
|
+
* or environment variables. No dependencies required.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Mock authentication adapter
|
|
12
|
+
*
|
|
13
|
+
* Extracts user ID from `x-mock-user-id` header or `MOCK_USER_ID` environment variable.
|
|
14
|
+
* Useful for testing, examples, and development where real authentication isn't needed.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { MockAuthAdapter } from '@solvapay/auth/mock';
|
|
19
|
+
*
|
|
20
|
+
* const auth = new MockAuthAdapter();
|
|
21
|
+
*
|
|
22
|
+
* // In tests or examples:
|
|
23
|
+
* // Set header: x-mock-user-id: user_123
|
|
24
|
+
* // Or set env: MOCK_USER_ID=user_123
|
|
25
|
+
*
|
|
26
|
+
* const userId = await auth.getUserIdFromRequest(request);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
declare class MockAuthAdapter implements AuthAdapter {
|
|
30
|
+
getUserIdFromRequest(req: Request | RequestLike): Promise<string | null>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { MockAuthAdapter };
|
package/dist/mock.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { A as AuthAdapter, R as RequestLike } from './adapter-4HTpALyc.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mock Auth Adapter
|
|
5
|
+
*
|
|
6
|
+
* Simple adapter for testing and examples that extracts user ID from headers
|
|
7
|
+
* or environment variables. No dependencies required.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Mock authentication adapter
|
|
12
|
+
*
|
|
13
|
+
* Extracts user ID from `x-mock-user-id` header or `MOCK_USER_ID` environment variable.
|
|
14
|
+
* Useful for testing, examples, and development where real authentication isn't needed.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { MockAuthAdapter } from '@solvapay/auth/mock';
|
|
19
|
+
*
|
|
20
|
+
* const auth = new MockAuthAdapter();
|
|
21
|
+
*
|
|
22
|
+
* // In tests or examples:
|
|
23
|
+
* // Set header: x-mock-user-id: user_123
|
|
24
|
+
* // Or set env: MOCK_USER_ID=user_123
|
|
25
|
+
*
|
|
26
|
+
* const userId = await auth.getUserIdFromRequest(request);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
declare class MockAuthAdapter implements AuthAdapter {
|
|
30
|
+
getUserIdFromRequest(req: Request | RequestLike): Promise<string | null>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { MockAuthAdapter };
|
package/dist/mock.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
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
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/next-utils.ts
|
|
31
|
+
var next_utils_exports = {};
|
|
32
|
+
__export(next_utils_exports, {
|
|
33
|
+
getUserEmailFromRequest: () => getUserEmailFromRequest,
|
|
34
|
+
getUserIdFromRequest: () => getUserIdFromRequest,
|
|
35
|
+
getUserNameFromRequest: () => getUserNameFromRequest,
|
|
36
|
+
requireUserId: () => requireUserId
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(next_utils_exports);
|
|
39
|
+
function getUserIdFromRequest(request, options) {
|
|
40
|
+
const headerName = options?.headerName || "x-user-id";
|
|
41
|
+
return request.headers.get(headerName);
|
|
42
|
+
}
|
|
43
|
+
async function getUserEmailFromRequest(request, options) {
|
|
44
|
+
const authHeader = request.headers.get("authorization");
|
|
45
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const token = authHeader.slice(7);
|
|
49
|
+
if (!token) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const { jwtVerify } = await import("jose");
|
|
54
|
+
const jwtSecret = options?.jwtSecret || process.env.SUPABASE_JWT_SECRET;
|
|
55
|
+
if (!jwtSecret) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const secret = new TextEncoder().encode(jwtSecret);
|
|
59
|
+
const { payload } = await jwtVerify(token, secret, {
|
|
60
|
+
algorithms: ["HS256"]
|
|
61
|
+
});
|
|
62
|
+
return payload.email ? String(payload.email) : null;
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function getUserNameFromRequest(request, options) {
|
|
68
|
+
const authHeader = request.headers.get("authorization");
|
|
69
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const token = authHeader.slice(7);
|
|
73
|
+
if (!token) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const { jwtVerify } = await import("jose");
|
|
78
|
+
const jwtSecret = options?.jwtSecret || process.env.SUPABASE_JWT_SECRET;
|
|
79
|
+
if (!jwtSecret) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const secret = new TextEncoder().encode(jwtSecret);
|
|
83
|
+
const { payload } = await jwtVerify(token, secret, {
|
|
84
|
+
algorithms: ["HS256"]
|
|
85
|
+
});
|
|
86
|
+
const name = payload.user_metadata?.full_name || payload.user_metadata?.name || payload.name || null;
|
|
87
|
+
return name ? String(name) : null;
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function requireUserId(request, options) {
|
|
93
|
+
const headerName = options?.headerName || "x-user-id";
|
|
94
|
+
const errorMessage = options?.errorMessage || "Unauthorized";
|
|
95
|
+
const errorDetails = options?.errorDetails || "User ID not found. Ensure middleware is configured.";
|
|
96
|
+
const userId = request.headers.get(headerName);
|
|
97
|
+
if (!userId) {
|
|
98
|
+
return new Response(
|
|
99
|
+
JSON.stringify({ error: errorMessage, details: errorDetails }),
|
|
100
|
+
{
|
|
101
|
+
status: 401,
|
|
102
|
+
headers: { "Content-Type": "application/json" }
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return userId;
|
|
107
|
+
}
|
|
108
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
109
|
+
0 && (module.exports = {
|
|
110
|
+
getUserEmailFromRequest,
|
|
111
|
+
getUserIdFromRequest,
|
|
112
|
+
getUserNameFromRequest,
|
|
113
|
+
requireUserId
|
|
114
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js Route Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for common patterns in Next.js API routes.
|
|
5
|
+
* These utilities work with authenticated requests where user IDs
|
|
6
|
+
* have been extracted and set in headers (e.g., by middleware).
|
|
7
|
+
*
|
|
8
|
+
* Note: These functions work with the standard web Request API
|
|
9
|
+
* and can be used with Next.js NextRequest (which extends Request)
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Extract user ID from request headers.
|
|
13
|
+
* Checks for 'x-user-id' header (commonly set by middleware after authentication).
|
|
14
|
+
*
|
|
15
|
+
* @param request - Request object (works with NextRequest from next/server)
|
|
16
|
+
* @param options - Configuration options
|
|
17
|
+
* @returns User ID string or null if not found
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { NextRequest, NextResponse } from 'next/server';
|
|
22
|
+
* import { getUserIdFromRequest } from '@solvapay/auth';
|
|
23
|
+
*
|
|
24
|
+
* const userId = getUserIdFromRequest(request);
|
|
25
|
+
* if (!userId) {
|
|
26
|
+
* return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
declare function getUserIdFromRequest(request: Request, options?: {
|
|
31
|
+
headerName?: string;
|
|
32
|
+
}): string | null;
|
|
33
|
+
/**
|
|
34
|
+
* Extract user email from Supabase JWT token in Authorization header.
|
|
35
|
+
* Returns null if token is missing or invalid.
|
|
36
|
+
*
|
|
37
|
+
* @param request - Request object (works with NextRequest from next/server)
|
|
38
|
+
* @param options - Configuration options
|
|
39
|
+
* @returns User email string or null if not found
|
|
40
|
+
*/
|
|
41
|
+
declare function getUserEmailFromRequest(request: Request, options?: {
|
|
42
|
+
jwtSecret?: string;
|
|
43
|
+
}): Promise<string | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Extract user name from Supabase JWT token in Authorization header.
|
|
46
|
+
* Returns null if token is missing or invalid.
|
|
47
|
+
*
|
|
48
|
+
* @param request - Request object (works with NextRequest from next/server)
|
|
49
|
+
* @param options - Configuration options
|
|
50
|
+
* @returns User name string or null if not found
|
|
51
|
+
*/
|
|
52
|
+
declare function getUserNameFromRequest(request: Request, options?: {
|
|
53
|
+
jwtSecret?: string;
|
|
54
|
+
}): Promise<string | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Require user ID from request headers.
|
|
57
|
+
* Returns an error response if user ID is not found.
|
|
58
|
+
*
|
|
59
|
+
* This function returns a standard Response object that works with Next.js.
|
|
60
|
+
* In Next.js, NextResponse extends Response, so this is compatible.
|
|
61
|
+
*
|
|
62
|
+
* @param request - Request object (works with NextRequest from next/server)
|
|
63
|
+
* @param options - Configuration options
|
|
64
|
+
* @returns Either the user ID string or a Response error object
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* import { NextRequest, NextResponse } from 'next/server';
|
|
69
|
+
* import { requireUserId } from '@solvapay/auth';
|
|
70
|
+
*
|
|
71
|
+
* const userIdOrError = requireUserId(request);
|
|
72
|
+
* if (userIdOrError instanceof Response) {
|
|
73
|
+
* return userIdOrError; // Returns 401 error
|
|
74
|
+
* }
|
|
75
|
+
* // userIdOrError is now a string
|
|
76
|
+
* const userId = userIdOrError;
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
declare function requireUserId(request: Request, options?: {
|
|
80
|
+
headerName?: string;
|
|
81
|
+
errorMessage?: string;
|
|
82
|
+
errorDetails?: string;
|
|
83
|
+
}): string | Response;
|
|
84
|
+
|
|
85
|
+
export { getUserEmailFromRequest, getUserIdFromRequest, getUserNameFromRequest, requireUserId };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js Route Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for common patterns in Next.js API routes.
|
|
5
|
+
* These utilities work with authenticated requests where user IDs
|
|
6
|
+
* have been extracted and set in headers (e.g., by middleware).
|
|
7
|
+
*
|
|
8
|
+
* Note: These functions work with the standard web Request API
|
|
9
|
+
* and can be used with Next.js NextRequest (which extends Request)
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Extract user ID from request headers.
|
|
13
|
+
* Checks for 'x-user-id' header (commonly set by middleware after authentication).
|
|
14
|
+
*
|
|
15
|
+
* @param request - Request object (works with NextRequest from next/server)
|
|
16
|
+
* @param options - Configuration options
|
|
17
|
+
* @returns User ID string or null if not found
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { NextRequest, NextResponse } from 'next/server';
|
|
22
|
+
* import { getUserIdFromRequest } from '@solvapay/auth';
|
|
23
|
+
*
|
|
24
|
+
* const userId = getUserIdFromRequest(request);
|
|
25
|
+
* if (!userId) {
|
|
26
|
+
* return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
declare function getUserIdFromRequest(request: Request, options?: {
|
|
31
|
+
headerName?: string;
|
|
32
|
+
}): string | null;
|
|
33
|
+
/**
|
|
34
|
+
* Extract user email from Supabase JWT token in Authorization header.
|
|
35
|
+
* Returns null if token is missing or invalid.
|
|
36
|
+
*
|
|
37
|
+
* @param request - Request object (works with NextRequest from next/server)
|
|
38
|
+
* @param options - Configuration options
|
|
39
|
+
* @returns User email string or null if not found
|
|
40
|
+
*/
|
|
41
|
+
declare function getUserEmailFromRequest(request: Request, options?: {
|
|
42
|
+
jwtSecret?: string;
|
|
43
|
+
}): Promise<string | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Extract user name from Supabase JWT token in Authorization header.
|
|
46
|
+
* Returns null if token is missing or invalid.
|
|
47
|
+
*
|
|
48
|
+
* @param request - Request object (works with NextRequest from next/server)
|
|
49
|
+
* @param options - Configuration options
|
|
50
|
+
* @returns User name string or null if not found
|
|
51
|
+
*/
|
|
52
|
+
declare function getUserNameFromRequest(request: Request, options?: {
|
|
53
|
+
jwtSecret?: string;
|
|
54
|
+
}): Promise<string | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Require user ID from request headers.
|
|
57
|
+
* Returns an error response if user ID is not found.
|
|
58
|
+
*
|
|
59
|
+
* This function returns a standard Response object that works with Next.js.
|
|
60
|
+
* In Next.js, NextResponse extends Response, so this is compatible.
|
|
61
|
+
*
|
|
62
|
+
* @param request - Request object (works with NextRequest from next/server)
|
|
63
|
+
* @param options - Configuration options
|
|
64
|
+
* @returns Either the user ID string or a Response error object
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* import { NextRequest, NextResponse } from 'next/server';
|
|
69
|
+
* import { requireUserId } from '@solvapay/auth';
|
|
70
|
+
*
|
|
71
|
+
* const userIdOrError = requireUserId(request);
|
|
72
|
+
* if (userIdOrError instanceof Response) {
|
|
73
|
+
* return userIdOrError; // Returns 401 error
|
|
74
|
+
* }
|
|
75
|
+
* // userIdOrError is now a string
|
|
76
|
+
* const userId = userIdOrError;
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
declare function requireUserId(request: Request, options?: {
|
|
80
|
+
headerName?: string;
|
|
81
|
+
errorMessage?: string;
|
|
82
|
+
errorDetails?: string;
|
|
83
|
+
}): string | Response;
|
|
84
|
+
|
|
85
|
+
export { getUserEmailFromRequest, getUserIdFromRequest, getUserNameFromRequest, requireUserId };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
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
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/supabase.ts
|
|
31
|
+
var supabase_exports = {};
|
|
32
|
+
__export(supabase_exports, {
|
|
33
|
+
SupabaseAuthAdapter: () => SupabaseAuthAdapter
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(supabase_exports);
|
|
36
|
+
var SupabaseAuthAdapter = class {
|
|
37
|
+
jwtSecret;
|
|
38
|
+
constructor(config) {
|
|
39
|
+
if (!config.jwtSecret) {
|
|
40
|
+
throw new Error("SupabaseAuthAdapter requires jwtSecret in config");
|
|
41
|
+
}
|
|
42
|
+
this.jwtSecret = new TextEncoder().encode(config.jwtSecret);
|
|
43
|
+
}
|
|
44
|
+
async getUserIdFromRequest(req) {
|
|
45
|
+
try {
|
|
46
|
+
const headers = "headers" in req ? req.headers : req;
|
|
47
|
+
const authHeader = headers.get("authorization");
|
|
48
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const token = authHeader.slice(7);
|
|
52
|
+
if (!token) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const { jwtVerify } = await import("jose");
|
|
56
|
+
const { payload } = await jwtVerify(token, this.jwtSecret, {
|
|
57
|
+
algorithms: ["HS256"]
|
|
58
|
+
});
|
|
59
|
+
const userId = payload.sub ? String(payload.sub) : null;
|
|
60
|
+
return userId;
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
67
|
+
0 && (module.exports = {
|
|
68
|
+
SupabaseAuthAdapter
|
|
69
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { A as AuthAdapter, R as RequestLike } from './adapter-4HTpALyc.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Supabase Auth Adapter
|
|
5
|
+
*
|
|
6
|
+
* Extracts user ID from Supabase JWT tokens using HS256 verification.
|
|
7
|
+
* Works in both Node.js and Edge runtimes (uses dynamic import for jose).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface SupabaseAuthAdapterConfig {
|
|
11
|
+
/**
|
|
12
|
+
* Supabase JWT secret (from Supabase dashboard: Settings → API → JWT Secret)
|
|
13
|
+
*/
|
|
14
|
+
jwtSecret: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Supabase authentication adapter
|
|
18
|
+
*
|
|
19
|
+
* Verifies Supabase JWT tokens and extracts the user ID from the `sub` claim.
|
|
20
|
+
* Uses dynamic import for jose to keep the package lean and Edge-compatible.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { SupabaseAuthAdapter } from '@solvapay/auth/supabase';
|
|
25
|
+
*
|
|
26
|
+
* const auth = new SupabaseAuthAdapter({
|
|
27
|
+
* jwtSecret: process.env.SUPABASE_JWT_SECRET!
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* const userId = await auth.getUserIdFromRequest(request);
|
|
31
|
+
* if (!userId) {
|
|
32
|
+
* return new Response('Unauthorized', { status: 401 });
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare class SupabaseAuthAdapter implements AuthAdapter {
|
|
37
|
+
private jwtSecret;
|
|
38
|
+
constructor(config: SupabaseAuthAdapterConfig);
|
|
39
|
+
getUserIdFromRequest(req: Request | RequestLike): Promise<string | null>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { SupabaseAuthAdapter, type SupabaseAuthAdapterConfig };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { A as AuthAdapter, R as RequestLike } from './adapter-4HTpALyc.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Supabase Auth Adapter
|
|
5
|
+
*
|
|
6
|
+
* Extracts user ID from Supabase JWT tokens using HS256 verification.
|
|
7
|
+
* Works in both Node.js and Edge runtimes (uses dynamic import for jose).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface SupabaseAuthAdapterConfig {
|
|
11
|
+
/**
|
|
12
|
+
* Supabase JWT secret (from Supabase dashboard: Settings → API → JWT Secret)
|
|
13
|
+
*/
|
|
14
|
+
jwtSecret: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Supabase authentication adapter
|
|
18
|
+
*
|
|
19
|
+
* Verifies Supabase JWT tokens and extracts the user ID from the `sub` claim.
|
|
20
|
+
* Uses dynamic import for jose to keep the package lean and Edge-compatible.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { SupabaseAuthAdapter } from '@solvapay/auth/supabase';
|
|
25
|
+
*
|
|
26
|
+
* const auth = new SupabaseAuthAdapter({
|
|
27
|
+
* jwtSecret: process.env.SUPABASE_JWT_SECRET!
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* const userId = await auth.getUserIdFromRequest(request);
|
|
31
|
+
* if (!userId) {
|
|
32
|
+
* return new Response('Unauthorized', { status: 401 });
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare class SupabaseAuthAdapter implements AuthAdapter {
|
|
37
|
+
private jwtSecret;
|
|
38
|
+
constructor(config: SupabaseAuthAdapterConfig);
|
|
39
|
+
getUserIdFromRequest(req: Request | RequestLike): Promise<string | null>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { SupabaseAuthAdapter, type SupabaseAuthAdapterConfig };
|
package/dist/supabase.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/supabase.ts
|
|
2
|
+
var SupabaseAuthAdapter = class {
|
|
3
|
+
jwtSecret;
|
|
4
|
+
constructor(config) {
|
|
5
|
+
if (!config.jwtSecret) {
|
|
6
|
+
throw new Error("SupabaseAuthAdapter requires jwtSecret in config");
|
|
7
|
+
}
|
|
8
|
+
this.jwtSecret = new TextEncoder().encode(config.jwtSecret);
|
|
9
|
+
}
|
|
10
|
+
async getUserIdFromRequest(req) {
|
|
11
|
+
try {
|
|
12
|
+
const headers = "headers" in req ? req.headers : req;
|
|
13
|
+
const authHeader = headers.get("authorization");
|
|
14
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const token = authHeader.slice(7);
|
|
18
|
+
if (!token) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const { jwtVerify } = await import("jose");
|
|
22
|
+
const { payload } = await jwtVerify(token, this.jwtSecret, {
|
|
23
|
+
algorithms: ["HS256"]
|
|
24
|
+
});
|
|
25
|
+
const userId = payload.sub ? String(payload.sub) : null;
|
|
26
|
+
return userId;
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
export {
|
|
33
|
+
SupabaseAuthAdapter
|
|
34
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@solvapay/auth",
|
|
3
|
+
"version": "1.0.0-preview.10",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
},
|
|
14
|
+
"./supabase": {
|
|
15
|
+
"types": "./dist/supabase.d.ts",
|
|
16
|
+
"import": "./dist/supabase.js",
|
|
17
|
+
"require": "./dist/supabase.cjs"
|
|
18
|
+
},
|
|
19
|
+
"./mock": {
|
|
20
|
+
"types": "./dist/mock.d.ts",
|
|
21
|
+
"import": "./dist/mock.js",
|
|
22
|
+
"require": "./dist/mock.cjs"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/solvapay/solvapay-sdk.git",
|
|
35
|
+
"directory": "packages/auth"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.17"
|
|
39
|
+
},
|
|
40
|
+
"sideEffects": false,
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"jose": "^5.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"tsup": "^8.0.1",
|
|
46
|
+
"typescript": "^5.5.4",
|
|
47
|
+
"vitest": "^2.0.5"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsup src/index.ts src/supabase.ts src/mock.ts src/next-utils.ts --format esm,cjs --dts --tsconfig tsconfig.build.json",
|
|
51
|
+
"test": "vitest run || exit 0",
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"lint": "echo 'no lint yet'"
|
|
54
|
+
}
|
|
55
|
+
}
|