@absolutejs/auth 0.26.0-beta.1 → 0.26.0-beta.3

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.
Files changed (41) hide show
  1. package/dist/audit/config.d.ts +2 -1
  2. package/dist/audit/types.d.ts +1 -1
  3. package/dist/authorization/config.d.ts +19 -0
  4. package/dist/authorization/protectPermission.d.ts +52 -0
  5. package/dist/compliance/cipher.d.ts +5 -0
  6. package/dist/compliance/config.d.ts +18 -0
  7. package/dist/compliance/redaction.d.ts +8 -0
  8. package/dist/compliance/routes.d.ts +89 -0
  9. package/dist/htmx/index.js +494 -98
  10. package/dist/htmx/index.js.map +3 -3
  11. package/dist/index.d.ts +2539 -344
  12. package/dist/index.js +3679 -1619
  13. package/dist/index.js.map +33 -13
  14. package/dist/lockout/redisLockoutStore.d.ts +3 -0
  15. package/dist/providers/clients.d.ts +3 -3
  16. package/dist/routes/authorize.d.ts +2 -2
  17. package/dist/routes/protectRoute.d.ts +2 -2
  18. package/dist/scim/config.d.ts +55 -0
  19. package/dist/scim/inMemoryScimTokenStore.d.ts +2 -0
  20. package/dist/scim/postgresScimTokenStore.d.ts +102 -0
  21. package/dist/scim/routes.d.ts +296 -0
  22. package/dist/scim/serialize.d.ts +45 -0
  23. package/dist/scim/types.d.ts +52 -0
  24. package/dist/session/promote.d.ts +9 -2
  25. package/dist/sso/config.d.ts +104 -0
  26. package/dist/sso/discoveryRoute.d.ts +63 -0
  27. package/dist/sso/inMemorySsoConnectionStore.d.ts +2 -0
  28. package/dist/sso/oidcRoutes.d.ts +97 -0
  29. package/dist/sso/postgresSsoConnectionStore.d.ts +139 -0
  30. package/dist/sso/samlRoutes.d.ts +176 -0
  31. package/dist/sso/types.d.ts +39 -0
  32. package/dist/stores/redis.d.ts +5 -0
  33. package/dist/typebox.d.ts +1 -1
  34. package/dist/types.d.ts +36 -0
  35. package/dist/webauthn/adapter.d.ts +59 -0
  36. package/dist/webauthn/config.d.ts +35 -0
  37. package/dist/webauthn/inMemoryWebAuthnCredentialStore.d.ts +2 -0
  38. package/dist/webauthn/postgresWebAuthnCredentialStore.d.ts +172 -0
  39. package/dist/webauthn/routes.d.ts +155 -0
  40. package/dist/webauthn/types.d.ts +17 -0
  41. package/package.json +2 -2
