@revojs/auth 0.2.65 → 0.3.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/dist/index.d.ts CHANGED
@@ -185,6 +185,19 @@ export declare function useClientAuth(scope: Scope): {
185
185
  signOut(): Promise<unknown>;
186
186
  };
187
187
  export declare function requireAccount(scope: Scope, redirectUrl?: string): Promise<RequiredAccount>;
188
+ export declare function useSessionCookie(scope: Scope): {
189
+ name: string;
190
+ options: {
191
+ domain?: string;
192
+ expires?: Date;
193
+ httpOnly?: boolean;
194
+ maxAge?: number;
195
+ path?: string;
196
+ priority?: import("@revojs/server").CookiePriority;
197
+ sameSite?: import("@revojs/server").CookieSameSite;
198
+ secure: boolean;
199
+ };
200
+ };
188
201
  export declare function readSessionCookie(scope: Scope): Promise<Session | undefined>;
189
202
  export declare function getSession(scope: Scope): Promise<Session | undefined>;
190
203
  export declare function setSession(scope: Scope, session: Session): Promise<void>;
@@ -235,10 +248,13 @@ export declare function useRoles(): Promise<Map<string, Role>>;
235
248
  export declare function checkPassword(scope: Scope, account: Account, password: string): Promise<boolean>;
236
249
  export declare function hashToken(value: string): Promise<string>;
237
250
  export declare function requireOrganization(scope: Scope, organizationId: string, redirectUrl?: string): Promise<Authorization>;
251
+ export declare function requireToken(scope: Scope): Promise<Authorization>;
252
+ export declare function useAuthorization(scope: Scope): Authorization;
238
253
  export declare function getPrincipal(scope: Scope, method: Method): Promise<Principal | false | undefined>;
239
254
  export declare function createPrincipal(scope: Scope, method: Method): Promise<Principal | undefined>;
240
255
  export declare function createSession(scope: Scope, principal: Principal): Promise<Session | undefined>;
241
256
  export declare const ACCOUNT_STATE = "auth:account";
257
+ export declare const AUTHORIZATION_CONTEXT: import("revojs").Descriptor<Authorization>;
242
258
  export declare const authIdSchema: import("valibot").SchemaWithPipe<readonly [import("valibot").StringSchema<undefined>, import("valibot").MinLengthAction<string, 1, undefined>, import("valibot").MaxLengthAction<string, 128, undefined>]>;
243
259
  export declare const organizationNameSchema: import("valibot").SchemaWithPipe<readonly [import("valibot").StringSchema<undefined>, import("valibot").MaxLengthAction<string, 200, undefined>, import("valibot").TrimAction, import("valibot").MinLengthAction<string, 1, undefined>]>;
244
260
  export declare const organizationParametersSchema: import("valibot").ObjectSchema<{
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@ import config from "#config/auth";
2
2
  import permissionSources from "#permissions";
3
3
  import roleSources from "#roles";
4
4
  import { isClient, isServer } from "#variables";
5
- import { deleteCookie, fetch, getState, sendForbidden, sendRedirect, sendUnauthorized, setCookie, setState, useCookies, useHeaders, useQuery, useUrl, withQuery } from "@revojs/server";
5
+ import { FetchError, deleteCookie, fetch, getState, sendForbidden, sendRedirect, sendUnauthorized, setCookie, setState, useCookies, useHeaders, useQuery, useUrl, withQuery } from "@revojs/server";
6
6
  import { useStorageProvider } from "@revojs/storage";
7
- import { BASE64_DECODE, BASE64_ENCODE, decoder, defineHook, digest, dispatchHook, encoder, equalsConstantTime, hashPassword, isPasswordHash, needsPasswordRehash, sign, verify, verifyPassword } from "revojs";
7
+ import { BASE64_DECODE, BASE64_ENCODE, decoder, defineContext, defineHook, digest, dispatchHook, encoder, equalsConstantTime, hashPassword, isPasswordHash, needsPasswordRehash, requireContext, setContext, sign, verify, verifyPassword } from "revojs";
8
8
  import { mergeObjects } from "revojs/shared";
9
9
  import { boolean, literal, maxLength, minLength, nullable, nullish, number, object, optional, parse, pipe, string, transform, trim, union } from "valibot";
10
10
  //#region src/index.ts
@@ -38,7 +38,7 @@ async function requireAccount(scope, redirectUrl) {
38
38
  headers: useHeaders(scope)
39
39
  });
