@ttoss/auth-postgresdb 0.1.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.
@@ -0,0 +1,244 @@
1
+
2
+ import { AuthCodeStore, ClientStore, ConsentGrant, ConsentGrantStore, RefreshTokenStore } from "@ttoss/auth-core";
3
+ import { Model } from "@ttoss/postgresdb";
4
+
5
+ //#region src/models/OAuthAuthCode.d.ts
6
+ /**
7
+ * A short-lived authorization code with its bound PKCE challenge.
8
+ *
9
+ * The primary key is the code's SHA-256 hash, never the code itself: a code
10
+ * travels through a browser redirect and a client's URL bar, so storing it in
11
+ * plaintext would make a database dump yield replayable codes.
12
+ */
13
+ declare class OAuthAuthCode extends Model {
14
+ /** SHA-256 hash (hex) of the authorization code. */
15
+ codeHash: string;
16
+ /** The `client_id` the code was issued to. */
17
+ clientId: string;
18
+ /** The redirect URI the code was issued for (must match on exchange). */
19
+ redirectUri: string;
20
+ /** The PKCE `code_challenge` (S256) bound to this code. */
21
+ codeChallenge: string;
22
+ /** The scopes granted to this code. */
23
+ scopes: string[];
24
+ /** The authenticated end-user subject identifier. */
25
+ subject: string;
26
+ /** Instant after which the code is invalid. */
27
+ expiresAt: Date;
28
+ }
29
+ //#endregion
30
+ //#region src/models/OAuthClient.d.ts
31
+ /**
32
+ * A registered OAuth client (RFC 7591 dynamic client registration).
33
+ *
34
+ * The RFC's registered fields get their own columns so they can be queried and
35
+ * indexed; any additional metadata a client submits is kept verbatim in
36
+ * `metadata`, because `OAuthClientMetadata` is an open shape.
37
+ */
38
+ declare class OAuthClient extends Model {
39
+ /** The `client_id` issued by the authorization server. */
40
+ clientId: string;
41
+ /**
42
+ * Client secret for confidential clients, `null` for public clients
43
+ * (`token_endpoint_auth_method: 'none'`).
44
+ */
45
+ clientSecret: string | null;
46
+ /** Human-readable client name shown on consent screens. */
47
+ clientName: string | null;
48
+ /** Exact redirect URIs registered for this client. */
49
+ redirectUris: string[];
50
+ /** OAuth grant types the client may use. */
51
+ grantTypes: string[] | null;
52
+ /** OAuth response types the client may use. */
53
+ responseTypes: string[] | null;
54
+ /** Client authentication method at the token endpoint. */
55
+ tokenEndpointAuthMethod: string | null;
56
+ /** Space-separated scopes the client may request. */
57
+ scope: string | null;
58
+ /** Unix timestamp (seconds) when the client was registered. */
59
+ clientIdIssuedAt: number | null;
60
+ /**
61
+ * Registration metadata outside the columns above, preserved so a client's
62
+ * submitted document round-trips unchanged (e.g. `logo_uri`, `client_uri`).
63
+ */
64
+ metadata: Record<string, unknown>;
65
+ }
66
+ //#endregion
67
+ //#region src/models/OAuthConsent.d.ts
68
+ /**
69
+ * A single-use consent handoff, correlated by the PKCE `code_challenge`.
70
+ *
71
+ * This is the record an external consent screen writes on approval so the
72
+ * restarted `/authorize` request can be approved without asking again — it is
73
+ * not a durable "user X trusts client Y" grant, which is an app-level
74
+ * authorization decision and belongs in the app's own schema.
75
+ */
76
+ declare class OAuthConsent extends Model {
77
+ /** The PKCE `code_challenge` the consent was recorded against. */
78
+ codeChallenge: string;
79
+ /** The authenticated end-user subject identifier. */
80
+ subject: string;
81
+ /** The scopes the user approved. */
82
+ scopes: string[];
83
+ /** Instant after which the consent is no longer usable. */
84
+ expiresAt: Date;
85
+ }
86
+ //#endregion
87
+ //#region src/models/OAuthRefreshToken.d.ts
88
+ /**
89
+ * A refresh token tracked for OAuth 2.1 rotation, stored by hash so a database
90
+ * dump yields no usable credentials.
91
+ *
92
+ * `consumedAt` marks a rotated token. Presenting one again is reuse, and
93
+ * `createRefreshRotation` revokes the whole `(clientId, subject)` token set —
94
+ * which is why the store adapter must report a live token's `consumedAt` as
95
+ * absent rather than as `null`.
96
+ */
97
+ declare class OAuthRefreshToken extends Model {
98
+ /** SHA-256 hash (hex) of the opaque refresh token. */
99
+ tokenHash: string;
100
+ /** The `client_id` the token was issued to. */
101
+ clientId: string;
102
+ /** The authenticated end-user subject identifier. */
103
+ subject: string;
104
+ /** The scopes granted to this token. */
105
+ scopes: string[];
106
+ /** Instant after which the token is invalid. */
107
+ expiresAt: Date;
108
+ /** Instant the token was rotated (consumed), or `null` while it is live. */
109
+ consumedAt: Date | null;
110
+ }
111
+ //#endregion
112
+ //#region src/models/index.d.ts
113
+ /**
114
+ * The OAuth data model, ready to register alongside the application's own
115
+ * models so `ttoss-postgresdb sync` and `erd` cover these tables like any
116
+ * other instead of them being a side-channel schema the CLI cannot see.
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * import { oauthModels } from '@ttoss/auth-postgresdb';
121
+ * import { initialize } from '@ttoss/postgresdb';
122
+ *
123
+ * export const db = await initialize({ models: { ...oauthModels, User } });
124
+ * ```
125
+ */
126
+ declare const oauthModels: {
127
+ OAuthAuthCode: typeof OAuthAuthCode;
128
+ OAuthClient: typeof OAuthClient;
129
+ OAuthConsent: typeof OAuthConsent;
130
+ OAuthRefreshToken: typeof OAuthRefreshToken;
131
+ };
132
+ /** The shape a `db` handle must have to back the OAuth stores. */
133
+ type OAuthModels = typeof oauthModels;
134
+ //#endregion
135
+ //#region src/stores/createConsentStore.d.ts
136
+ /** A {@link ConsentGrantStore} plus the write side an app's consent page needs. */
137
+ interface PostgresdbConsentStore extends ConsentGrantStore {
138
+ /**
139
+ * Record an approval so the restarted `/authorize` request can consume it.
140
+ * Call this from the consent page's approve handler, before navigating back
141
+ * to the authorization server's `/authorize`.
142
+ */
143
+ saveConsentGrant: (grant: ConsentGrant & {
144
+ codeChallenge: string;
145
+ }) => Promise<void>;
146
+ }
147
+ /**
148
+ * Creates a consent store backed by the `oauth_consents` table, for use with
149
+ * `createRedirectConsentOnAuthorize`.
150
+ *
151
+ * Reads and deletes satisfy `ConsentGrantStore`; `saveConsentGrant` covers the
152
+ * write the consent page performs. Grants are single-use — the `onAuthorize`
153
+ * hook deletes each one as it consumes it.
154
+ */
155
+ declare const createConsentStore: ({
156
+ model
157
+ }: {
158
+ /** The `OAuthConsent` model class, taken from the app's `db` handle. */model: typeof OAuthConsent;
159
+ }) => PostgresdbConsentStore;
160
+ //#endregion
161
+ //#region src/createPostgresdbOAuthStores.d.ts
162
+ /** The durable OAuth stores returned by {@link createPostgresdbOAuthStores}. */
163
+ interface PostgresdbOAuthStores {
164
+ /** Dynamic client registrations, for `createOAuthHandlers`. */
165
+ clientStore: ClientStore;
166
+ /** Single-use authorization codes, stored by hash. */
167
+ authCodeStore: AuthCodeStore;
168
+ /** Consent handoff records for `createRedirectConsentOnAuthorize`. */
169
+ consentStore: PostgresdbConsentStore;
170
+ /** Tracked refresh tokens, for `createRefreshRotation`. */
171
+ refreshTokenStore: RefreshTokenStore;
172
+ }
173
+ /**
174
+ * Creates every durable OAuth store from a `@ttoss/postgresdb` `db` handle.
175
+ *
176
+ * The stores are mechanical adapters between the `@ttoss/auth-core` store
177
+ * contracts and Sequelize, so an app that already uses `@ttoss/postgresdb` does
178
+ * not have to write them — nor reach around its own ORM to inject a raw `pg`
179
+ * query runner.
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * import { createPostgresdbOAuthStores, oauthModels } from '@ttoss/auth-postgresdb';
184
+ * import { initialize } from '@ttoss/postgresdb';
185
+ *
186
+ * const db = await initialize({ models: { ...oauthModels, User } });
187
+ *
188
+ * const { clientStore, authCodeStore, consentStore, refreshTokenStore } =
189
+ * createPostgresdbOAuthStores({ db });
190
+ * ```
191
+ */
192
+ declare const createPostgresdbOAuthStores: ({
193
+ db
194
+ }: {
195
+ /**
196
+ * The handle returned by `@ttoss/postgresdb`'s `initialize`, with
197
+ * `oauthModels` among its registered models.
198
+ */
199
+ db: OAuthModels;
200
+ }) => PostgresdbOAuthStores;
201
+ //#endregion
202
+ //#region src/stores/createAuthCodeStore.d.ts
203
+ /**
204
+ * Creates an {@link AuthCodeStore} backed by the `oauth_auth_codes` table,
205
+ * storing codes by SHA-256 hash.
206
+ *
207
+ * The engine hands the store the plaintext code on every call and never compares
208
+ * the returned `code` against anything — it reads only `clientId`,
209
+ * `redirectUri`, `codeChallenge`, `scopes`, `subject`, and `expiresAt`. So the
210
+ * adapter hashes to find the row and echoes the presented value back, and a
211
+ * database dump yields no replayable codes.
212
+ */
213
+ declare const createAuthCodeStore: ({
214
+ model
215
+ }: {
216
+ /** The `OAuthAuthCode` model class, taken from the app's `db` handle. */model: typeof OAuthAuthCode;
217
+ }) => AuthCodeStore;
218
+ //#endregion
219
+ //#region src/stores/createClientStore.d.ts
220
+ /**
221
+ * Creates a {@link ClientStore} backed by the `oauth_clients` table.
222
+ *
223
+ * `register` upserts, so re-registering an existing `client_id` replaces the
224
+ * stored document rather than failing on the primary key.
225
+ */
226
+ declare const createClientStore: ({
227
+ model
228
+ }: {
229
+ /** The `OAuthClient` model class, taken from the app's `db` handle. */model: typeof OAuthClient;
230
+ }) => ClientStore;
231
+ //#endregion
232
+ //#region src/stores/createRefreshTokenStore.d.ts
233
+ /**
234
+ * Creates a {@link RefreshTokenStore} backed by the `oauth_refresh_tokens`
235
+ * table, keyed by token hash and by the `(clientId, subject)` owner that
236
+ * `deleteByOwner` revokes on reuse detection.
237
+ */
238
+ declare const createRefreshTokenStore: ({
239
+ model
240
+ }: {
241
+ /** The `OAuthRefreshToken` model class, taken from the app's `db` handle. */model: typeof OAuthRefreshToken;
242
+ }) => RefreshTokenStore;
243
+ //#endregion
244
+ export { OAuthAuthCode, OAuthClient, OAuthConsent, type OAuthModels, OAuthRefreshToken, type PostgresdbConsentStore, type PostgresdbOAuthStores, createAuthCodeStore, createClientStore, createConsentStore, createPostgresdbOAuthStores, createRefreshTokenStore, oauthModels };
@@ -0,0 +1,244 @@
1
+
2
+ import { AuthCodeStore, ClientStore, ConsentGrant, ConsentGrantStore, RefreshTokenStore } from "@ttoss/auth-core";
3
+ import { Model } from "@ttoss/postgresdb";
4
+
5
+ //#region src/models/OAuthAuthCode.d.ts
6
+ /**
7
+ * A short-lived authorization code with its bound PKCE challenge.
8
+ *
9
+ * The primary key is the code's SHA-256 hash, never the code itself: a code
10
+ * travels through a browser redirect and a client's URL bar, so storing it in
11
+ * plaintext would make a database dump yield replayable codes.
12
+ */
13
+ declare class OAuthAuthCode extends Model {
14
+ /** SHA-256 hash (hex) of the authorization code. */
15
+ codeHash: string;
16
+ /** The `client_id` the code was issued to. */
17
+ clientId: string;
18
+ /** The redirect URI the code was issued for (must match on exchange). */
19
+ redirectUri: string;
20
+ /** The PKCE `code_challenge` (S256) bound to this code. */
21
+ codeChallenge: string;
22
+ /** The scopes granted to this code. */
23
+ scopes: string[];
24
+ /** The authenticated end-user subject identifier. */
25
+ subject: string;
26
+ /** Instant after which the code is invalid. */
27
+ expiresAt: Date;
28
+ }
29
+ //#endregion
30
+ //#region src/models/OAuthClient.d.ts
31
+ /**
32
+ * A registered OAuth client (RFC 7591 dynamic client registration).
33
+ *
34
+ * The RFC's registered fields get their own columns so they can be queried and
35
+ * indexed; any additional metadata a client submits is kept verbatim in
36
+ * `metadata`, because `OAuthClientMetadata` is an open shape.
37
+ */
38
+ declare class OAuthClient extends Model {
39
+ /** The `client_id` issued by the authorization server. */
40
+ clientId: string;
41
+ /**
42
+ * Client secret for confidential clients, `null` for public clients
43
+ * (`token_endpoint_auth_method: 'none'`).
44
+ */
45
+ clientSecret: string | null;
46
+ /** Human-readable client name shown on consent screens. */
47
+ clientName: string | null;
48
+ /** Exact redirect URIs registered for this client. */
49
+ redirectUris: string[];
50
+ /** OAuth grant types the client may use. */
51
+ grantTypes: string[] | null;
52
+ /** OAuth response types the client may use. */
53
+ responseTypes: string[] | null;
54
+ /** Client authentication method at the token endpoint. */
55
+ tokenEndpointAuthMethod: string | null;
56
+ /** Space-separated scopes the client may request. */
57
+ scope: string | null;
58
+ /** Unix timestamp (seconds) when the client was registered. */
59
+ clientIdIssuedAt: number | null;
60
+ /**
61
+ * Registration metadata outside the columns above, preserved so a client's
62
+ * submitted document round-trips unchanged (e.g. `logo_uri`, `client_uri`).
63
+ */
64
+ metadata: Record<string, unknown>;
65
+ }
66
+ //#endregion
67
+ //#region src/models/OAuthConsent.d.ts
68
+ /**
69
+ * A single-use consent handoff, correlated by the PKCE `code_challenge`.
70
+ *
71
+ * This is the record an external consent screen writes on approval so the
72
+ * restarted `/authorize` request can be approved without asking again — it is
73
+ * not a durable "user X trusts client Y" grant, which is an app-level
74
+ * authorization decision and belongs in the app's own schema.
75
+ */
76
+ declare class OAuthConsent extends Model {
77
+ /** The PKCE `code_challenge` the consent was recorded against. */
78
+ codeChallenge: string;
79
+ /** The authenticated end-user subject identifier. */
80
+ subject: string;
81
+ /** The scopes the user approved. */
82
+ scopes: string[];
83
+ /** Instant after which the consent is no longer usable. */
84
+ expiresAt: Date;
85
+ }
86
+ //#endregion
87
+ //#region src/models/OAuthRefreshToken.d.ts
88
+ /**
89
+ * A refresh token tracked for OAuth 2.1 rotation, stored by hash so a database
90
+ * dump yields no usable credentials.
91
+ *
92
+ * `consumedAt` marks a rotated token. Presenting one again is reuse, and
93
+ * `createRefreshRotation` revokes the whole `(clientId, subject)` token set —
94
+ * which is why the store adapter must report a live token's `consumedAt` as
95
+ * absent rather than as `null`.
96
+ */
97
+ declare class OAuthRefreshToken extends Model {
98
+ /** SHA-256 hash (hex) of the opaque refresh token. */
99
+ tokenHash: string;
100
+ /** The `client_id` the token was issued to. */
101
+ clientId: string;
102
+ /** The authenticated end-user subject identifier. */
103
+ subject: string;
104
+ /** The scopes granted to this token. */
105
+ scopes: string[];
106
+ /** Instant after which the token is invalid. */
107
+ expiresAt: Date;
108
+ /** Instant the token was rotated (consumed), or `null` while it is live. */
109
+ consumedAt: Date | null;
110
+ }
111
+ //#endregion
112
+ //#region src/models/index.d.ts
113
+ /**
114
+ * The OAuth data model, ready to register alongside the application's own
115
+ * models so `ttoss-postgresdb sync` and `erd` cover these tables like any
116
+ * other instead of them being a side-channel schema the CLI cannot see.
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * import { oauthModels } from '@ttoss/auth-postgresdb';
121
+ * import { initialize } from '@ttoss/postgresdb';
122
+ *
123
+ * export const db = await initialize({ models: { ...oauthModels, User } });
124
+ * ```
125
+ */
126
+ declare const oauthModels: {
127
+ OAuthAuthCode: typeof OAuthAuthCode;
128
+ OAuthClient: typeof OAuthClient;
129
+ OAuthConsent: typeof OAuthConsent;
130
+ OAuthRefreshToken: typeof OAuthRefreshToken;
131
+ };
132
+ /** The shape a `db` handle must have to back the OAuth stores. */
133
+ type OAuthModels = typeof oauthModels;
134
+ //#endregion
135
+ //#region src/stores/createConsentStore.d.ts
136
+ /** A {@link ConsentGrantStore} plus the write side an app's consent page needs. */
137
+ interface PostgresdbConsentStore extends ConsentGrantStore {
138
+ /**
139
+ * Record an approval so the restarted `/authorize` request can consume it.
140
+ * Call this from the consent page's approve handler, before navigating back
141
+ * to the authorization server's `/authorize`.
142
+ */
143
+ saveConsentGrant: (grant: ConsentGrant & {
144
+ codeChallenge: string;
145
+ }) => Promise<void>;
146
+ }
147
+ /**
148
+ * Creates a consent store backed by the `oauth_consents` table, for use with
149
+ * `createRedirectConsentOnAuthorize`.
150
+ *
151
+ * Reads and deletes satisfy `ConsentGrantStore`; `saveConsentGrant` covers the
152
+ * write the consent page performs. Grants are single-use — the `onAuthorize`
153
+ * hook deletes each one as it consumes it.
154
+ */
155
+ declare const createConsentStore: ({
156
+ model
157
+ }: {
158
+ /** The `OAuthConsent` model class, taken from the app's `db` handle. */model: typeof OAuthConsent;
159
+ }) => PostgresdbConsentStore;
160
+ //#endregion
161
+ //#region src/createPostgresdbOAuthStores.d.ts
162
+ /** The durable OAuth stores returned by {@link createPostgresdbOAuthStores}. */
163
+ interface PostgresdbOAuthStores {
164
+ /** Dynamic client registrations, for `createOAuthHandlers`. */
165
+ clientStore: ClientStore;
166
+ /** Single-use authorization codes, stored by hash. */
167
+ authCodeStore: AuthCodeStore;
168
+ /** Consent handoff records for `createRedirectConsentOnAuthorize`. */
169
+ consentStore: PostgresdbConsentStore;
170
+ /** Tracked refresh tokens, for `createRefreshRotation`. */
171
+ refreshTokenStore: RefreshTokenStore;
172
+ }
173
+ /**
174
+ * Creates every durable OAuth store from a `@ttoss/postgresdb` `db` handle.
175
+ *
176
+ * The stores are mechanical adapters between the `@ttoss/auth-core` store
177
+ * contracts and Sequelize, so an app that already uses `@ttoss/postgresdb` does
178
+ * not have to write them — nor reach around its own ORM to inject a raw `pg`
179
+ * query runner.
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * import { createPostgresdbOAuthStores, oauthModels } from '@ttoss/auth-postgresdb';
184
+ * import { initialize } from '@ttoss/postgresdb';
185
+ *
186
+ * const db = await initialize({ models: { ...oauthModels, User } });
187
+ *
188
+ * const { clientStore, authCodeStore, consentStore, refreshTokenStore } =
189
+ * createPostgresdbOAuthStores({ db });
190
+ * ```
191
+ */
192
+ declare const createPostgresdbOAuthStores: ({
193
+ db
194
+ }: {
195
+ /**
196
+ * The handle returned by `@ttoss/postgresdb`'s `initialize`, with
197
+ * `oauthModels` among its registered models.
198
+ */
199
+ db: OAuthModels;
200
+ }) => PostgresdbOAuthStores;
201
+ //#endregion
202
+ //#region src/stores/createAuthCodeStore.d.ts
203
+ /**
204
+ * Creates an {@link AuthCodeStore} backed by the `oauth_auth_codes` table,
205
+ * storing codes by SHA-256 hash.
206
+ *
207
+ * The engine hands the store the plaintext code on every call and never compares
208
+ * the returned `code` against anything — it reads only `clientId`,
209
+ * `redirectUri`, `codeChallenge`, `scopes`, `subject`, and `expiresAt`. So the
210
+ * adapter hashes to find the row and echoes the presented value back, and a
211
+ * database dump yields no replayable codes.
212
+ */
213
+ declare const createAuthCodeStore: ({
214
+ model
215
+ }: {
216
+ /** The `OAuthAuthCode` model class, taken from the app's `db` handle. */model: typeof OAuthAuthCode;
217
+ }) => AuthCodeStore;
218
+ //#endregion
219
+ //#region src/stores/createClientStore.d.ts
220
+ /**
221
+ * Creates a {@link ClientStore} backed by the `oauth_clients` table.
222
+ *
223
+ * `register` upserts, so re-registering an existing `client_id` replaces the
224
+ * stored document rather than failing on the primary key.
225
+ */
226
+ declare const createClientStore: ({
227
+ model
228
+ }: {
229
+ /** The `OAuthClient` model class, taken from the app's `db` handle. */model: typeof OAuthClient;
230
+ }) => ClientStore;
231
+ //#endregion
232
+ //#region src/stores/createRefreshTokenStore.d.ts
233
+ /**
234
+ * Creates a {@link RefreshTokenStore} backed by the `oauth_refresh_tokens`
235
+ * table, keyed by token hash and by the `(clientId, subject)` owner that
236
+ * `deleteByOwner` revokes on reuse detection.
237
+ */
238
+ declare const createRefreshTokenStore: ({
239
+ model
240
+ }: {
241
+ /** The `OAuthRefreshToken` model class, taken from the app's `db` handle. */model: typeof OAuthRefreshToken;
242
+ }) => RefreshTokenStore;
243
+ //#endregion
244
+ export { OAuthAuthCode, OAuthClient, OAuthConsent, type OAuthModels, OAuthRefreshToken, type PostgresdbConsentStore, type PostgresdbOAuthStores, createAuthCodeStore, createClientStore, createConsentStore, createPostgresdbOAuthStores, createRefreshTokenStore, oauthModels };