@absolutejs/auth 0.35.0 → 0.36.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,7 +1,7 @@
1
1
  import type { RouteString } from '../types';
2
2
  import { type SigningKey } from './keys';
3
3
  import type { OnClientRegistration } from './registration';
4
- import type { AuthorizationCodeStore, ClientAssertionJtiStore, ClientRegistrationTokenStore, DeviceAuthorizationStore, InitialAccessTokenStore, LogoutDeliveryStore, OAuthClientStore, OidcRefreshTokenStore, PushedAuthorizationRequestStore } from './types';
4
+ import type { AuthorizationCodeStore, BackchannelAuthStore, ClientAssertionJtiStore, ClientRegistrationTokenStore, DeviceAuthorizationStore, InitialAccessTokenStore, LogoutDeliveryStore, OAuthClient, OAuthClientStore, OidcRefreshTokenStore, PushedAuthorizationRequestStore } from './types';
5
5
  export declare const DEFAULT_OIDC_ROUTE: RouteString;
6
6
  export type OidcProviderConfig<UserType> = {
7
7
  accessTokenTtlMs?: number;
@@ -13,6 +13,22 @@ export type OidcProviderConfig<UserType> = {
13
13
  deviceAuthorizationStore?: DeviceAuthorizationStore;
14
14
  deviceCodeTtlMs?: number;
15
15
  devicePollIntervalSeconds?: number;
16
+ backchannelAuthStore?: BackchannelAuthStore;
17
+ backchannelAuthTtlMs?: number;
18
+ backchannelPollIntervalSeconds?: number;
19
+ resolveBackchannelUser?: (hint: {
20
+ client: OAuthClient;
21
+ loginHint: string;
22
+ }) => Promise<{
23
+ sub: string;
24
+ } | undefined>;
25
+ onBackchannelAuthRequest?: (context: {
26
+ authReqId: string;
27
+ bindingMessage: string | undefined;
28
+ clientId: string;
29
+ scopes: string[];
30
+ userSub: string;
31
+ }) => Promise<void> | void;
16
32
  getAccessTokenClaims?: (context: {
17
33
  audience?: string;
18
34
  clientId: string;
@@ -35,6 +51,7 @@ export type OidcProviderConfig<UserType> = {
35
51
  dpopNonce?: {
36
52
  secret: string;
37
53
  };
54
+ extractTlsClientCert?: (headers: Headers) => Promise<Uint8Array | undefined> | Uint8Array | undefined;
38
55
  getAcr?: (context: {
39
56
  scopes: string[];
40
57
  user: UserType;
@@ -68,9 +85,10 @@ export declare const exchangeToken: <UserType>({ actorClientId, audience, config
68
85
  requestedScopes?: string[];
69
86
  subjectToken: string;
70
87
  }) => Promise<TokenExchangeResult>;
71
- export declare const issueTokenSet: <UserType>({ acr, claims, clientId, config, dpopJkt, nonce, now, scopes, sub }: {
88
+ export declare const issueTokenSet: <UserType>({ acr, claims, clientCertThumbprint, clientId, config, dpopJkt, nonce, now, scopes, sub }: {
72
89
  acr?: string;
73
90
  claims?: Record<string, unknown>;
91
+ clientCertThumbprint?: string;
74
92
  clientId: string;
75
93
  config: OidcProviderConfig<UserType>;
76
94
  dpopJkt?: string;
@@ -182,3 +200,59 @@ export declare const exchangeDeviceCode: <UserType>({ clientId, config, deviceCo
182
200
  dpopJkt?: string;
183
201
  now?: number;
184
202
  }) => Promise<DeviceCodeExchangeResult>;
203
+ export declare const CIBA_GRANT_TYPE = "urn:openid:params:grant-type:ciba";
204
+ export type BackchannelAuthResponse = {
205
+ auth_req_id: string;
206
+ expires_in: number;
207
+ interval: number;
208
+ };
209
+ export type BackchannelAuthError = 'expired_login_hint_token' | 'invalid_client' | 'invalid_request' | 'unknown_user_id';
210
+ export declare const issueBackchannelAuth: <UserType>({ clientId, config, loginHint, bindingMessage, now, requestedScopes }: {
211
+ clientId: string;
212
+ config: OidcProviderConfig<UserType>;
213
+ loginHint: string;
214
+ bindingMessage?: string;
215
+ now?: number;
216
+ requestedScopes: string[];
217
+ }) => Promise<{
218
+ error: BackchannelAuthError;
219
+ ok: false;
220
+ } | ({
221
+ ok: true;
222
+ } & BackchannelAuthResponse)>;
223
+ export type BackchannelDecisionResult = {
224
+ error: 'already_decided' | 'expired_token' | 'invalid_auth_req_id' | 'not_configured';
225
+ ok: false;
226
+ } | {
227
+ ok: true;
228
+ };
229
+ export declare const approveBackchannelAuth: <UserType>({ authReqId, config, userSub }: {
230
+ authReqId: string;
231
+ config: OidcProviderConfig<UserType>;
232
+ userSub?: string;
233
+ }) => Promise<BackchannelDecisionResult>;
234
+ export declare const denyBackchannelAuth: <UserType>({ authReqId, config }: {
235
+ authReqId: string;
236
+ config: OidcProviderConfig<UserType>;
237
+ }) => Promise<BackchannelDecisionResult>;
238
+ export type BackchannelExchangeError = 'access_denied' | 'authorization_pending' | 'expired_token' | 'invalid_grant' | 'slow_down';
239
+ export type BackchannelExchangeResult = {
240
+ access_token: string;
241
+ expires_in: number;
242
+ id_token: string;
243
+ ok: true;
244
+ refresh_token: string;
245
+ scope: string;
246
+ token_type: 'Bearer' | 'DPoP';
247
+ } | {
248
+ error: BackchannelExchangeError;
249
+ ok: false;
250
+ };
251
+ export declare const exchangeBackchannelAuth: <UserType>({ authReqId, clientCertThumbprint, clientId, config, dpopJkt, now }: {
252
+ authReqId: string;
253
+ clientCertThumbprint?: string;
254
+ clientId: string;
255
+ config: OidcProviderConfig<UserType>;
256
+ dpopJkt?: string;
257
+ now?: number;
258
+ }) => Promise<BackchannelExchangeResult>;
@@ -1,5 +1,6 @@
1
- import type { AuthorizationCodeStore, ClientAssertionJtiStore, ClientRegistrationTokenStore, DeviceAuthorizationStore, InitialAccessTokenStore, LogoutDeliveryStore, OAuthClient, OAuthClientStore, OidcRefreshTokenStore, PushedAuthorizationRequestStore } from './types';
1
+ import type { AuthorizationCodeStore, BackchannelAuthStore, ClientAssertionJtiStore, ClientRegistrationTokenStore, DeviceAuthorizationStore, InitialAccessTokenStore, LogoutDeliveryStore, OAuthClient, OAuthClientStore, OidcRefreshTokenStore, PushedAuthorizationRequestStore } from './types';
2
2
  export declare const createInMemoryAuthorizationCodeStore: () => AuthorizationCodeStore;
3
+ export declare const createInMemoryBackchannelAuthStore: () => BackchannelAuthStore;
3
4
  export declare const createInMemoryClientAssertionJtiStore: () => ClientAssertionJtiStore;
4
5
  export declare const createInMemoryClientRegistrationTokenStore: () => ClientRegistrationTokenStore;
5
6
  export declare const createInMemoryDeviceAuthorizationStore: () => DeviceAuthorizationStore;
@@ -0,0 +1,11 @@
1
+ export declare const computeCertThumbprint: (derBytes: Uint8Array) => Promise<string>;
2
+ export declare const extractRfc9440ClientCert: (headers: Headers) => Uint8Array<ArrayBuffer> | undefined;
3
+ export declare const resolveClientCert: ({ extract, headers }: {
4
+ extract: ((headers: Headers) => Promise<Uint8Array | undefined> | Uint8Array | undefined) | undefined;
5
+ headers: Headers;
6
+ }) => Promise<Uint8Array<ArrayBufferLike> | undefined>;
7
+ export declare const verifyCertificateBoundToken: ({ cnfThumbprint, extract, headers }: {
8
+ cnfThumbprint: string | undefined;
9
+ extract: ((headers: Headers) => Promise<Uint8Array | undefined> | Uint8Array | undefined) | undefined;
10
+ headers: Headers;
11
+ }) => Promise<boolean>;
@@ -1,5 +1,216 @@
1
1
  import { type AnyPgDatabase } from '../stores/postgres';
2
- import type { AuthorizationCodeStore, ClientAssertionJtiStore, ClientRegistrationTokenStore, DeviceAuthorizationStore, InitialAccessTokenStore, LogoutDeliveryStore, OAuthClientStore, OidcRefreshTokenStore, PushedAuthorizationRequestStore } from './types';
2
+ import type { AuthorizationCodeStore, BackchannelAuthStore, ClientAssertionJtiStore, ClientRegistrationTokenStore, DeviceAuthorizationStore, InitialAccessTokenStore, LogoutDeliveryStore, OAuthClientStore, OidcRefreshTokenStore, PushedAuthorizationRequestStore } from './types';
3
+ export declare const oauthBackchannelAuthRequestsTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
4
+ name: "auth_oauth_backchannel_auth_requests";
5
+ schema: undefined;
6
+ columns: {
7
+ auth_req_id: import("drizzle-orm/pg-core").PgColumn<{
8
+ name: "auth_req_id";
9
+ tableName: "auth_oauth_backchannel_auth_requests";
10
+ dataType: "string";
11
+ columnType: "PgVarchar";
12
+ data: string;
13
+ driverParam: string;
14
+ notNull: true;
15
+ hasDefault: false;
16
+ isPrimaryKey: true;
17
+ isAutoincrement: false;
18
+ hasRuntimeDefault: false;
19
+ enumValues: [string, ...string[]];
20
+ baseColumn: never;
21
+ identity: undefined;
22
+ generated: undefined;
23
+ }, {}, {
24
+ length: 255;
25
+ }>;
26
+ binding_message: import("drizzle-orm/pg-core").PgColumn<{
27
+ name: "binding_message";
28
+ tableName: "auth_oauth_backchannel_auth_requests";
29
+ dataType: "string";
30
+ columnType: "PgText";
31
+ data: string;
32
+ driverParam: string;
33
+ notNull: false;
34
+ hasDefault: false;
35
+ isPrimaryKey: false;
36
+ isAutoincrement: false;
37
+ hasRuntimeDefault: false;
38
+ enumValues: [string, ...string[]];
39
+ baseColumn: never;
40
+ identity: undefined;
41
+ generated: undefined;
42
+ }, {}, {}>;
43
+ client_id: import("drizzle-orm/pg-core").PgColumn<{
44
+ name: "client_id";
45
+ tableName: "auth_oauth_backchannel_auth_requests";
46
+ dataType: "string";
47
+ columnType: "PgVarchar";
48
+ data: string;
49
+ driverParam: string;
50
+ notNull: true;
51
+ hasDefault: false;
52
+ isPrimaryKey: false;
53
+ isAutoincrement: false;
54
+ hasRuntimeDefault: false;
55
+ enumValues: [string, ...string[]];
56
+ baseColumn: never;
57
+ identity: undefined;
58
+ generated: undefined;
59
+ }, {}, {
60
+ length: 255;
61
+ }>;
62
+ created_at_ms: import("drizzle-orm/pg-core").PgColumn<{
63
+ name: "created_at_ms";
64
+ tableName: "auth_oauth_backchannel_auth_requests";
65
+ dataType: "number";
66
+ columnType: "PgBigInt53";
67
+ data: number;
68
+ driverParam: string | number;
69
+ notNull: true;
70
+ hasDefault: false;
71
+ isPrimaryKey: false;
72
+ isAutoincrement: false;
73
+ hasRuntimeDefault: false;
74
+ enumValues: undefined;
75
+ baseColumn: never;
76
+ identity: undefined;
77
+ generated: undefined;
78
+ }, {}, {}>;
79
+ expires_at_ms: import("drizzle-orm/pg-core").PgColumn<{
80
+ name: "expires_at_ms";
81
+ tableName: "auth_oauth_backchannel_auth_requests";
82
+ dataType: "number";
83
+ columnType: "PgBigInt53";
84
+ data: number;
85
+ driverParam: string | number;
86
+ notNull: true;
87
+ hasDefault: false;
88
+ isPrimaryKey: false;
89
+ isAutoincrement: false;
90
+ hasRuntimeDefault: false;
91
+ enumValues: undefined;
92
+ baseColumn: never;
93
+ identity: undefined;
94
+ generated: undefined;
95
+ }, {}, {}>;
96
+ interval_seconds: import("drizzle-orm/pg-core").PgColumn<{
97
+ name: "interval_seconds";
98
+ tableName: "auth_oauth_backchannel_auth_requests";
99
+ dataType: "number";
100
+ columnType: "PgBigInt53";
101
+ data: number;
102
+ driverParam: string | number;
103
+ notNull: true;
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
+ last_polled_at_ms: import("drizzle-orm/pg-core").PgColumn<{
114
+ name: "last_polled_at_ms";
115
+ tableName: "auth_oauth_backchannel_auth_requests";
116
+ dataType: "number";
117
+ columnType: "PgBigInt53";
118
+ data: number;
119
+ driverParam: string | number;
120
+ notNull: false;
121
+ hasDefault: false;
122
+ isPrimaryKey: false;
123
+ isAutoincrement: false;
124
+ hasRuntimeDefault: false;
125
+ enumValues: undefined;
126
+ baseColumn: never;
127
+ identity: undefined;
128
+ generated: undefined;
129
+ }, {}, {}>;
130
+ scopes: import("drizzle-orm/pg-core").PgColumn<{
131
+ name: "scopes";
132
+ tableName: "auth_oauth_backchannel_auth_requests";
133
+ dataType: "array";
134
+ columnType: "PgArray";
135
+ data: string[];
136
+ driverParam: string | string[];
137
+ notNull: true;
138
+ hasDefault: false;
139
+ isPrimaryKey: false;
140
+ isAutoincrement: false;
141
+ hasRuntimeDefault: false;
142
+ enumValues: [string, ...string[]];
143
+ baseColumn: import("drizzle-orm").Column<{
144
+ name: "scopes";
145
+ tableName: "auth_oauth_backchannel_auth_requests";
146
+ dataType: "string";
147
+ columnType: "PgText";
148
+ data: string;
149
+ driverParam: string;
150
+ notNull: false;
151
+ hasDefault: false;
152
+ isPrimaryKey: false;
153
+ isAutoincrement: false;
154
+ hasRuntimeDefault: false;
155
+ enumValues: [string, ...string[]];
156
+ baseColumn: never;
157
+ identity: undefined;
158
+ generated: undefined;
159
+ }, {}, {}>;
160
+ identity: undefined;
161
+ generated: undefined;
162
+ }, {}, {
163
+ baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
164
+ name: "scopes";
165
+ dataType: "string";
166
+ columnType: "PgText";
167
+ data: string;
168
+ enumValues: [string, ...string[]];
169
+ driverParam: string;
170
+ }, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
171
+ size: undefined;
172
+ }>;
173
+ status: import("drizzle-orm/pg-core").PgColumn<{
174
+ name: "status";
175
+ tableName: "auth_oauth_backchannel_auth_requests";
176
+ dataType: "string";
177
+ columnType: "PgVarchar";
178
+ data: string;
179
+ driverParam: string;
180
+ notNull: true;
181
+ hasDefault: false;
182
+ isPrimaryKey: false;
183
+ isAutoincrement: false;
184
+ hasRuntimeDefault: false;
185
+ enumValues: [string, ...string[]];
186
+ baseColumn: never;
187
+ identity: undefined;
188
+ generated: undefined;
189
+ }, {}, {
190
+ length: 16;
191
+ }>;
192
+ user_sub: import("drizzle-orm/pg-core").PgColumn<{
193
+ name: "user_sub";
194
+ tableName: "auth_oauth_backchannel_auth_requests";
195
+ dataType: "string";
196
+ columnType: "PgVarchar";
197
+ data: string;
198
+ driverParam: string;
199
+ notNull: false;
200
+ hasDefault: false;
201
+ isPrimaryKey: false;
202
+ isAutoincrement: false;
203
+ hasRuntimeDefault: false;
204
+ enumValues: [string, ...string[]];
205
+ baseColumn: never;
206
+ identity: undefined;
207
+ generated: undefined;
208
+ }, {}, {
209
+ length: 255;
210
+ }>;
211
+ };
212
+ dialect: "pg";
213
+ }>;
3
214
  export declare const oauthClientAssertionJtisTable: import("drizzle-orm/pg-core").PgTableWithColumns<{
4
215
  name: "auth_oauth_client_assertion_jtis";
5
216
  schema: undefined;
@@ -1386,3 +1597,5 @@ export declare const createPostgresLogoutDeliveryStore: (db: AnyPgDatabase) => L
1386
1597
  export declare const createPostgresOAuthClientStore: (db: AnyPgDatabase) => OAuthClientStore;
1387
1598
  export declare const createPostgresOidcRefreshTokenStore: (db: AnyPgDatabase) => OidcRefreshTokenStore;
1388
1599
  export declare const createPostgresPushedAuthorizationRequestStore: (db: AnyPgDatabase) => PushedAuthorizationRequestStore;
1600
+ export declare const createNeonBackchannelAuthStore: (databaseUrl: string) => BackchannelAuthStore;
1601
+ export declare const createPostgresBackchannelAuthStore: (db: AnyPgDatabase) => BackchannelAuthStore;
@@ -68,6 +68,7 @@ export declare const oidcProviderRoutes: <UserType>(config: OidcProviderConfig<U
68
68
  resource?: string | undefined;
69
69
  refresh_token?: string | undefined;
70
70
  device_code?: string | undefined;
71
+ auth_req_id?: string | undefined;
71
72
  client_secret?: string | undefined;
72
73
  grant_type?: string | undefined;
73
74
  code?: string | undefined;
@@ -190,6 +191,35 @@ export declare const oidcProviderRoutes: <UserType>(config: OidcProviderConfig<U
190
191
  };
191
192
  };
192
193
  };
194
+ } & {
195
+ [x: string]: {
196
+ post: {
197
+ body: {
198
+ client_id?: string | undefined;
199
+ scope?: string | undefined;
200
+ client_secret?: string | undefined;
201
+ binding_message?: string | undefined;
202
+ login_hint?: string | undefined;
203
+ };
204
+ params: {};
205
+ query: unknown;
206
+ headers: {
207
+ authorization?: string | undefined;
208
+ };
209
+ response: {
210
+ 200: Response;
211
+ 422: {
212
+ type: "validation";
213
+ on: string;
214
+ summary?: string;
215
+ message?: string;
216
+ found?: unknown;
217
+ property?: string;
218
+ expected?: string;
219
+ };
220
+ };
221
+ };
222
+ };
193
223
  } & {
194
224
  [x: string]: {
195
225
  post: {
@@ -10,6 +10,7 @@ export type OAuthClient = {
10
10
  requirePushedAuthorizationRequests?: boolean;
11
11
  requireSignedRequestObject?: boolean;
12
12
  scopes: string[];
13
+ tlsCertificateBoundThumbprints?: string[];
13
14
  };
14
15
  export type ClientAssertionJtiStore = {
15
16
  recordIfFresh: (clientId: string, jti: string, expiresAt: number) => Promise<boolean>;
@@ -115,3 +116,25 @@ export type DeviceAuthorizationStore = {
115
116
  saveDeviceAuthorization: (deviceAuthorization: DeviceAuthorization) => Promise<void>;
116
117
  updateStatus: (deviceCodeHash: string, status: DeviceAuthorizationStatus, userSub?: string) => Promise<void>;
117
118
  };
119
+ export type BackchannelAuthStatus = 'approved' | 'denied' | 'pending';
120
+ export type BackchannelAuthRequest = {
121
+ authReqId: string;
122
+ bindingMessage?: string;
123
+ clientId: string;
124
+ createdAt: number;
125
+ expiresAt: number;
126
+ /** Poll-mode interval seconds. Bumped by 5 each time the client polls faster than
127
+ * allowed (returns `slow_down` until the new interval has elapsed). */
128
+ intervalSeconds: number;
129
+ lastPolledAt?: number;
130
+ scopes: string[];
131
+ status: BackchannelAuthStatus;
132
+ userSub?: string;
133
+ };
134
+ export type BackchannelAuthStore = {
135
+ deleteByAuthReqId: (authReqId: string) => Promise<void>;
136
+ findByAuthReqId: (authReqId: string) => Promise<BackchannelAuthRequest | undefined>;
137
+ recordPoll: (authReqId: string, at: number) => Promise<void>;
138
+ saveBackchannelAuth: (request: BackchannelAuthRequest) => Promise<void>;
139
+ updateStatus: (authReqId: string, status: BackchannelAuthStatus, userSub?: string) => Promise<void>;
140
+ };
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.35.0",
2
+ "version": "0.36.0",
3
3
  "name": "@absolutejs/auth",
4
4
  "description": "An authorization library for absolutejs",
5
5
  "repository": {