@absolutejs/auth 0.84.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.
@@ -1,31 +1,31 @@
1
- import type { CustomProviderClientConfiguration } from '../types';
2
- /** Neon requires a registered partner OAuth application. */
3
- export declare const neonProviderConfiguration: import("citra").CustomProviderDefinition<{
4
- readonly authorizationUrl: "https://oauth2.neon.tech/oauth2/auth";
5
- readonly isOIDC: true;
6
- readonly isRefreshable: true;
7
- readonly PKCEMethod: "S256";
8
- readonly profileRequest: {
9
- readonly authIn: "header";
10
- readonly encoding: "application/json";
11
- readonly method: "GET";
12
- readonly url: "https://oauth2.neon.tech/userinfo";
1
+ import type { OAuth2ProviderClientConfiguration } from '../types';
2
+ /** Canonical Citra definition for the registered Neon partner application. */
3
+ export declare const neonProviderConfiguration: {
4
+ authorizationUrl: string;
5
+ isOIDC: true;
6
+ isRefreshable: true;
7
+ PKCEMethod: "S256";
8
+ profileRequest: {
9
+ authIn: "header";
10
+ encoding: "application/json";
11
+ method: "GET";
12
+ url: string;
13
13
  };
14
- readonly revocationRequest: {
15
- readonly authIn: "body";
16
- readonly encoding: "application/x-www-form-urlencoded";
17
- readonly tokenParamName: "token";
18
- readonly url: "https://oauth2.neon.tech/oauth2/revoke";
14
+ revocationRequest: {
15
+ authIn: "body";
16
+ encoding: "application/x-www-form-urlencoded";
17
+ tokenParamName: "token";
18
+ url: string;
19
19
  };
20
- readonly scopeRequired: true;
21
- readonly subject: ["sub"];
22
- readonly subjectType: "string";
23
- readonly tokenRequest: {
24
- readonly authIn: "body";
25
- readonly encoding: "application/x-www-form-urlencoded";
26
- readonly url: "https://oauth2.neon.tech/oauth2/token";
20
+ scopeRequired: true;
21
+ subject: string[];
22
+ subjectType: "string";
23
+ tokenRequest: {
24
+ authIn: "body";
25
+ encoding: "application/x-www-form-urlencoded";
26
+ url: string;
27
27
  };
28
- }, import("citra").CustomProviderCredentials>;
28
+ } & import("citra").ProviderConfig<any>;
29
29
  export type NeonManagementScope = `urn:neoncloud:${'projects' | 'orgs'}:${'create' | 'read' | 'update' | 'delete' | 'permission'}`;
30
30
  export type NeonProviderOptions = {
31
31
  credentials: {
@@ -38,5 +38,5 @@ export type NeonProviderOptions = {
38
38
  /** Request both scopes required by Neon to issue a refresh token. */
39
39
  offlineAccess?: boolean;
40
40
  };
41
- /** Configure `customProviders.neon` without granting management permissions implicitly. */
42
- export declare const createNeonProviderConfiguration: ({ credentials, offlineAccess, scopes }: NeonProviderOptions) => CustomProviderClientConfiguration;
41
+ /** Configure `providersConfiguration.neon` without granting management permissions implicitly. */
42
+ export declare const createNeonProviderConfiguration: ({ credentials, offlineAccess, scopes }: NeonProviderOptions) => OAuth2ProviderClientConfiguration<"neon">;
package/dist/server.js CHANGED
@@ -2393,6 +2393,32 @@ var providers = defineProviders({
2393
2393
  url: "https://nid.naver.com/oauth2.0/token"
2394
2394
  }
2395
2395
  },
2396
+ neon: {
2397
+ authorizationUrl: "https://oauth2.neon.tech/oauth2/auth",
2398
+ isOIDC: true,
2399
+ isRefreshable: true,
2400
+ PKCEMethod: "S256",
2401
+ profileRequest: {
2402
+ authIn: "header",
2403
+ encoding: "application/json",
2404
+ method: "GET",
2405
+ url: "https://oauth2.neon.tech/userinfo"
2406
+ },
2407
+ revocationRequest: {
2408
+ authIn: "body",
2409
+ encoding: "application/x-www-form-urlencoded",
2410
+ tokenParamName: "token",
2411
+ url: "https://oauth2.neon.tech/oauth2/revoke"
2412
+ },
2413
+ scopeRequired: true,
2414
+ subject: ["sub"],
2415
+ subjectType: "string",
2416
+ tokenRequest: {
2417
+ authIn: "body",
2418
+ encoding: "application/x-www-form-urlencoded",
2419
+ url: "https://oauth2.neon.tech/oauth2/token"
2420
+ }
2421
+ },
2396
2422
  notion: {
2397
2423
  authorizationUrl: "https://api.notion.com/v1/oauth/authorize",
2398
2424
  email: ["bot", "owner", "user", "person", "email"],
@@ -11213,29 +11239,17 @@ init_crypto();
11213
11239
  var acceptInvitation = async ({
11214
11240
  organizationStore,
11215
11241
  token,
11216
- userId
11242
+ userId,
11243
+ verifiedEmail
11217
11244
  }) => {
11218
- const invitation = await organizationStore.getInvitationByTokenHash(await hashToken(token));
11219
- if (!invitation || invitation.state !== "pending")
11245
+ if (!verifiedEmail?.trim() || !organizationStore.acceptInvitation)
11220
11246
  return;
11221
- if (invitation.expiresAt < Date.now())
11222
- return;
11223
- const now = Date.now();
11224
- await organizationStore.saveInvitation({
11225
- ...invitation,
11226
- acceptedAt: now,
11227
- state: "accepted"
11247
+ return organizationStore.acceptInvitation({
11248
+ now: Date.now(),
11249
+ tokenHash: await hashToken(token),
11250
+ userId,
11251
+ verifiedEmail: verifiedEmail.trim().toLowerCase()
11228
11252
  });
11229
- const membership = {
11230
- createdAt: now,
11231
- organizationId: invitation.organizationId,
11232
- roles: invitation.roles,
11233
- status: "active",
11234
- updatedAt: now,
11235
- userId
11236
- };
11237
- await organizationStore.saveMembership(membership);
11238
- return membership;
11239
11253
  };
11240
11254
  var createOrganization = async ({
11241
11255
  metadata,
@@ -11306,6 +11320,7 @@ var organizationRoutes = ({
11306
11320
  canManageMembers,
11307
11321
  emit,
11308
11322
  getUserId,
11323
+ getVerifiedEmail,
11309
11324
  invitationDurationMs,
11310
11325
  onMembershipAdded,
11311
11326
  onMembershipRemoved,
@@ -11494,7 +11509,8 @@ var organizationRoutes = ({
11494
11509
  const membership = await acceptInvitation({
11495
11510
  organizationStore,
11496
11511
  token,
11497
- userId: getUserId(user)
11512
+ userId: getUserId(user),
11513
+ verifiedEmail: await getVerifiedEmail?.(user)
11498
11514
  });
11499
11515
  if (!membership) {
11500
11516
  return status("Bad Request", "Invalid or expired invitation");
@@ -40391,6 +40407,27 @@ var toInvitation = (row) => ({
40391
40407
  });
40392
40408
  var createNeonOrganizationStore = (databaseUrl) => createPostgresOrganizationStore(createNeonDatabase(databaseUrl));
40393
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
+ },
40394
40431
  deleteOrganization: async (organizationId) => {
40395
40432
  await db.delete(organizationsTable).where(eq(organizationsTable.organization_id, organizationId));
40396
40433
  },
@@ -41441,6 +41478,30 @@ var createInMemoryOrganizationStore = () => {
41441
41478
  const memberships = new Map;
41442
41479
  const invitations = new Map;
41443
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
+ },
41444
41505
  deleteOrganization: async (organizationId) => {
41445
41506
  organizations.delete(organizationId);
41446
41507
  },
@@ -42186,5 +42247,5 @@ export {
42186
42247
  userSessionIdTypebox
42187
42248
  };
42188
42249
 
42189
- //# debugId=B801BF9A602847F164756E2164756E21
42250
+ //# debugId=0AD53C59A773867564756E2164756E21
42190
42251
  //# sourceMappingURL=server.js.map