@parsrun/auth 0.1.0
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 +133 -0
- package/dist/adapters/hono.d.ts +9 -0
- package/dist/adapters/hono.js +6 -0
- package/dist/adapters/hono.js.map +1 -0
- package/dist/adapters/index.d.ts +9 -0
- package/dist/adapters/index.js +7 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/authorization-By1Xp8Za.d.ts +213 -0
- package/dist/base-BKyR8rcE.d.ts +646 -0
- package/dist/chunk-42MGHABB.js +263 -0
- package/dist/chunk-42MGHABB.js.map +1 -0
- package/dist/chunk-7GOBAL4G.js +3 -0
- package/dist/chunk-7GOBAL4G.js.map +1 -0
- package/dist/chunk-G5I3T73A.js +152 -0
- package/dist/chunk-G5I3T73A.js.map +1 -0
- package/dist/chunk-IB4WUQDZ.js +410 -0
- package/dist/chunk-IB4WUQDZ.js.map +1 -0
- package/dist/chunk-MOG4Y6I7.js +415 -0
- package/dist/chunk-MOG4Y6I7.js.map +1 -0
- package/dist/chunk-NK4TJV2W.js +295 -0
- package/dist/chunk-NK4TJV2W.js.map +1 -0
- package/dist/chunk-RHNVRCF3.js +838 -0
- package/dist/chunk-RHNVRCF3.js.map +1 -0
- package/dist/chunk-YTCPXJR5.js +570 -0
- package/dist/chunk-YTCPXJR5.js.map +1 -0
- package/dist/cloudflare-kv-L64CZKDK.js +105 -0
- package/dist/cloudflare-kv-L64CZKDK.js.map +1 -0
- package/dist/deno-kv-F55HKKP6.js +111 -0
- package/dist/deno-kv-F55HKKP6.js.map +1 -0
- package/dist/index-C3kz9XqE.d.ts +226 -0
- package/dist/index-DOGcetyD.d.ts +1041 -0
- package/dist/index.d.ts +1579 -0
- package/dist/index.js +4294 -0
- package/dist/index.js.map +1 -0
- package/dist/jwt-manager-CH8H0kmm.d.ts +182 -0
- package/dist/providers/index.d.ts +90 -0
- package/dist/providers/index.js +3 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/otp/index.d.ts +3 -0
- package/dist/providers/otp/index.js +4 -0
- package/dist/providers/otp/index.js.map +1 -0
- package/dist/redis-5TIS6XCA.js +121 -0
- package/dist/redis-5TIS6XCA.js.map +1 -0
- package/dist/security/index.d.ts +301 -0
- package/dist/security/index.js +5 -0
- package/dist/security/index.js.map +1 -0
- package/dist/session/index.d.ts +117 -0
- package/dist/session/index.js +4 -0
- package/dist/session/index.js.map +1 -0
- package/dist/storage/index.d.ts +97 -0
- package/dist/storage/index.js +3 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/types-DSjafxJ4.d.ts +193 -0
- package/package.json +102 -0
|
@@ -0,0 +1,838 @@
|
|
|
1
|
+
import { createAuthorizationGuard } from './chunk-NK4TJV2W.js';
|
|
2
|
+
import { extractBearerToken } from './chunk-MOG4Y6I7.js';
|
|
3
|
+
|
|
4
|
+
// src/adapters/types.ts
|
|
5
|
+
function createAuthCookies(tokens, config) {
|
|
6
|
+
const prefix = config.prefix ?? "pars";
|
|
7
|
+
return [
|
|
8
|
+
{
|
|
9
|
+
name: `${prefix}.access_token`,
|
|
10
|
+
value: tokens.accessToken,
|
|
11
|
+
expires: tokens.accessExpiresAt,
|
|
12
|
+
path: config.path ?? "/",
|
|
13
|
+
domain: config.domain,
|
|
14
|
+
secure: config.secure ?? true,
|
|
15
|
+
sameSite: config.sameSite ?? "lax",
|
|
16
|
+
httpOnly: false
|
|
17
|
+
// Access token may be needed by JS
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: `${prefix}.refresh_token`,
|
|
21
|
+
value: tokens.refreshToken,
|
|
22
|
+
expires: tokens.refreshExpiresAt,
|
|
23
|
+
path: config.path ?? "/",
|
|
24
|
+
domain: config.domain,
|
|
25
|
+
secure: config.secure ?? true,
|
|
26
|
+
sameSite: config.sameSite ?? "lax",
|
|
27
|
+
httpOnly: config.httpOnly ?? true
|
|
28
|
+
// Refresh token should be HttpOnly
|
|
29
|
+
}
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
function createLogoutCookies(config) {
|
|
33
|
+
const prefix = config.prefix ?? "pars";
|
|
34
|
+
const past = /* @__PURE__ */ new Date(0);
|
|
35
|
+
return [
|
|
36
|
+
{
|
|
37
|
+
name: `${prefix}.access_token`,
|
|
38
|
+
value: "",
|
|
39
|
+
expires: past,
|
|
40
|
+
path: config.path ?? "/",
|
|
41
|
+
domain: config.domain
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: `${prefix}.refresh_token`,
|
|
45
|
+
value: "",
|
|
46
|
+
expires: past,
|
|
47
|
+
path: config.path ?? "/",
|
|
48
|
+
domain: config.domain
|
|
49
|
+
}
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/adapters/hono.ts
|
|
54
|
+
function setCookie(c, cookie) {
|
|
55
|
+
let cookieString = `${cookie.name}=${cookie.value}`;
|
|
56
|
+
if (cookie.expires) {
|
|
57
|
+
cookieString += `; Expires=${cookie.expires.toUTCString()}`;
|
|
58
|
+
}
|
|
59
|
+
if (cookie.maxAge !== void 0) {
|
|
60
|
+
cookieString += `; Max-Age=${cookie.maxAge}`;
|
|
61
|
+
}
|
|
62
|
+
if (cookie.path) {
|
|
63
|
+
cookieString += `; Path=${cookie.path}`;
|
|
64
|
+
}
|
|
65
|
+
if (cookie.domain) {
|
|
66
|
+
cookieString += `; Domain=${cookie.domain}`;
|
|
67
|
+
}
|
|
68
|
+
if (cookie.secure) {
|
|
69
|
+
cookieString += "; Secure";
|
|
70
|
+
}
|
|
71
|
+
if (cookie.httpOnly) {
|
|
72
|
+
cookieString += "; HttpOnly";
|
|
73
|
+
}
|
|
74
|
+
if (cookie.sameSite) {
|
|
75
|
+
cookieString += `; SameSite=${cookie.sameSite.charAt(0).toUpperCase() + cookie.sameSite.slice(1)}`;
|
|
76
|
+
}
|
|
77
|
+
c.header("Set-Cookie", cookieString, { append: true });
|
|
78
|
+
}
|
|
79
|
+
function getCookie(c, name) {
|
|
80
|
+
const cookieHeader = c.req.header("Cookie");
|
|
81
|
+
if (!cookieHeader) return void 0;
|
|
82
|
+
const cookies = cookieHeader.split(";").map((c2) => c2.trim());
|
|
83
|
+
for (const cookie of cookies) {
|
|
84
|
+
const [cookieName, ...valueParts] = cookie.split("=");
|
|
85
|
+
if (cookieName === name) {
|
|
86
|
+
return valueParts.join("=");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
function createAuthMiddleware(config) {
|
|
92
|
+
const { auth, onUnauthorized } = config;
|
|
93
|
+
return async (c, next) => {
|
|
94
|
+
const authHeader = c.req.header("Authorization");
|
|
95
|
+
let token = extractBearerToken(authHeader);
|
|
96
|
+
if (!token) {
|
|
97
|
+
const cookiePrefix = config.cookies?.prefix ?? "pars";
|
|
98
|
+
token = getCookie(c, `${cookiePrefix}.access_token`) ?? null;
|
|
99
|
+
}
|
|
100
|
+
if (!token) {
|
|
101
|
+
if (onUnauthorized) {
|
|
102
|
+
return onUnauthorized(c, "No token provided");
|
|
103
|
+
}
|
|
104
|
+
return c.json({ error: "Unauthorized", message: "No token provided" }, 401);
|
|
105
|
+
}
|
|
106
|
+
const result = await auth.verifyAccessToken(token);
|
|
107
|
+
if (!result.valid || !result.payload) {
|
|
108
|
+
if (onUnauthorized) {
|
|
109
|
+
return onUnauthorized(c, result.error);
|
|
110
|
+
}
|
|
111
|
+
return c.json({ error: "Unauthorized", message: result.error }, 401);
|
|
112
|
+
}
|
|
113
|
+
const authContext = {
|
|
114
|
+
userId: result.payload.sub,
|
|
115
|
+
payload: result.payload,
|
|
116
|
+
...result.payload.sid && { sessionId: result.payload.sid },
|
|
117
|
+
...result.payload.tid && { tenantId: result.payload.tid },
|
|
118
|
+
...result.payload.roles && { roles: result.payload.roles },
|
|
119
|
+
...result.payload.permissions && { permissions: result.payload.permissions }
|
|
120
|
+
};
|
|
121
|
+
c.set("auth", authContext);
|
|
122
|
+
await next();
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function createOptionalAuthMiddleware(config) {
|
|
126
|
+
const { auth } = config;
|
|
127
|
+
return async (c, next) => {
|
|
128
|
+
const authHeader = c.req.header("Authorization");
|
|
129
|
+
let token = extractBearerToken(authHeader);
|
|
130
|
+
if (!token) {
|
|
131
|
+
const cookiePrefix = config.cookies?.prefix ?? "pars";
|
|
132
|
+
token = getCookie(c, `${cookiePrefix}.access_token`) ?? null;
|
|
133
|
+
}
|
|
134
|
+
if (token) {
|
|
135
|
+
const result = await auth.verifyAccessToken(token);
|
|
136
|
+
if (result.valid && result.payload) {
|
|
137
|
+
const authContext = {
|
|
138
|
+
userId: result.payload.sub,
|
|
139
|
+
payload: result.payload,
|
|
140
|
+
...result.payload.sid && { sessionId: result.payload.sid },
|
|
141
|
+
...result.payload.tid && { tenantId: result.payload.tid },
|
|
142
|
+
...result.payload.roles && { roles: result.payload.roles },
|
|
143
|
+
...result.payload.permissions && { permissions: result.payload.permissions }
|
|
144
|
+
};
|
|
145
|
+
c.set("auth", authContext);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
await next();
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function createAuthRoutes(app, config) {
|
|
152
|
+
const { auth, cookies: cookieConfig } = config;
|
|
153
|
+
app.post("/otp/request", async (c) => {
|
|
154
|
+
try {
|
|
155
|
+
const body = await c.req.json();
|
|
156
|
+
if (!body.identifier || !body.type) {
|
|
157
|
+
return c.json({ error: "Bad Request", message: "identifier and type are required" }, 400);
|
|
158
|
+
}
|
|
159
|
+
const result = await auth.requestOTP({
|
|
160
|
+
identifier: body.identifier,
|
|
161
|
+
type: body.type
|
|
162
|
+
});
|
|
163
|
+
if (!result.success) {
|
|
164
|
+
return c.json({
|
|
165
|
+
success: false,
|
|
166
|
+
error: result.error,
|
|
167
|
+
remainingRequests: result.remainingRequests
|
|
168
|
+
}, 429);
|
|
169
|
+
}
|
|
170
|
+
return c.json({
|
|
171
|
+
success: true,
|
|
172
|
+
expiresAt: result.expiresAt,
|
|
173
|
+
remainingRequests: result.remainingRequests
|
|
174
|
+
});
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (config.onError) {
|
|
177
|
+
return config.onError(error, c);
|
|
178
|
+
}
|
|
179
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
app.post("/otp/verify", async (c) => {
|
|
183
|
+
try {
|
|
184
|
+
const body = await c.req.json();
|
|
185
|
+
if (!body.identifier || !body.code) {
|
|
186
|
+
return c.json({ error: "Bad Request", message: "identifier and code are required" }, 400);
|
|
187
|
+
}
|
|
188
|
+
const ipAddress = c.req.header("x-forwarded-for") ?? c.req.header("x-real-ip");
|
|
189
|
+
const userAgent = c.req.header("user-agent");
|
|
190
|
+
const result = await auth.signIn({
|
|
191
|
+
provider: "otp",
|
|
192
|
+
identifier: body.identifier,
|
|
193
|
+
credential: body.code,
|
|
194
|
+
data: { type: body.type ?? "email" },
|
|
195
|
+
metadata: {
|
|
196
|
+
...ipAddress && { ipAddress },
|
|
197
|
+
...userAgent && { userAgent }
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
if (!result.success) {
|
|
201
|
+
return c.json({
|
|
202
|
+
success: false,
|
|
203
|
+
error: result.error,
|
|
204
|
+
errorCode: result.errorCode
|
|
205
|
+
}, 401);
|
|
206
|
+
}
|
|
207
|
+
if (result.tokens && cookieConfig) {
|
|
208
|
+
const cookies = createAuthCookies(result.tokens, cookieConfig);
|
|
209
|
+
for (const cookie of cookies) {
|
|
210
|
+
setCookie(c, cookie);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return c.json({
|
|
214
|
+
success: true,
|
|
215
|
+
user: result.user ? {
|
|
216
|
+
id: result.user.id,
|
|
217
|
+
email: result.user.email,
|
|
218
|
+
name: result.user.name
|
|
219
|
+
} : void 0,
|
|
220
|
+
tokens: result.tokens
|
|
221
|
+
});
|
|
222
|
+
} catch (error) {
|
|
223
|
+
if (config.onError) {
|
|
224
|
+
return config.onError(error, c);
|
|
225
|
+
}
|
|
226
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
app.post("/sign-in", async (c) => {
|
|
230
|
+
try {
|
|
231
|
+
const body = await c.req.json();
|
|
232
|
+
if (!body.provider || !body.identifier) {
|
|
233
|
+
return c.json({
|
|
234
|
+
error: "Bad Request",
|
|
235
|
+
message: "provider and identifier are required"
|
|
236
|
+
}, 400);
|
|
237
|
+
}
|
|
238
|
+
const ipAddress = c.req.header("x-forwarded-for") ?? c.req.header("x-real-ip");
|
|
239
|
+
const userAgent = c.req.header("user-agent");
|
|
240
|
+
const result = await auth.signIn({
|
|
241
|
+
provider: body.provider,
|
|
242
|
+
identifier: body.identifier,
|
|
243
|
+
...body.credential && { credential: body.credential },
|
|
244
|
+
...body.data && { data: body.data },
|
|
245
|
+
metadata: {
|
|
246
|
+
...ipAddress && { ipAddress },
|
|
247
|
+
...userAgent && { userAgent }
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
if (!result.success) {
|
|
251
|
+
return c.json({
|
|
252
|
+
success: false,
|
|
253
|
+
error: result.error,
|
|
254
|
+
errorCode: result.errorCode,
|
|
255
|
+
requiresTwoFactor: result.requiresTwoFactor
|
|
256
|
+
}, 401);
|
|
257
|
+
}
|
|
258
|
+
if (result.tokens && cookieConfig) {
|
|
259
|
+
const cookies = createAuthCookies(result.tokens, cookieConfig);
|
|
260
|
+
for (const cookie of cookies) {
|
|
261
|
+
setCookie(c, cookie);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return c.json({
|
|
265
|
+
success: true,
|
|
266
|
+
user: result.user ? {
|
|
267
|
+
id: result.user.id,
|
|
268
|
+
email: result.user.email,
|
|
269
|
+
name: result.user.name
|
|
270
|
+
} : void 0,
|
|
271
|
+
tokens: result.tokens,
|
|
272
|
+
requiresTwoFactor: result.requiresTwoFactor
|
|
273
|
+
});
|
|
274
|
+
} catch (error) {
|
|
275
|
+
if (config.onError) {
|
|
276
|
+
return config.onError(error, c);
|
|
277
|
+
}
|
|
278
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
app.post("/sign-out", async (c) => {
|
|
282
|
+
try {
|
|
283
|
+
const authContext = c.get("auth");
|
|
284
|
+
if (authContext?.sessionId) {
|
|
285
|
+
await auth.signOut(authContext.sessionId);
|
|
286
|
+
}
|
|
287
|
+
if (cookieConfig) {
|
|
288
|
+
const cookies = createLogoutCookies(cookieConfig);
|
|
289
|
+
for (const cookie of cookies) {
|
|
290
|
+
setCookie(c, cookie);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return c.json({ success: true });
|
|
294
|
+
} catch (error) {
|
|
295
|
+
if (config.onError) {
|
|
296
|
+
return config.onError(error, c);
|
|
297
|
+
}
|
|
298
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
app.post("/refresh", async (c) => {
|
|
302
|
+
try {
|
|
303
|
+
let refreshToken;
|
|
304
|
+
try {
|
|
305
|
+
const body = await c.req.json();
|
|
306
|
+
refreshToken = body.refreshToken;
|
|
307
|
+
} catch {
|
|
308
|
+
}
|
|
309
|
+
if (!refreshToken) {
|
|
310
|
+
const cookiePrefix = cookieConfig?.prefix ?? "pars";
|
|
311
|
+
refreshToken = getCookie(c, `${cookiePrefix}.refresh_token`);
|
|
312
|
+
}
|
|
313
|
+
if (!refreshToken) {
|
|
314
|
+
return c.json({
|
|
315
|
+
success: false,
|
|
316
|
+
error: "No refresh token provided"
|
|
317
|
+
}, 401);
|
|
318
|
+
}
|
|
319
|
+
const result = await auth.refreshTokens(refreshToken);
|
|
320
|
+
if (!result.success) {
|
|
321
|
+
if (cookieConfig) {
|
|
322
|
+
const cookies = createLogoutCookies(cookieConfig);
|
|
323
|
+
for (const cookie of cookies) {
|
|
324
|
+
setCookie(c, cookie);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return c.json({
|
|
328
|
+
success: false,
|
|
329
|
+
error: result.error
|
|
330
|
+
}, 401);
|
|
331
|
+
}
|
|
332
|
+
if (result.tokens && cookieConfig) {
|
|
333
|
+
const cookies = createAuthCookies(result.tokens, cookieConfig);
|
|
334
|
+
for (const cookie of cookies) {
|
|
335
|
+
setCookie(c, cookie);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return c.json({
|
|
339
|
+
success: true,
|
|
340
|
+
tokens: result.tokens
|
|
341
|
+
});
|
|
342
|
+
} catch (error) {
|
|
343
|
+
if (config.onError) {
|
|
344
|
+
return config.onError(error, c);
|
|
345
|
+
}
|
|
346
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
app.get("/me", async (c) => {
|
|
350
|
+
const authContext = c.get("auth");
|
|
351
|
+
if (!authContext) {
|
|
352
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
353
|
+
}
|
|
354
|
+
const user = await auth.getAdapter().findUserById(authContext.userId);
|
|
355
|
+
if (!user) {
|
|
356
|
+
return c.json({ error: "User not found" }, 404);
|
|
357
|
+
}
|
|
358
|
+
return c.json({
|
|
359
|
+
id: user.id,
|
|
360
|
+
email: user.email,
|
|
361
|
+
name: user.name,
|
|
362
|
+
avatar: user.avatar,
|
|
363
|
+
emailVerified: user.emailVerified,
|
|
364
|
+
twoFactorEnabled: user.twoFactorEnabled
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
app.get("/sessions", async (c) => {
|
|
368
|
+
const authContext = c.get("auth");
|
|
369
|
+
if (!authContext) {
|
|
370
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
371
|
+
}
|
|
372
|
+
const sessions = await auth.getSessions(authContext.userId, authContext.sessionId);
|
|
373
|
+
return c.json({ sessions });
|
|
374
|
+
});
|
|
375
|
+
app.delete("/sessions/:id", async (c) => {
|
|
376
|
+
const authContext = c.get("auth");
|
|
377
|
+
if (!authContext) {
|
|
378
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
379
|
+
}
|
|
380
|
+
const sessionId = c.req.param("id");
|
|
381
|
+
const sessions = await auth.getSessions(authContext.userId);
|
|
382
|
+
const session = sessions.find((s) => s.id === sessionId);
|
|
383
|
+
if (!session) {
|
|
384
|
+
return c.json({ error: "Session not found" }, 404);
|
|
385
|
+
}
|
|
386
|
+
await auth.revokeSession(sessionId);
|
|
387
|
+
return c.json({ success: true });
|
|
388
|
+
});
|
|
389
|
+
app.delete("/sessions", async (c) => {
|
|
390
|
+
const authContext = c.get("auth");
|
|
391
|
+
if (!authContext) {
|
|
392
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
393
|
+
}
|
|
394
|
+
await auth.revokeAllSessions(authContext.userId);
|
|
395
|
+
if (cookieConfig) {
|
|
396
|
+
const cookies = createLogoutCookies(cookieConfig);
|
|
397
|
+
for (const cookie of cookies) {
|
|
398
|
+
setCookie(c, cookie);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return c.json({ success: true });
|
|
402
|
+
});
|
|
403
|
+
app.get("/providers", async (c) => {
|
|
404
|
+
const providers = auth.getProviders();
|
|
405
|
+
return c.json({ providers });
|
|
406
|
+
});
|
|
407
|
+
app.get("/tenants", async (c) => {
|
|
408
|
+
const authContext = c.get("auth");
|
|
409
|
+
if (!authContext) {
|
|
410
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
411
|
+
}
|
|
412
|
+
try {
|
|
413
|
+
const tenants = await auth.getUserTenants(authContext.userId);
|
|
414
|
+
return c.json({
|
|
415
|
+
tenants: tenants.map((t) => ({
|
|
416
|
+
id: t.tenant.id,
|
|
417
|
+
name: t.tenant.name,
|
|
418
|
+
slug: t.tenant.slug,
|
|
419
|
+
role: t.membership.role,
|
|
420
|
+
status: t.tenant.status
|
|
421
|
+
})),
|
|
422
|
+
currentTenantId: authContext.tenantId
|
|
423
|
+
});
|
|
424
|
+
} catch (error) {
|
|
425
|
+
if (config.onError) {
|
|
426
|
+
return config.onError(error, c);
|
|
427
|
+
}
|
|
428
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
app.post("/tenants/switch", async (c) => {
|
|
432
|
+
const authContext = c.get("auth");
|
|
433
|
+
if (!authContext?.sessionId) {
|
|
434
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
435
|
+
}
|
|
436
|
+
try {
|
|
437
|
+
const body = await c.req.json();
|
|
438
|
+
if (!body.tenantId) {
|
|
439
|
+
return c.json({ error: "Bad Request", message: "tenantId is required" }, 400);
|
|
440
|
+
}
|
|
441
|
+
const result = await auth.switchTenant(authContext.sessionId, body.tenantId);
|
|
442
|
+
if (!result.success) {
|
|
443
|
+
return c.json({ success: false, error: result.error }, 403);
|
|
444
|
+
}
|
|
445
|
+
if (result.tokens && cookieConfig) {
|
|
446
|
+
const cookies = createAuthCookies(result.tokens, cookieConfig);
|
|
447
|
+
for (const cookie of cookies) {
|
|
448
|
+
setCookie(c, cookie);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return c.json({
|
|
452
|
+
success: true,
|
|
453
|
+
tokens: result.tokens
|
|
454
|
+
});
|
|
455
|
+
} catch (error) {
|
|
456
|
+
if (config.onError) {
|
|
457
|
+
return config.onError(error, c);
|
|
458
|
+
}
|
|
459
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
app.get("/tenants/current", async (c) => {
|
|
463
|
+
const authContext = c.get("auth");
|
|
464
|
+
if (!authContext) {
|
|
465
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
466
|
+
}
|
|
467
|
+
if (!authContext.tenantId) {
|
|
468
|
+
return c.json({ tenant: null, membership: null });
|
|
469
|
+
}
|
|
470
|
+
try {
|
|
471
|
+
const membership = await auth.getTenantMembership(
|
|
472
|
+
authContext.userId,
|
|
473
|
+
authContext.tenantId
|
|
474
|
+
);
|
|
475
|
+
if (!membership) {
|
|
476
|
+
return c.json({ tenant: null, membership: null });
|
|
477
|
+
}
|
|
478
|
+
const tenant = await auth.getAdapter().findTenantById?.(authContext.tenantId);
|
|
479
|
+
return c.json({
|
|
480
|
+
tenant: tenant ? {
|
|
481
|
+
id: tenant.id,
|
|
482
|
+
name: tenant.name,
|
|
483
|
+
slug: tenant.slug,
|
|
484
|
+
status: tenant.status
|
|
485
|
+
} : null,
|
|
486
|
+
membership: {
|
|
487
|
+
role: membership.role,
|
|
488
|
+
permissions: membership.permissions,
|
|
489
|
+
status: membership.status
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
} catch (error) {
|
|
493
|
+
if (config.onError) {
|
|
494
|
+
return config.onError(error, c);
|
|
495
|
+
}
|
|
496
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
app.post("/tenants/:tenantId/invite", async (c) => {
|
|
500
|
+
const authContext = c.get("auth");
|
|
501
|
+
if (!authContext) {
|
|
502
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
503
|
+
}
|
|
504
|
+
try {
|
|
505
|
+
const tenantId = c.req.param("tenantId");
|
|
506
|
+
const body = await c.req.json();
|
|
507
|
+
if (!body.email || !body.role) {
|
|
508
|
+
return c.json({ error: "Bad Request", message: "email and role are required" }, 400);
|
|
509
|
+
}
|
|
510
|
+
const isAdmin = await auth.hasRoleInTenant(authContext.userId, tenantId, "admin") || await auth.hasRoleInTenant(authContext.userId, tenantId, "owner");
|
|
511
|
+
if (!isAdmin) {
|
|
512
|
+
return c.json({ error: "Forbidden", message: "Admin access required" }, 403);
|
|
513
|
+
}
|
|
514
|
+
const result = await auth.inviteToTenant({
|
|
515
|
+
email: body.email,
|
|
516
|
+
tenantId,
|
|
517
|
+
role: body.role,
|
|
518
|
+
permissions: body.permissions,
|
|
519
|
+
invitedBy: authContext.userId,
|
|
520
|
+
message: body.message
|
|
521
|
+
});
|
|
522
|
+
if (!result.success) {
|
|
523
|
+
return c.json({ success: false, error: result.error }, 400);
|
|
524
|
+
}
|
|
525
|
+
return c.json({
|
|
526
|
+
success: true,
|
|
527
|
+
invitationUrl: result.invitationUrl,
|
|
528
|
+
expiresAt: result.invitation?.expiresAt
|
|
529
|
+
});
|
|
530
|
+
} catch (error) {
|
|
531
|
+
if (config.onError) {
|
|
532
|
+
return config.onError(error, c);
|
|
533
|
+
}
|
|
534
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
app.get("/invitation", async (c) => {
|
|
538
|
+
try {
|
|
539
|
+
const token = c.req.query("token");
|
|
540
|
+
if (!token) {
|
|
541
|
+
return c.json({ error: "Bad Request", message: "token is required" }, 400);
|
|
542
|
+
}
|
|
543
|
+
const result = await auth.checkInvitation(token);
|
|
544
|
+
return c.json(result);
|
|
545
|
+
} catch (error) {
|
|
546
|
+
if (config.onError) {
|
|
547
|
+
return config.onError(error, c);
|
|
548
|
+
}
|
|
549
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
app.post("/invitation/accept", async (c) => {
|
|
553
|
+
const authContext = c.get("auth");
|
|
554
|
+
if (!authContext) {
|
|
555
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
556
|
+
}
|
|
557
|
+
try {
|
|
558
|
+
const body = await c.req.json();
|
|
559
|
+
if (!body.token) {
|
|
560
|
+
return c.json({ error: "Bad Request", message: "token is required" }, 400);
|
|
561
|
+
}
|
|
562
|
+
const result = await auth.acceptInvitation(body.token, authContext.userId);
|
|
563
|
+
if (!result.success) {
|
|
564
|
+
return c.json({ success: false, error: result.error }, 400);
|
|
565
|
+
}
|
|
566
|
+
return c.json({
|
|
567
|
+
success: true,
|
|
568
|
+
tenant: result.tenant ? {
|
|
569
|
+
id: result.tenant.id,
|
|
570
|
+
name: result.tenant.name,
|
|
571
|
+
slug: result.tenant.slug
|
|
572
|
+
} : void 0,
|
|
573
|
+
membership: result.membership ? {
|
|
574
|
+
role: result.membership.role,
|
|
575
|
+
status: result.membership.status
|
|
576
|
+
} : void 0
|
|
577
|
+
});
|
|
578
|
+
} catch (error) {
|
|
579
|
+
if (config.onError) {
|
|
580
|
+
return config.onError(error, c);
|
|
581
|
+
}
|
|
582
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
app.delete("/tenants/:tenantId/members/:userId", async (c) => {
|
|
586
|
+
const authContext = c.get("auth");
|
|
587
|
+
if (!authContext) {
|
|
588
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
589
|
+
}
|
|
590
|
+
try {
|
|
591
|
+
const tenantId = c.req.param("tenantId");
|
|
592
|
+
const userId = c.req.param("userId");
|
|
593
|
+
const isAdmin = await auth.hasRoleInTenant(authContext.userId, tenantId, "admin") || await auth.hasRoleInTenant(authContext.userId, tenantId, "owner");
|
|
594
|
+
const isSelf = authContext.userId === userId;
|
|
595
|
+
if (!isAdmin && !isSelf) {
|
|
596
|
+
return c.json({ error: "Forbidden", message: "Admin access required" }, 403);
|
|
597
|
+
}
|
|
598
|
+
await auth.removeTenantMember(userId, tenantId);
|
|
599
|
+
return c.json({ success: true });
|
|
600
|
+
} catch (error) {
|
|
601
|
+
if (config.onError) {
|
|
602
|
+
return config.onError(error, c);
|
|
603
|
+
}
|
|
604
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
app.patch("/tenants/:tenantId/members/:userId", async (c) => {
|
|
608
|
+
const authContext = c.get("auth");
|
|
609
|
+
if (!authContext) {
|
|
610
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
611
|
+
}
|
|
612
|
+
try {
|
|
613
|
+
const tenantId = c.req.param("tenantId");
|
|
614
|
+
const userId = c.req.param("userId");
|
|
615
|
+
const body = await c.req.json();
|
|
616
|
+
const isAdmin = await auth.hasRoleInTenant(authContext.userId, tenantId, "admin") || await auth.hasRoleInTenant(authContext.userId, tenantId, "owner");
|
|
617
|
+
if (!isAdmin) {
|
|
618
|
+
return c.json({ error: "Forbidden", message: "Admin access required" }, 403);
|
|
619
|
+
}
|
|
620
|
+
const membership = await auth.updateTenantMember(userId, tenantId, body);
|
|
621
|
+
return c.json({
|
|
622
|
+
success: true,
|
|
623
|
+
membership: {
|
|
624
|
+
role: membership.role,
|
|
625
|
+
permissions: membership.permissions,
|
|
626
|
+
status: membership.status
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
} catch (error) {
|
|
630
|
+
if (config.onError) {
|
|
631
|
+
return config.onError(error, c);
|
|
632
|
+
}
|
|
633
|
+
return c.json({ error: "Internal Server Error" }, 500);
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
return app;
|
|
637
|
+
}
|
|
638
|
+
function createHonoAuth(config) {
|
|
639
|
+
return {
|
|
640
|
+
/** Auth middleware (requires authentication) */
|
|
641
|
+
middleware: createAuthMiddleware(config),
|
|
642
|
+
/** Optional auth middleware (attaches auth if present) */
|
|
643
|
+
optionalMiddleware: createOptionalAuthMiddleware(config),
|
|
644
|
+
/** Create auth routes on an app */
|
|
645
|
+
createRoutes: (app) => createAuthRoutes(app, config)
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
function toAuthorizationContext(auth) {
|
|
649
|
+
return {
|
|
650
|
+
userId: auth.userId,
|
|
651
|
+
tenantId: auth.tenantId,
|
|
652
|
+
roles: auth.roles,
|
|
653
|
+
permissions: auth.permissions
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
function requireRole(...allowedRoles) {
|
|
657
|
+
return async (c, next) => {
|
|
658
|
+
const auth = c.get("auth");
|
|
659
|
+
if (!auth) {
|
|
660
|
+
return c.json({ error: "Unauthorized", message: "Authentication required" }, 401);
|
|
661
|
+
}
|
|
662
|
+
const guard = createAuthorizationGuard(toAuthorizationContext(auth));
|
|
663
|
+
if (!guard.hasAnyRole(allowedRoles)) {
|
|
664
|
+
return c.json(
|
|
665
|
+
{
|
|
666
|
+
error: "Forbidden",
|
|
667
|
+
message: "Insufficient role",
|
|
668
|
+
required: allowedRoles,
|
|
669
|
+
current: auth.roles ?? []
|
|
670
|
+
},
|
|
671
|
+
403
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
await next();
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
function requirePermission(...permissions) {
|
|
678
|
+
return async (c, next) => {
|
|
679
|
+
const auth = c.get("auth");
|
|
680
|
+
if (!auth) {
|
|
681
|
+
return c.json({ error: "Unauthorized", message: "Authentication required" }, 401);
|
|
682
|
+
}
|
|
683
|
+
const guard = createAuthorizationGuard(toAuthorizationContext(auth));
|
|
684
|
+
if (!guard.hasAllPermissions(permissions)) {
|
|
685
|
+
return c.json(
|
|
686
|
+
{
|
|
687
|
+
error: "Forbidden",
|
|
688
|
+
message: "Missing required permissions",
|
|
689
|
+
required: permissions,
|
|
690
|
+
current: auth.permissions ?? []
|
|
691
|
+
},
|
|
692
|
+
403
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
await next();
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
function requireAnyPermission(...permissions) {
|
|
699
|
+
return async (c, next) => {
|
|
700
|
+
const auth = c.get("auth");
|
|
701
|
+
if (!auth) {
|
|
702
|
+
return c.json({ error: "Unauthorized", message: "Authentication required" }, 401);
|
|
703
|
+
}
|
|
704
|
+
const guard = createAuthorizationGuard(toAuthorizationContext(auth));
|
|
705
|
+
if (!guard.hasAnyPermission(permissions)) {
|
|
706
|
+
return c.json(
|
|
707
|
+
{
|
|
708
|
+
error: "Forbidden",
|
|
709
|
+
message: "Missing required permissions",
|
|
710
|
+
required: permissions,
|
|
711
|
+
current: auth.permissions ?? []
|
|
712
|
+
},
|
|
713
|
+
403
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
await next();
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
function requireTenant() {
|
|
720
|
+
return async (c, next) => {
|
|
721
|
+
const auth = c.get("auth");
|
|
722
|
+
if (!auth) {
|
|
723
|
+
return c.json({ error: "Unauthorized", message: "Authentication required" }, 401);
|
|
724
|
+
}
|
|
725
|
+
if (!auth.tenantId) {
|
|
726
|
+
return c.json(
|
|
727
|
+
{ error: "Forbidden", message: "Tenant context required" },
|
|
728
|
+
403
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
await next();
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
function requireTenantAccess(getTenantId, options) {
|
|
735
|
+
return async (c, next) => {
|
|
736
|
+
const auth = c.get("auth");
|
|
737
|
+
if (!auth) {
|
|
738
|
+
return c.json({ error: "Unauthorized", message: "Authentication required" }, 401);
|
|
739
|
+
}
|
|
740
|
+
const requestedTenantId = getTenantId(c);
|
|
741
|
+
if (!requestedTenantId) {
|
|
742
|
+
await next();
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
const guard = createAuthorizationGuard(toAuthorizationContext(auth));
|
|
746
|
+
if (options?.bypassRoles && guard.hasAnyRole(options.bypassRoles)) {
|
|
747
|
+
await next();
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
if (auth.tenantId !== requestedTenantId) {
|
|
751
|
+
return c.json(
|
|
752
|
+
{
|
|
753
|
+
error: "Forbidden",
|
|
754
|
+
message: "Access denied to this tenant",
|
|
755
|
+
requestedTenant: requestedTenantId
|
|
756
|
+
},
|
|
757
|
+
403
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
await next();
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
function requireAdmin() {
|
|
764
|
+
return async (c, next) => {
|
|
765
|
+
const auth = c.get("auth");
|
|
766
|
+
if (!auth) {
|
|
767
|
+
return c.json({ error: "Unauthorized", message: "Authentication required" }, 401);
|
|
768
|
+
}
|
|
769
|
+
const guard = createAuthorizationGuard(toAuthorizationContext(auth));
|
|
770
|
+
const isAdmin = guard.hasRole("admin") || guard.hasRole("owner") || guard.hasRole("superadmin") || guard.hasRole("super_admin") || guard.hasPermission("*");
|
|
771
|
+
if (!isAdmin) {
|
|
772
|
+
return c.json({ error: "Forbidden", message: "Admin access required" }, 403);
|
|
773
|
+
}
|
|
774
|
+
await next();
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
function requireOwnerOrPermission(getOwnerId, permission) {
|
|
778
|
+
return async (c, next) => {
|
|
779
|
+
const auth = c.get("auth");
|
|
780
|
+
if (!auth) {
|
|
781
|
+
return c.json({ error: "Unauthorized", message: "Authentication required" }, 401);
|
|
782
|
+
}
|
|
783
|
+
const guard = createAuthorizationGuard(toAuthorizationContext(auth));
|
|
784
|
+
if (guard.hasPermission(permission)) {
|
|
785
|
+
await next();
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
const ownerId = await getOwnerId(c);
|
|
789
|
+
if (auth.userId === ownerId) {
|
|
790
|
+
await next();
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
return c.json(
|
|
794
|
+
{
|
|
795
|
+
error: "Forbidden",
|
|
796
|
+
message: "Resource owner or permission required",
|
|
797
|
+
requiredPermission: permission
|
|
798
|
+
},
|
|
799
|
+
403
|
|
800
|
+
);
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
function requireAll(...middlewares) {
|
|
804
|
+
return async (c, next) => {
|
|
805
|
+
for (const middleware of middlewares) {
|
|
806
|
+
let passed = false;
|
|
807
|
+
await middleware(c, async () => {
|
|
808
|
+
passed = true;
|
|
809
|
+
});
|
|
810
|
+
if (!passed) {
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
await next();
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function requireAny(...middlewares) {
|
|
818
|
+
return async (c, next) => {
|
|
819
|
+
for (const middleware of middlewares) {
|
|
820
|
+
let passed = false;
|
|
821
|
+
await middleware(c, async () => {
|
|
822
|
+
passed = true;
|
|
823
|
+
});
|
|
824
|
+
if (passed) {
|
|
825
|
+
await next();
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return c.json(
|
|
830
|
+
{ error: "Forbidden", message: "None of the authorization requirements were met" },
|
|
831
|
+
403
|
|
832
|
+
);
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
export { createAuthCookies, createAuthMiddleware, createAuthRoutes, createHonoAuth, createLogoutCookies, createOptionalAuthMiddleware, requireAdmin, requireAll, requireAny, requireAnyPermission, requireOwnerOrPermission, requirePermission, requireRole, requireTenant, requireTenantAccess };
|
|
837
|
+
//# sourceMappingURL=chunk-RHNVRCF3.js.map
|
|
838
|
+
//# sourceMappingURL=chunk-RHNVRCF3.js.map
|