@absolutejs/auth 0.85.0 → 0.86.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/CHANGELOG.md +7 -0
- package/changelog.json +21 -0
- package/dist/bun.js +22 -1
- package/dist/bun.js.map +3 -3
- package/dist/cli/migrate.js +6 -6
- package/dist/cli/migrate.js.map +3 -3
- package/dist/index.js +57 -22
- package/dist/index.js.map +7 -7
- package/dist/organizations/config.d.ts +2 -0
- package/dist/organizations/operations.d.ts +6 -4
- package/dist/organizations/routes.d.ts +1 -1
- package/dist/organizations/types.d.ts +8 -0
- package/dist/server.js +57 -22
- package/dist/server.js.map +7 -7
- package/package.json +1 -1
|
@@ -15,6 +15,8 @@ export type OrganizationInvitationMessage = {
|
|
|
15
15
|
};
|
|
16
16
|
export type OrganizationsConfig<UserType> = {
|
|
17
17
|
getUserId: (user: UserType) => string;
|
|
18
|
+
/** Return only an independently verified email. Missing evidence denies acceptance. */
|
|
19
|
+
getVerifiedEmail?: (user: UserType) => string | undefined | Promise<string | undefined>;
|
|
18
20
|
organizationStore: OrganizationStore;
|
|
19
21
|
canCreateOrganization?: (user: UserType) => boolean | Promise<boolean>;
|
|
20
22
|
canManageMembers?: (context: {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { OrganizationId } from '../tenancy';
|
|
2
|
-
import type { Organization, OrganizationInvitation,
|
|
3
|
-
export declare const acceptInvitation: ({ organizationStore, token, userId }: {
|
|
2
|
+
import type { Organization, OrganizationInvitation, OrganizationStore } from './types';
|
|
3
|
+
export declare const acceptInvitation: ({ organizationStore, token, userId, verifiedEmail }: {
|
|
4
4
|
organizationStore: OrganizationStore;
|
|
5
5
|
token: string;
|
|
6
6
|
userId: string;
|
|
7
|
-
|
|
7
|
+
/** Provider-verified email, never an unverified profile field or request body. */
|
|
8
|
+
verifiedEmail?: string;
|
|
9
|
+
}) => Promise<import("./types").OrganizationMembership | undefined>;
|
|
8
10
|
export declare const autoAssignOrgsByEmail: ({ email, getOrgsForDomain, organizationStore, roles, userId }: {
|
|
9
11
|
email: string;
|
|
10
12
|
getOrgsForDomain: (domain: string) => Promise<OrganizationId[]> | OrganizationId[];
|
|
@@ -34,6 +36,6 @@ export declare const listUserOrganizations: ({ organizationStore, userId }: {
|
|
|
34
36
|
organizationStore: OrganizationStore;
|
|
35
37
|
userId: string;
|
|
36
38
|
}) => Promise<{
|
|
37
|
-
membership: OrganizationMembership;
|
|
39
|
+
membership: import("./types").OrganizationMembership;
|
|
38
40
|
organization: Organization | undefined;
|
|
39
41
|
}[]>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SessionRecord } from '../types';
|
|
2
2
|
import { type OrganizationsRouteProps } from './config';
|
|
3
|
-
export declare const organizationRoutes: <UserType>({ authSessionStore, canCreateOrganization, canManageMembers, emit, getUserId, invitationDurationMs, onMembershipAdded, onMembershipRemoved, onOrganizationCreated, onSendInvitation, organizationsRoute, organizationStore, ownerRoles }: OrganizationsRouteProps<UserType>) => import("elysia/types").AddRoute<"", "local", {
|
|
3
|
+
export declare const organizationRoutes: <UserType>({ authSessionStore, canCreateOrganization, canManageMembers, emit, getUserId, getVerifiedEmail, invitationDurationMs, onMembershipAdded, onMembershipRemoved, onOrganizationCreated, onSendInvitation, organizationsRoute, organizationStore, ownerRoles }: OrganizationsRouteProps<UserType>) => import("elysia/types").AddRoute<"", "local", {
|
|
4
4
|
decorator: {};
|
|
5
5
|
store: {
|
|
6
6
|
session: SessionRecord<UserType>;
|
|
@@ -29,6 +29,14 @@ export type OrganizationInvitation = {
|
|
|
29
29
|
tokenHash: string;
|
|
30
30
|
};
|
|
31
31
|
export type OrganizationStore = {
|
|
32
|
+
/** Atomically consume a live invitation and add membership. Custom stores must
|
|
33
|
+
* implement this capability; acceptance fails closed without it. */
|
|
34
|
+
acceptInvitation?: (input: {
|
|
35
|
+
tokenHash: string;
|
|
36
|
+
userId: string;
|
|
37
|
+
verifiedEmail: string;
|
|
38
|
+
now: number;
|
|
39
|
+
}) => Promise<OrganizationMembership | undefined>;
|
|
32
40
|
deleteOrganization: (organizationId: OrganizationId) => Promise<void>;
|
|
33
41
|
getInvitation: (invitationId: string) => Promise<OrganizationInvitation | undefined>;
|
|
34
42
|
getInvitationByTokenHash: (tokenHash: string) => Promise<OrganizationInvitation | undefined>;
|
package/dist/server.js
CHANGED
|
@@ -11239,29 +11239,17 @@ init_crypto();
|
|
|
11239
11239
|
var acceptInvitation = async ({
|
|
11240
11240
|
organizationStore,
|
|
11241
11241
|
token,
|
|
11242
|
-
userId
|
|
11242
|
+
userId,
|
|
11243
|
+
verifiedEmail
|
|
11243
11244
|
}) => {
|
|
11244
|
-
|
|
11245
|
-
if (!invitation || invitation.state !== "pending")
|
|
11245
|
+
if (!verifiedEmail?.trim() || !organizationStore.acceptInvitation)
|
|
11246
11246
|
return;
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11252
|
-
acceptedAt: now,
|
|
11253
|
-
state: "accepted"
|
|
11247
|
+
return organizationStore.acceptInvitation({
|
|
11248
|
+
now: Date.now(),
|
|
11249
|
+
tokenHash: await hashToken(token),
|
|
11250
|
+
userId,
|
|
11251
|
+
verifiedEmail: verifiedEmail.trim().toLowerCase()
|
|
11254
11252
|
});
|
|
11255
|
-
const membership = {
|
|
11256
|
-
createdAt: now,
|
|
11257
|
-
organizationId: invitation.organizationId,
|
|
11258
|
-
roles: invitation.roles,
|
|
11259
|
-
status: "active",
|
|
11260
|
-
updatedAt: now,
|
|
11261
|
-
userId
|
|
11262
|
-
};
|
|
11263
|
-
await organizationStore.saveMembership(membership);
|
|
11264
|
-
return membership;
|
|
11265
11253
|
};
|
|
11266
11254
|
var createOrganization = async ({
|
|
11267
11255
|
metadata,
|
|
@@ -11332,6 +11320,7 @@ var organizationRoutes = ({
|
|
|
11332
11320
|
canManageMembers,
|
|
11333
11321
|
emit,
|
|
11334
11322
|
getUserId,
|
|
11323
|
+
getVerifiedEmail,
|
|
11335
11324
|
invitationDurationMs,
|
|
11336
11325
|
onMembershipAdded,
|
|
11337
11326
|
onMembershipRemoved,
|
|
@@ -11520,7 +11509,8 @@ var organizationRoutes = ({
|
|
|
11520
11509
|
const membership = await acceptInvitation({
|
|
11521
11510
|
organizationStore,
|
|
11522
11511
|
token,
|
|
11523
|
-
userId: getUserId(user)
|
|
11512
|
+
userId: getUserId(user),
|
|
11513
|
+
verifiedEmail: await getVerifiedEmail?.(user)
|
|
11524
11514
|
});
|
|
11525
11515
|
if (!membership) {
|
|
11526
11516
|
return status("Bad Request", "Invalid or expired invitation");
|
|
@@ -40417,6 +40407,27 @@ var toInvitation = (row) => ({
|
|
|
40417
40407
|
});
|
|
40418
40408
|
var createNeonOrganizationStore = (databaseUrl) => createPostgresOrganizationStore(createNeonDatabase(databaseUrl));
|
|
40419
40409
|
var createPostgresOrganizationStore = (db) => ({
|
|
40410
|
+
acceptInvitation: async ({ now, tokenHash, userId, verifiedEmail }) => {
|
|
40411
|
+
const claimed = db.$with("claimed").as(db.update(organizationInvitationsTable).set({ accepted_at_ms: now, state: "accepted" }).where(and(eq(organizationInvitationsTable.token_hash, tokenHash), eq(organizationInvitationsTable.state, "pending"), gt(organizationInvitationsTable.expires_at_ms, now), sql`lower(trim(${organizationInvitationsTable.email})) = ${verifiedEmail}`, exists(db.select().from(organizationsTable).where(eq(organizationsTable.organization_id, organizationInvitationsTable.organization_id))), notExists(db.select().from(organizationMembershipsTable).where(and(eq(organizationMembershipsTable.organization_id, organizationInvitationsTable.organization_id), eq(organizationMembershipsTable.user_id, userId), eq(organizationMembershipsTable.status, "suspended")))))).returning({
|
|
40412
|
+
organizationId: organizationInvitationsTable.organization_id,
|
|
40413
|
+
roles: organizationInvitationsTable.roles
|
|
40414
|
+
}));
|
|
40415
|
+
const [row] = await db.with(claimed).insert(organizationMembershipsTable).select(db.select({
|
|
40416
|
+
created_at_ms: sql`${now}`.as("created_at_ms"),
|
|
40417
|
+
organization_id: claimed.organizationId,
|
|
40418
|
+
roles: claimed.roles,
|
|
40419
|
+
status: sql`'active'`.as("status"),
|
|
40420
|
+
updated_at_ms: sql`${now}`.as("updated_at_ms"),
|
|
40421
|
+
user_id: sql`${userId}`.as("user_id")
|
|
40422
|
+
}).from(claimed)).onConflictDoUpdate({
|
|
40423
|
+
set: { user_id: userId },
|
|
40424
|
+
target: [
|
|
40425
|
+
organizationMembershipsTable.organization_id,
|
|
40426
|
+
organizationMembershipsTable.user_id
|
|
40427
|
+
]
|
|
40428
|
+
}).returning();
|
|
40429
|
+
return row && row.status === "active" ? toMembership(row) : undefined;
|
|
40430
|
+
},
|
|
40420
40431
|
deleteOrganization: async (organizationId) => {
|
|
40421
40432
|
await db.delete(organizationsTable).where(eq(organizationsTable.organization_id, organizationId));
|
|
40422
40433
|
},
|
|
@@ -41467,6 +41478,30 @@ var createInMemoryOrganizationStore = () => {
|
|
|
41467
41478
|
const memberships = new Map;
|
|
41468
41479
|
const invitations = new Map;
|
|
41469
41480
|
return {
|
|
41481
|
+
acceptInvitation: async ({ tokenHash, userId, verifiedEmail, now }) => {
|
|
41482
|
+
const invitation = [...invitations.values()].find((value) => value.tokenHash === tokenHash);
|
|
41483
|
+
if (!invitation || invitation.state !== "pending" || invitation.expiresAt <= now || invitation.email.trim().toLowerCase() !== verifiedEmail || !organizations.has(invitation.organizationId))
|
|
41484
|
+
return;
|
|
41485
|
+
const key = membershipKey(invitation.organizationId, userId);
|
|
41486
|
+
const existing = memberships.get(key);
|
|
41487
|
+
if (existing?.status === "suspended")
|
|
41488
|
+
return;
|
|
41489
|
+
const membership = existing ?? {
|
|
41490
|
+
createdAt: now,
|
|
41491
|
+
organizationId: invitation.organizationId,
|
|
41492
|
+
roles: [...invitation.roles],
|
|
41493
|
+
status: "active",
|
|
41494
|
+
updatedAt: now,
|
|
41495
|
+
userId
|
|
41496
|
+
};
|
|
41497
|
+
invitations.set(invitation.invitationId, {
|
|
41498
|
+
...invitation,
|
|
41499
|
+
acceptedAt: now,
|
|
41500
|
+
state: "accepted"
|
|
41501
|
+
});
|
|
41502
|
+
memberships.set(key, membership);
|
|
41503
|
+
return cloneMembership(membership);
|
|
41504
|
+
},
|
|
41470
41505
|
deleteOrganization: async (organizationId) => {
|
|
41471
41506
|
organizations.delete(organizationId);
|
|
41472
41507
|
},
|
|
@@ -42212,5 +42247,5 @@ export {
|
|
|
42212
42247
|
userSessionIdTypebox
|
|
42213
42248
|
};
|
|
42214
42249
|
|
|
42215
|
-
//# debugId=
|
|
42250
|
+
//# debugId=0AD53C59A773867564756E2164756E21
|
|
42216
42251
|
//# sourceMappingURL=server.js.map
|