package/dist/types.d.ts CHANGED
@@ -2,13 +2,18 @@ import { CredentialsFor, NonEmptyArray, OAuth2Client, OAuth2TokenResponse, Provi
2
2
  import { Cookie, status as statusType, redirect as redirectType } from 'elysia';
3
3
  import { ElysiaCustomStatusResponse } from 'elysia/error';
4
4
  import type { AuditConfig } from './audit/config';
5
+ import type { AuthorizationConfig } from './authorization/config';
6
+ import type { ComplianceConfig } from './compliance/config';
5
7
  import type { CredentialsConfig } from './credentials/config';
6
8
  import type { AuthIdentityConflict } from './errors';
7
9
  import type { AuthHtmxConfig, AuthHtmxUser } from './htmx/types';
8
10
  import type { LockoutConfig } from './lockout/config';
9
11
  import type { MfaConfig } from './mfa/config';
12
+ import type { ScimConfig } from './scim/config';
10
13
  import type { SessionsConfig } from './session/sessionsConfig';
11
14
  import type { AuthSessionStore } from './session/types';
15
+ import type { SSOConfig } from './sso/config';
16
+ import type { WebAuthnConfig } from './webauthn/config';
12
17
  export type AuthIntent = 'login' | 'link_identity' | 'link_connector';
13
18
  export type OAuth2ProviderClientConfiguration<Provider extends ProviderOption> = {
14
19
  credentials: CredentialsFor<Provider>;
@@ -34,6 +39,14 @@ export type SessionData<UserType> = {
34
39
  /** When the session was last established by an actual authentication (login, OAuth
35
40
  * callback, or MFA challenge — NOT a token refresh). Drives step-up `requireRecentAuth`. */
36
41
  authenticatedAt?: number;
42
+ /** SAML SP-initiated Single Logout context, captured at the SAML ACS. Lets
43
+ * `{ssoRoute}/saml/:org/logout` build a signed LogoutRequest (the IdP needs the original
44
+ * NameID + SessionIndex) for the connection the session was created from. */
45
+ samlLogout?: {
46
+ connectionId: string;
47
+ nameId: string;
48
+ sessionIndex?: string;
49
+ };
37
50
  };
38
51
  export type SessionRecord<UserType> = Record<UserSessionId, SessionData<UserType>>;
39
52
  export type UnregisteredSessionData = {
@@ -181,6 +194,29 @@ export type AuthConfig<UserType> = {
181
194
  * sessions) and `DELETE /auth/sessions/:id` (remote revoke). Requires an
182
195
  * `authSessionStore` that can enumerate sessions. */
183
196
  sessions?: SessionsConfig<UserType>;
197
+ /** Per-organization enterprise SSO (the WorkOS-style model). When present, mounts
198
+ * `GET {ssoRoute}/oidc/:organizationId/authorize` + `.../callback`, resolves the org's
199
+ * OIDC connection from `ssoConnectionStore`, verifies the id_token in-house against the
200
+ * issuer's JWKS, and mints the same `SessionData<UserType>` as every other flow. */
201
+ sso?: SSOConfig<UserType>;
202
+ /** SCIM 2.0 auto-provisioning for enterprise directory sync (Okta / Azure AD). When present,
203
+ * mounts `{scimRoute}/Users` (+ `/ServiceProviderConfig`) with per-org bearer-token auth via
204
+ * `scimTokenStore`, and maps SCIM resources to the consumer's user store through hooks. */
205
+ scim?: ScimConfig;
206
+ /** Role-based / attribute-based access control (E4). When present, `auth()` exposes a
207
+ * `protectPermission(check, handler)` derive (alongside `protectRoute`) that delegates the
208
+ * decision to your `hasPermission` hook — the package stays schema-agnostic about roles. */
209
+ authorization?: AuthorizationConfig<UserType>;
210
+ /** GDPR/CCPA self-service compliance (E5). When present, mounts `GET {complianceRoute}/export`
211
+ * (right to access) and `DELETE {complianceRoute}` (right to erasure — runs your delete hook,
212
+ * revokes the user's sessions, clears the cookie). Pair with `audit.redact` for PII redaction. */
213
+ compliance?: ComplianceConfig<UserType>;
214
+ /** Passwordless / passkey auth (WebAuthn). When present, mounts the registration ceremony
215
+ * (`{webauthnRoute}/register/options` + `/verify`, adds a passkey to the authenticated caller)
216
+ * and the authentication ceremony (`.../authenticate/options` + `/verify`, passwordless sign-in
217
+ * → mints the same `SessionData<UserType>`). A `webauthnAdapter` wraps a vetted library (e.g.
218
+ * `@simplewebauthn/server`); the package never bundles the WebAuthn crypto. */
219
+ webauthn?: WebAuthnConfig<UserType>;
184
220
  /** Enable the built-in HTMX fragment routes (login, identities, connectors,
185
221
  * account, signout, delete-account). Supply provider display data + the
186
222
  * identity/connector data actions; the package owns the route wiring and
@@ -0,0 +1,59 @@
1
+ export type WebAuthnCredentialDescriptor = {
2
+ id: string;
3
+ transports?: string[];
4
+ };
5
+ export type WebAuthnRegistrationOptions = {
6
+ challenge: string;
7
+ options: Record<string, unknown>;
8
+ };
9
+ export type WebAuthnAuthenticationOptions = {
10
+ challenge: string;
11
+ options: Record<string, unknown>;
12
+ };
13
+ export type WebAuthnRegistrationResult = {
14
+ credential?: {
15
+ backedUp?: boolean;
16
+ counter: number;
17
+ credentialId: string;
18
+ deviceType?: string;
19
+ publicKey: string;
20
+ transports?: string[];
21
+ };
22
+ verified: boolean;
23
+ };
24
+ export type WebAuthnAuthenticationResult = {
25
+ newCounter?: number;
26
+ verified: boolean;
27
+ };
28
+ export type WebAuthnAdapter = {
29
+ createAuthenticationOptions: (request: {
30
+ allowCredentials: WebAuthnCredentialDescriptor[];
31
+ rpId: string;
32
+ }) => Promise<WebAuthnAuthenticationOptions> | WebAuthnAuthenticationOptions;
33
+ createRegistrationOptions: (request: {
34
+ excludeCredentials: WebAuthnCredentialDescriptor[];
35
+ rpId: string;
36
+ rpName: string;
37
+ userDisplayName: string;
38
+ userId: string;
39
+ userName: string;
40
+ }) => Promise<WebAuthnRegistrationOptions> | WebAuthnRegistrationOptions;
41
+ verifyAuthentication: (request: {
42
+ credential: {
43
+ counter: number;
44
+ credentialId: string;
45
+ publicKey: string;
46
+ transports?: string[];
47
+ };
48
+ expectedChallenge: string;
49
+ expectedOrigin: string;
50
+ expectedRPID: string;
51
+ response: unknown;
52
+ }) => Promise<WebAuthnAuthenticationResult>;
53
+ verifyRegistration: (request: {
54
+ expectedChallenge: string;
55
+ expectedOrigin: string;
56
+ expectedRPID: string;
57
+ response: unknown;
58
+ }) => Promise<WebAuthnRegistrationResult>;
59
+ };
@@ -0,0 +1,35 @@
1
+ import type { AuditEmitter } from '../audit/config';
2
+ import type { AuthSessionStore } from '../session/types';
3
+ import type { RouteString, UserSessionId } from '../types';
4
+ import type { WebAuthnAdapter } from './adapter';
5
+ import type { WebAuthnCredentialStore } from './types';
6
+ export declare const DEFAULT_WEBAUTHN_CHALLENGE_TTL_MS = 300000;
7
+ export declare const DEFAULT_WEBAUTHN_ROUTE: RouteString;
8
+ export declare const DEFAULT_WEBAUTHN_SESSION_TTL_MS: number;
9
+ export declare const WEBAUTHN_CHALLENGE_COOKIE = "webauthn_challenge";
10
+ export type WebAuthnConfig<UserType> = {
11
+ credentialStore: WebAuthnCredentialStore;
12
+ getUserId: (user: UserType) => string;
13
+ getWebAuthnUser: (userId: string) => Promise<UserType | null | undefined> | UserType | null | undefined;
14
+ origin: string;
15
+ rpId: string;
16
+ rpName: string;
17
+ webauthnAdapter: WebAuthnAdapter;
18
+ challengeDurationMs?: number;
19
+ getUserDisplayName?: (user: UserType) => string;
20
+ getUserName?: (user: UserType) => string;
21
+ onWebAuthnAuthenticated?: (context: {
22
+ user: UserType;
23
+ userSessionId: UserSessionId;
24
+ }) => void | Promise<void>;
25
+ onWebAuthnRegistered?: (context: {
26
+ credentialId: string;
27
+ userId: string;
28
+ }) => void | Promise<void>;
29
+ sessionDurationMs?: number;
30
+ webauthnRoute?: RouteString;
31
+ };
32
+ export type WebAuthnRouteProps<UserType> = WebAuthnConfig<UserType> & {
33
+ authSessionStore?: AuthSessionStore<UserType>;
34
+ emit?: AuditEmitter;
35
+ };
@@ -0,0 +1,2 @@
1
+ import type { WebAuthnCredentialStore } from './types';
2
+ export declare const createInMemoryWebAuthnCredentialStore: () => WebAuthnCredentialStore;
@@ -0,0 +1,172 @@
1
+ import { type AnyPgDatabase } from '../stores/postgres';
2
+ import type { WebAuthnCredentialStore } from './types';
3
+ export declare const webauthnCredentialsTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
4
+ name: "auth_webauthn_credentials";
5
+ schema: undefined;
6
+ columns: {
7
+ backed_up: import("drizzle-orm/pg-core").PgColumn<{
8
+ name: "backed_up";
9
+ tableName: "auth_webauthn_credentials";
10
+ dataType: "boolean";
11
+ columnType: "PgBoolean";
12
+ data: boolean;
13
+ driverParam: boolean;
14
+ notNull: false;
15
+ hasDefault: false;
16
+ isPrimaryKey: false;
17
+ isAutoincrement: false;
18
+ hasRuntimeDefault: false;
19
+ enumValues: undefined;
20
+ baseColumn: never;
21
+ identity: undefined;
22
+ generated: undefined;
23
+ }, {}, {}>;
24
+ counter: import("drizzle-orm/pg-core").PgColumn<{
25
+ name: "counter";
26
+ tableName: "auth_webauthn_credentials";
27
+ dataType: "number";
28
+ columnType: "PgBigInt53";
29
+ data: number;
30
+ driverParam: string | number;
31
+ notNull: true;
32
+ hasDefault: true;
33
+ isPrimaryKey: false;
34
+ isAutoincrement: false;
35
+ hasRuntimeDefault: false;
36
+ enumValues: undefined;
37
+ baseColumn: never;
38
+ identity: undefined;
39
+ generated: undefined;
40
+ }, {}, {}>;
41
+ created_at_ms: import("drizzle-orm/pg-core").PgColumn<{
42
+ name: "created_at_ms";
43
+ tableName: "auth_webauthn_credentials";
44
+ dataType: "number";
45
+ columnType: "PgBigInt53";
46
+ data: number;
47
+ driverParam: string | number;
48
+ notNull: true;
49
+ hasDefault: false;
50
+ isPrimaryKey: false;
51
+ isAutoincrement: false;
52
+ hasRuntimeDefault: false;
53
+ enumValues: undefined;
54
+ baseColumn: never;
55
+ identity: undefined;
56
+ generated: undefined;
57
+ }, {}, {}>;
58
+ credential_id: import("drizzle-orm/pg-core").PgColumn<{
59
+ name: "credential_id";
60
+ tableName: "auth_webauthn_credentials";
61
+ dataType: "string";
62
+ columnType: "PgVarchar";
63
+ data: string;
64
+ driverParam: string;
65
+ notNull: true;
66
+ hasDefault: false;
67
+ isPrimaryKey: true;
68
+ isAutoincrement: false;
69
+ hasRuntimeDefault: false;
70
+ enumValues: [string, ...string[]];
71
+ baseColumn: never;
72
+ identity: undefined;
73
+ generated: undefined;
74
+ }, {}, {
75
+ length: 255;
76
+ }>;
77
+ device_type: import("drizzle-orm/pg-core").PgColumn<{
78
+ name: "device_type";
79
+ tableName: "auth_webauthn_credentials";
80
+ dataType: "string";
81
+ columnType: "PgVarchar";
82
+ data: string;
83
+ driverParam: string;
84
+ notNull: false;
85
+ hasDefault: false;
86
+ isPrimaryKey: false;
87
+ isAutoincrement: false;
88
+ hasRuntimeDefault: false;
89
+ enumValues: [string, ...string[]];
90
+ baseColumn: never;
91
+ identity: undefined;
92
+ generated: undefined;
93
+ }, {}, {
94
+ length: 32;
95
+ }>;
96
+ last_used_at_ms: import("drizzle-orm/pg-core").PgColumn<{
97
+ name: "last_used_at_ms";
98
+ tableName: "auth_webauthn_credentials";
99
+ dataType: "number";
100
+ columnType: "PgBigInt53";
101
+ data: number;
102
+ driverParam: string | number;
103
+ notNull: false;
104
+ hasDefault: false;
105
+ isPrimaryKey: false;
106
+ isAutoincrement: false;
107
+ hasRuntimeDefault: false;
108
+ enumValues: undefined;
109
+ baseColumn: never;
110
+ identity: undefined;
111
+ generated: undefined;
112
+ }, {}, {}>;
113
+ public_key: import("drizzle-orm/pg-core").PgColumn<{
114
+ name: "public_key";
115
+ tableName: "auth_webauthn_credentials";
116
+ dataType: "string";
117
+ columnType: "PgText";
118
+ data: string;
119
+ driverParam: string;
120
+ notNull: true;
121
+ hasDefault: false;
122
+ isPrimaryKey: false;
123
+ isAutoincrement: false;
124
+ hasRuntimeDefault: false;
125
+ enumValues: [string, ...string[]];
126
+ baseColumn: never;
127
+ identity: undefined;
128
+ generated: undefined;
129
+ }, {}, {}>;
130
+ transports: import("drizzle-orm/pg-core").PgColumn<{
131
+ name: "transports";
132
+ tableName: "auth_webauthn_credentials";
133
+ dataType: "json";
134
+ columnType: "PgJsonb";
135
+ data: string[];
136
+ driverParam: unknown;
137
+ notNull: false;
138
+ hasDefault: false;
139
+ isPrimaryKey: false;
140
+ isAutoincrement: false;
141
+ hasRuntimeDefault: false;
142
+ enumValues: undefined;
143
+ baseColumn: never;
144
+ identity: undefined;
145
+ generated: undefined;
146
+ }, {}, {
147
+ $type: string[];
148
+ }>;
149
+ user_id: import("drizzle-orm/pg-core").PgColumn<{
150
+ name: "user_id";
151
+ tableName: "auth_webauthn_credentials";
152
+ dataType: "string";
153
+ columnType: "PgVarchar";
154
+ data: string;
155
+ driverParam: string;
156
+ notNull: true;
157
+ hasDefault: false;
158
+ isPrimaryKey: false;
159
+ isAutoincrement: false;
160
+ hasRuntimeDefault: false;
161
+ enumValues: [string, ...string[]];
162
+ baseColumn: never;
163
+ identity: undefined;
164
+ generated: undefined;
165
+ }, {}, {
166
+ length: 255;
167
+ }>;
168
+ };
169
+ dialect: "pg";
170
+ }>;
171
+ export declare const createNeonWebAuthnCredentialStore: (databaseUrl: string) => WebAuthnCredentialStore;
172
+ export declare const createPostgresWebAuthnCredentialStore: (db: AnyPgDatabase) => WebAuthnCredentialStore;
@@ -0,0 +1,155 @@
1
+ import { Elysia } from 'elysia';
2
+ import { type WebAuthnRouteProps } from './config';
3
+ export declare const webauthnRoutes: <UserType>({ authSessionStore, challengeDurationMs, credentialStore, emit, getUserDisplayName, getUserId, getUserName, getWebAuthnUser, onWebAuthnAuthenticated, onWebAuthnRegistered, origin, rpId, rpName, sessionDurationMs, webauthnAdapter, webauthnRoute }: WebAuthnRouteProps<UserType>) => Elysia<"", {
4
+ decorator: {};
5
+ store: {
6
+ session: import("..").SessionRecord<UserType>;
7
+ unregisteredSession: import("..").UnregisteredSessionRecord;
8
+ };
9
+ derive: {};
10
+ resolve: {};
11
+ }, {
12
+ typebox: {};
13
+ error: {};
14
+ }, {
15
+ schema: {};
16
+ standaloneSchema: {};
17
+ macro: {};
18
+ macroFn: {};
19
+ parser: {};
20
+ response: {};
21
+ }, {
22
+ [x: string]: {
23
+ register: {
24
+ options: {
25
+ post: {
26
+ body: unknown;
27
+ params: {};
28
+ query: unknown;
29
+ headers: unknown;
30
+ response: {
31
+ 200: {
32
+ [x: string]: unknown;
33
+ };
34
+ 401: "Authentication required";
35
+ 422: {
36
+ type: "validation";
37
+ on: string;
38
+ summary?: string;
39
+ message?: string;
40
+ found?: unknown;
41
+ property?: string;
42
+ expected?: string;
43
+ };
44
+ };
45
+ };
46
+ };
47
+ };
48
+ };
49
+ } & {
50
+ [x: string]: {
51
+ register: {
52
+ verify: {
53
+ post: {
54
+ body: {};
55
+ params: {};
56
+ query: unknown;
57
+ headers: unknown;
58
+ response: {
59
+ 200: {
60
+ readonly credentialId: string;
61
+ readonly verified: true;
62
+ };
63
+ 400: "No registration challenge in progress" | "WebAuthn registration failed";
64
+ 401: "Authentication required";
65
+ 422: {
66
+ type: "validation";
67
+ on: string;
68
+ summary?: string;
69
+ message?: string;
70
+ found?: unknown;
71
+ property?: string;
72
+ expected?: string;
73
+ };
74
+ };
75
+ };
76
+ };
77
+ };
78
+ };
79
+ } & {
80
+ [x: string]: {
81
+ authenticate: {
82
+ options: {
83
+ post: {
84
+ body: unknown;
85
+ params: {};
86
+ query: unknown;
87
+ headers: unknown;
88
+ response: {
89
+ 200: {
90
+ [x: string]: unknown;
91
+ };
92
+ 422: {
93
+ type: "validation";
94
+ on: string;
95
+ summary?: string;
96
+ message?: string;
97
+ found?: unknown;
98
+ property?: string;
99
+ expected?: string;
100
+ };
101
+ };
102
+ };
103
+ };
104
+ };
105
+ };
106
+ } & {
107
+ [x: string]: {
108
+ authenticate: {
109
+ verify: {
110
+ post: {
111
+ body: {
112
+ id: string;
113
+ };
114
+ params: {};
115
+ query: unknown;
116
+ headers: unknown;
117
+ response: {
118
+ 200: {
119
+ readonly status: "authenticated";
120
+ };
121
+ 400: "No authentication challenge in progress";
122
+ 401: "Unknown credential" | "WebAuthn authentication failed";
123
+ 422: {
124
+ type: "validation";
125
+ on: string;
126
+ summary?: string;
127
+ message?: string;
128
+ found?: unknown;
129
+ property?: string;
130
+ expected?: string;
131
+ };
132
+ };
133
+ };
134
+ };
135
+ };
136
+ };
137
+ }, {
138
+ derive: {};
139
+ resolve: {};
140
+ schema: {};
141
+ standaloneSchema: {};
142
+ response: {};
143
+ }, {
144
+ derive: {};
145
+ resolve: {};
146
+ schema: {};
147
+ standaloneSchema: {};
148
+ response: {};
149
+ } & {
150
+ derive: {};
151
+ resolve: {};
152
+ schema: {};
153
+ standaloneSchema: {};
154
+ response: {};
155
+ }>;
@@ -0,0 +1,17 @@
1
+ export type WebAuthnCredential = {
2
+ backedUp?: boolean;
3
+ counter: number;
4
+ createdAt: number;
5
+ credentialId: string;
6
+ deviceType?: string;
7
+ lastUsedAt?: number;
8
+ publicKey: string;
9
+ transports?: string[];
10
+ userId: string;
11
+ };
12
+ export type WebAuthnCredentialStore = {
13
+ getCredential: (credentialId: string) => Promise<WebAuthnCredential | undefined>;
14
+ listCredentialsByUser: (userId: string) => Promise<WebAuthnCredential[]>;
15
+ removeCredential: (credentialId: string) => Promise<void>;
16
+ saveCredential: (credential: WebAuthnCredential) => Promise<void>;
17
+ };
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.26.0-beta.1",
2
+ "version": "0.26.0-beta.3",
3
3
  "name": "@absolutejs/auth",
4
4
  "description": "An authorization library for absolutejs",
5
5
  "repository": {
@@ -30,8 +30,8 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@absolutejs/linked-providers": "0.0.2",
33
- "citra": "0.25.11",
34
33
  "@neondatabase/serverless": "1.0.0",
34
+ "citra": "^0.28.0",
35
35
  "drizzle-orm": "0.41.0"
36
36
  },
37
37
  "devDependencies": {