40
40
  } catch (error) {
41
- if (redirectUrl && error instanceof Response && error.status === 401) {
41
+ if (redirectUrl && error instanceof FetchError && error.status === 401) {
42
42
  const { pathname } = useUrl(scope);
43
43
  if (redirectUrl !== pathname) globalThis.location.href = redirectUrl;
44
44
  }
@@ -51,9 +51,14 @@ async function requireAccount(scope, redirectUrl) {
51
51
  const cookie = await readSessionCookie(scope);
52
52
  if (cookie?.id === resolved.session.id && cookie.accountId === resolved.account.id) return resolved;
53
53
  }
54
- const session = await getSession(scope);
54
+ const cookie = await readSessionCookie(scope);
55
+ let session;
55
56
  let account;
56
- if (session) account = await (await useAccountStorage(scope)).getAccountById(session.accountId);
57
+ if (cookie) {
58
+ const storage = await useAccountStorage(scope);
59
+ [session, account] = await Promise.all([getSession(scope), storage.getAccountById(cookie.accountId)]);
60
+ }
61
+ if (session?.accountId !== account?.id) account = void 0;
57
62
  if (session && account) {
58
63
  const result = {
59
64
  session,
@@ -71,8 +76,29 @@ async function requireAccount(scope, redirectUrl) {
71
76
  }
72
77
  throw new Error("Session is unavailable");
73
78
  }
79
+ function useSessionCookie(scope) {
80
+ const { name, cookie } = config.session;
81
+ const { hostname } = useUrl(scope);
82
+ const secure = cookie.secure ?? ![
83
+ "localhost",
84
+ "127.0.0.1",
85
+ "[::1]"
86
+ ].includes(hostname);
87
+ const options = {
88
+ ...cookie,
89
+ secure
90
+ };
91
+ if (secure && cookie.path === "/" && cookie.domain === void 0 && !name.startsWith("__")) return {
92
+ name: `__Host-${name}`,
93
+ options
94
+ };
95
+ return {
96
+ name,
97
+ options
98
+ };
99
+ }
74
100
  async function readSessionCookie(scope) {
75
- const session = useCookies(scope)[config.session.name];
101
+ const session = useCookies(scope)[useSessionCookie(scope).name];
76
102
  if (!session) return;
77
103
  const index = session.lastIndexOf(".");
78
104
  if (index <= 0) return;
@@ -98,10 +124,12 @@ async function getSession(scope) {
98
124
  async function setSession(scope, session) {
99
125
  const payload = encoder.encode(JSON.stringify(session)).toBase64(BASE64_ENCODE);
100
126
  const cookie = payload + "." + await sign(scope, SESSION_PURPOSE, payload);
101
- setCookie(scope, config.session.name, cookie, config.session.cookie);
127
+ const { name, options } = useSessionCookie(scope);
128
+ setCookie(scope, name, cookie, options);
102
129
  }
103
130
  function clearSession(scope) {
104
- deleteCookie(scope, config.session.name, config.session.cookie);
131
+ const { name, options } = useSessionCookie(scope);
132
+ deleteCookie(scope, name, options);
105
133
  }
106
134
  function defineAuthProvider(provider) {
107
135
  return { async fetch(scope) {
@@ -265,50 +293,68 @@ async function requireOrganization(scope, organizationId, redirectUrl) {
265
293
  permissions: new Set(permissions)
266
294
  };
267
295
  } catch (error) {
268
- if (redirectUrl && error instanceof Response && error.status === 401) {
296
+ if (redirectUrl && error instanceof FetchError && error.status === 401) {
269
297
  const { pathname } = useUrl(scope);
270
298
  if (redirectUrl !== pathname) globalThis.location.href = redirectUrl;
271
299
  }
272
300
  throw error;
273
301
  }
302
+ return await authorize(scope, organizationId, redirectUrl);
303
+ }
304
+ async function requireToken(scope) {
305
+ if (useHeaders(scope).authorization === void 0) throw sendUnauthorized(scope, void 0, { headers: new Headers({ "WWW-Authenticate": "Bearer" }) });
306
+ return await authorize(scope);
307
+ }
308
+ function useAuthorization(scope) {
309
+ return requireContext(scope, AUTHORIZATION_CONTEXT);
310
+ }
311
+ async function authorize(scope, organizationId, redirectUrl) {
274
312
  const storage = await useAuthStorage(scope);
275
313
  const header = useHeaders(scope).authorization;
276
314
  let account;
277
315
  let sessionId;
278
316
  let token;
317
+ let organization;
318
+ let membership;
279
319
  if (header === void 0) {
320
+ if (organizationId === void 0) throw sendUnauthorized(scope);
280
321
  const authenticated = await requireAccount(scope, redirectUrl);
281
322
  account = authenticated.account;
282
323
  sessionId = authenticated.session.id;
324
+ [organization, membership] = await Promise.all([storage.getOrganization(organizationId), storage.getMembership(organizationId, account.id)]);
283
325
  } else {
284
326
  const value = /^Bearer ([\w-]{1,256})$/.exec(header)?.[1];
285
327
  token = value ? await storage.getTokenByHash(await hashToken(value)) : void 0;
286
328
  if (!token || token.expiresAt && token.expiresAt.getTime() <= Date.now()) throw sendUnauthorized(scope);
287
- if (token.organizationId !== organizationId) throw sendForbidden(scope);
288
- const stored = await storage.getAccountById(token.accountId);
329
+ if (organizationId !== void 0 && token.organizationId !== organizationId) throw sendForbidden(scope);
330
+ const [stored, found, joined] = await Promise.all([
331
+ storage.getAccountById(token.accountId),
332
+ storage.getOrganization(token.organizationId),
333
+ storage.getMembership(token.organizationId, token.accountId)
334
+ ]);
289
335
  if (!stored) throw sendUnauthorized(scope);
290
336
  account = {
291
337
  id: stored.id,
292
338
  email: stored.email,
293
339
  createdAt: stored.createdAt
294
340
  };
341
+ organization = found;
342
+ membership = joined;
295
343
  if (!token.lastUsedAt || Date.now() - token.lastUsedAt.getTime() > TOKEN_TOUCH_INTERVAL) await storage.touchToken(token.id, /* @__PURE__ */ new Date());
296
344
  }
297
- const organization = await storage.getOrganization(organizationId);
298
- const membership = await storage.getMembership(organizationId, account.id);
299
345
  if (!organization || membership?.status !== "active") throw sendForbidden(scope);
300
346
  let granted = new Array();
301
347
  if (organization.accountId === account.id) granted = [...(await usePermissions()).keys()];
302
348
  else if (membership.role) granted = (await useRoles()).get(membership.role)?.permissions ?? [];
303
349
  const permissions = new Set(granted.filter((permission) => !token || token.permissions.includes(permission)));
304
- return {
350
+ return setContext(scope, AUTHORIZATION_CONTEXT, {
305
351
  account,
306
352
  organization,
307
353
  membership,
308
354
  sessionId,
309
355
  tokenId: token?.id,
310
356
  permissions
311
- };
357
+ });
312
358
  }
313
359
  async function getPrincipal(scope, method) {
314
360
  if (method.type === "PASSWORD") {
@@ -486,6 +532,7 @@ async function createSession(scope, principal) {
486
532
  return created;
487
533
  }
488
534
  var ACCOUNT_STATE = "auth:account";
535
+ var AUTHORIZATION_CONTEXT = defineContext("AUTHORIZATION_CONTEXT");
489
536
  var authIdSchema = pipe(string(), minLength(1), maxLength(128));
490
537
  var organizationNameSchema = pipe(string(), maxLength(200), trim(), minLength(1));
491
538
  var organizationParametersSchema = object({ organizationId: authIdSchema });
@@ -572,4 +619,4 @@ var userInfoSchema = pipe(object({
572
619
  var SESSION_PURPOSE = "revojs:auth:session";
573
620
  var TOKEN_TOUCH_INTERVAL = 6e4;
574
621
  //#endregion
575
- export { ACCOUNT_STATE, AFTER_ACCOUNT_CREATE_HOOK, AFTER_ACCOUNT_GET_HOOK, AFTER_IDENTITY_CREATE_HOOK, AFTER_IDENTITY_GET_HOOK, AFTER_PRINCIPAL_CREATE_HOOK, AFTER_PRINCIPAL_GET_HOOK, AFTER_SESSION_CREATE_HOOK, AFTER_SESSION_GET_HOOK, BEFORE_ACCOUNT_CREATE_HOOK, BEFORE_ACCOUNT_GET_HOOK, BEFORE_IDENTITY_CREATE_HOOK, BEFORE_IDENTITY_GET_HOOK, BEFORE_PRINCIPAL_CREATE_HOOK, BEFORE_PRINCIPAL_GET_HOOK, BEFORE_SESSION_CREATE_HOOK, BEFORE_SESSION_GET_HOOK, authIdSchema, checkPassword, clearSession, createPrincipal, createSession, defineAuthProvider, definePermission, defineRole, getPrincipal, getSession, hashToken, membershipParametersSchema, methodSchema, organizationNameSchema, organizationParametersSchema, passkeyMethodSchema, passwordMethodSchema, principalSchema, providerMethodSchema, readSessionCookie, requireAccount, requireOrganization, roleSchema, setSession, useAccountStorage, useAuthStorage, useClientAuth, useOrganizationStorage, usePermissions, useRoles };
622
+ export { ACCOUNT_STATE, AFTER_ACCOUNT_CREATE_HOOK, AFTER_ACCOUNT_GET_HOOK, AFTER_IDENTITY_CREATE_HOOK, AFTER_IDENTITY_GET_HOOK, AFTER_PRINCIPAL_CREATE_HOOK, AFTER_PRINCIPAL_GET_HOOK, AFTER_SESSION_CREATE_HOOK, AFTER_SESSION_GET_HOOK, AUTHORIZATION_CONTEXT, BEFORE_ACCOUNT_CREATE_HOOK, BEFORE_ACCOUNT_GET_HOOK, BEFORE_IDENTITY_CREATE_HOOK, BEFORE_IDENTITY_GET_HOOK, BEFORE_PRINCIPAL_CREATE_HOOK, BEFORE_PRINCIPAL_GET_HOOK, BEFORE_SESSION_CREATE_HOOK, BEFORE_SESSION_GET_HOOK, authIdSchema, checkPassword, clearSession, createPrincipal, createSession, defineAuthProvider, definePermission, defineRole, getPrincipal, getSession, hashToken, membershipParametersSchema, methodSchema, organizationNameSchema, organizationParametersSchema, passkeyMethodSchema, passwordMethodSchema, principalSchema, providerMethodSchema, readSessionCookie, requireAccount, requireOrganization, requireToken, roleSchema, setSession, useAccountStorage, useAuthStorage, useAuthorization, useClientAuth, useOrganizationStorage, usePermissions, useRoles, useSessionCookie };
@@ -9,7 +9,6 @@ var module_default = defineModule({
9
9
  name: "session",
10
10
  cookie: {
11
11
  path: "/",
12
- secure: true,
13
12
  httpOnly: true,
14
13
  sameSite: "Lax",
15
14
  maxAge: 604800
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revojs/auth",
3
- "version": "0.2.65",
3
+ "version": "0.3.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -41,12 +41,12 @@
41
41
  "watch": "vite build --watch"
42
42
  },
43
43
  "dependencies": {
44
- "@revojs/server": "*",
45
- "@revojs/storage": "*",
46
- "revojs": "*",
44
+ "@revojs/server": "0.3.0",
45
+ "@revojs/storage": "0.3.0",
46
+ "revojs": "0.3.0",
47
47
  "valibot": "^1.5.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@revojs/tsconfig": "*"
50
+ "@revojs/tsconfig": "0.3.0"
51
51
  }
52
52
  }