@nxgt/janus-drizzle 0.3.3 → 0.4.1
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/README.md +9 -1
- package/dist/index.js +8 -4
- package/dist/index.js.map +4 -4
- package/dist/stores/records.d.ts.map +1 -1
- package/dist/tables/index.d.ts +15 -0
- package/dist/tables/index.d.ts.map +1 -1
- package/dist/tables/users.d.ts +15 -0
- package/dist/tables/users.d.ts.map +1 -1
- package/docs/guide/migrations.md +35 -4
- package/docs/roadmap.md +6 -0
- package/docs/troubleshooting.md +21 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -101,7 +101,7 @@ tables.
|
|
|
101
101
|
|
|
102
102
|
| Table | Keys and indexes |
|
|
103
103
|
| --- | --- |
|
|
104
|
-
| `users` | `id` · `(id, type)` unique, for the logins' foreign key · `(type, id)` for listing · a check that a password has both its hash and its date, or neither · a check that a second factor (`second_factor_method`, `_secret`, `_confirmed_at`, `_last_step`) has its method and its secret, or none of the
|
|
104
|
+
| `users` | `id` · `(id, type)` unique, for the logins' foreign key · `(type, id)` for listing · a check that a password has both its hash and its date, or neither · a check that a second factor (`second_factor_method`, `_secret`, `_confirmed_at`, `_last_step`, `_recovery_codes`) has its method and its secret, or none of the five · a check that the method is `totp` and the last step not negative |
|
|
105
105
|
| `logins` | primary key `(type, login)`: **a login is unique per user type** · `(user_id, type)` references the user, `on delete cascade` |
|
|
106
106
|
| `sessions` | `id` · `token_hash` unique · `user_id` · `expires_at`, for `collectExpired()` |
|
|
107
107
|
| `tokens` | `token_hash` · `user_id` · a check on `kind` · `code_hash`, and `attempts`, `not null default 0`, checked `>= 0` |
|
|
@@ -116,6 +116,8 @@ requires. No secret is stored: sessions and tokens hold `sha256` of the secret,
|
|
|
116
116
|
passwords a self-describing hash, and `second_factor_secret` a TOTP secret
|
|
117
117
|
`@nxgt/janus` has already sealed with your application's key —
|
|
118
118
|
`v1.<key id>.…` — which the store keeps byte for byte.
|
|
119
|
+
`second_factor_recovery_codes` holds keyed hashes of the recovery codes, never
|
|
120
|
+
a code, in the order written; no codes are `null`, read as `[]`.
|
|
119
121
|
|
|
120
122
|
## Traps
|
|
121
123
|
|
|
@@ -131,6 +133,12 @@ passwords a self-describing hash, and `second_factor_secret` a TOTP secret
|
|
|
131
133
|
Run `bunx drizzle-kit generate`, then `bunx drizzle-kit migrate`, before
|
|
132
134
|
deploying — with `--config` naming Janus's config if it has its own.
|
|
133
135
|
[Upgrading](docs/guide/migrations.md#upgrading) shows what it writes.
|
|
136
|
+
- **Upgrading to 0.4 needs a migration too.** `users` gained
|
|
137
|
+
`second_factor_recovery_codes`, and its whole-factor check now covers it.
|
|
138
|
+
Deployed without it, every query on `users` fails with `STORE_FAILED`,
|
|
139
|
+
caused by `column "second_factor_recovery_codes" does not exist`.
|
|
140
|
+
[To 0.4](docs/guide/migrations.md#to-04-recovery-codes) shows the two
|
|
141
|
+
statements drizzle-kit writes.
|
|
134
142
|
- **Your migrations create the tables, not this package.** The core never
|
|
135
143
|
manages a schema, and the stores create nothing per request. Without the
|
|
136
144
|
exports in your schema file, the first sign-up fails with `STORE_FAILED`,
|
package/dist/index.js
CHANGED
|
@@ -110,6 +110,7 @@ function usersTable(table) {
|
|
|
110
110
|
secondFactorSecret: text2("second_factor_secret"),
|
|
111
111
|
secondFactorConfirmedAt: at("second_factor_confirmed_at"),
|
|
112
112
|
secondFactorLastStep: integer2("second_factor_last_step"),
|
|
113
|
+
secondFactorRecoveryCodes: text2("second_factor_recovery_codes").array(),
|
|
113
114
|
emailVerifiedAt: at("email_verified_at"),
|
|
114
115
|
version: integer2("version").notNull(),
|
|
115
116
|
createdAt: at("created_at").notNull(),
|
|
@@ -118,7 +119,7 @@ function usersTable(table) {
|
|
|
118
119
|
unique3("users_id_type_unique").on(t.id, t.type),
|
|
119
120
|
index5("users_type_id").on(t.type, t.id),
|
|
120
121
|
check2("users_password_whole", sql2`(${t.passwordHash} is null) = (${t.passwordUpdatedAt} is null)`),
|
|
121
|
-
check2("users_second_factor_whole", sql2`(${t.secondFactorMethod} is null) = (${t.secondFactorSecret} is null) and (${t.secondFactorMethod} is not null or (${t.secondFactorConfirmedAt} is null and ${t.secondFactorLastStep} is null))`),
|
|
122
|
+
check2("users_second_factor_whole", sql2`(${t.secondFactorMethod} is null) = (${t.secondFactorSecret} is null) and (${t.secondFactorMethod} is not null or (${t.secondFactorConfirmedAt} is null and ${t.secondFactorLastStep} is null and ${t.secondFactorRecoveryCodes} is null))`),
|
|
122
123
|
check2("users_second_factor_values", sql2`${t.secondFactorMethod} in ('totp') and ${t.secondFactorLastStep} >= 0`)
|
|
123
124
|
]);
|
|
124
125
|
}
|
|
@@ -313,11 +314,13 @@ function toUserSet(patch) {
|
|
|
313
314
|
return set;
|
|
314
315
|
}
|
|
315
316
|
function toSecondFactorColumns(secondFactor) {
|
|
317
|
+
const codes = secondFactor?.recoveryCodes ?? [];
|
|
316
318
|
return {
|
|
317
319
|
secondFactorMethod: secondFactor?.method ?? null,
|
|
318
320
|
secondFactorSecret: secondFactor?.secret ?? null,
|
|
319
321
|
secondFactorConfirmedAt: secondFactor?.confirmedAt ?? null,
|
|
320
|
-
secondFactorLastStep: secondFactor?.lastStep ?? null
|
|
322
|
+
secondFactorLastStep: secondFactor?.lastStep ?? null,
|
|
323
|
+
secondFactorRecoveryCodes: codes.length === 0 ? null : [...codes]
|
|
321
324
|
};
|
|
322
325
|
}
|
|
323
326
|
function toUser(row) {
|
|
@@ -333,7 +336,8 @@ function toUser(row) {
|
|
|
333
336
|
method: row.secondFactorMethod,
|
|
334
337
|
secret: row.secondFactorSecret,
|
|
335
338
|
confirmedAt: row.secondFactorConfirmedAt,
|
|
336
|
-
lastStep: row.secondFactorLastStep
|
|
339
|
+
lastStep: row.secondFactorLastStep,
|
|
340
|
+
recoveryCodes: [...row.secondFactorRecoveryCodes ?? []]
|
|
337
341
|
},
|
|
338
342
|
emailVerifiedAt: row.emailVerifiedAt,
|
|
339
343
|
version: row.version,
|
|
@@ -556,5 +560,5 @@ export {
|
|
|
556
560
|
defineJanusTables
|
|
557
561
|
};
|
|
558
562
|
|
|
559
|
-
//# debugId=
|
|
563
|
+
//# debugId=BBBB8A4136A5079264756E2164756E21
|
|
560
564
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -8,21 +8,21 @@
|
|
|
8
8
|
"import { index, unique } from 'drizzle-orm/pg-core';\nimport { key, type TableFactory } from './columns';\n\n/**\n * Permission tuples, one row each: `record:r1#owners@patient:u1`, or\n * `…@team:t1#members` for a subject set, whose `subject_relation` an\n * entity's row leaves `null`.\n *\n * **The uniqueness of a tuple is the unique constraint**, `nulls not\n * distinct` so two rows for one entity collide — PostgreSQL 15 or later.\n * Its column order is the reverse index `findObjects` pages: every equality\n * first, the object id last.\n */\nexport function relationsTable(table: TableFactory) {\n\treturn table(\n\t\t'relations',\n\t\t{\n\t\t\tobjectType: key('object_type').notNull(),\n\t\t\tobjectId: key('object_id').notNull(),\n\t\t\trelation: key('relation').notNull(),\n\t\t\tsubjectType: key('subject_type').notNull(),\n\t\t\tsubjectId: key('subject_id').notNull(),\n\t\t\tsubjectRelation: key('subject_relation'),\n\t\t},\n\t\t(t) => [\n\t\t\tunique('relations_tuple_unique')\n\t\t\t\t.on(\n\t\t\t\t\tt.subjectType,\n\t\t\t\t\tt.subjectId,\n\t\t\t\t\tt.subjectRelation,\n\t\t\t\t\tt.relation,\n\t\t\t\t\tt.objectType,\n\t\t\t\t\tt.objectId,\n\t\t\t\t)\n\t\t\t\t.nullsNotDistinct(),\n\t\t\t/** One hop forwards: `findSubjectSets`, `findEntities`, `deleteEntity` as the object. */\n\t\t\tindex('relations_object').on(t.objectType, t.objectId, t.relation),\n\t\t],\n\t);\n}\n",
|
|
9
9
|
"import { index, unique } from 'drizzle-orm/pg-core';\nimport { at, key, type TableFactory } from './columns';\n\n/**\n * Sessions. No foreign key to the user: deleting a user deletes the user,\n * and the core deletes the sessions next — as it does when they live\n * elsewhere, in Redis.\n */\nexport function sessionsTable(table: TableFactory) {\n\treturn table(\n\t\t'sessions',\n\t\t{\n\t\t\tid: key('id').primaryKey(),\n\t\t\t/** `sha256` of the session token. The secret itself is never stored. */\n\t\t\ttokenHash: key('token_hash').notNull(),\n\t\t\tuserId: key('user_id').notNull(),\n\t\t\tauthenticatedAt: at('authenticated_at').notNull(),\n\t\t\texpiresAt: at('expires_at').notNull(),\n\t\t\trevokedAt: at('revoked_at'),\n\t\t\tcreatedAt: at('created_at').notNull(),\n\t\t},\n\t\t(t) => [\n\t\t\tunique('sessions_token_hash_unique').on(t.tokenHash),\n\t\t\tindex('sessions_user_id').on(t.userId),\n\t\t\t/** `deleteExpiredSessions`: PostgreSQL has no TTL, so the core collects. */\n\t\t\tindex('sessions_expires_at').on(t.expiresAt),\n\t\t],\n\t);\n}\n",
|
|
10
10
|
"import { sql } from 'drizzle-orm';\nimport { check, index, integer, text } from 'drizzle-orm/pg-core';\nimport { at, key, type TableFactory } from './columns';\n\n/** One-time tokens, keyed by their hash. */\nexport function tokensTable(table: TableFactory) {\n\treturn table(\n\t\t'tokens',\n\t\t{\n\t\t\ttokenHash: key('token_hash').primaryKey(),\n\t\t\tkind: text('kind', {\n\t\t\t\tenum: ['verifyEmail', 'resetPassword', 'secondFactor', 'signInCode'],\n\t\t\t}).notNull(),\n\t\t\tuserId: key('user_id').notNull(),\n\t\t\taddress: text('address').notNull(),\n\t\t\t/** A sign-in code's hash, keyed by the token's secret. */\n\t\t\tcodeHash: text('code_hash'),\n\t\t\t/** Codes tried against it. The default fills the rows a migration finds. */\n\t\t\tattempts: integer('attempts').notNull().default(0),\n\t\t\texpiresAt: at('expires_at').notNull(),\n\t\t\tspentAt: at('spent_at'),\n\t\t\tcreatedAt: at('created_at').notNull(),\n\t\t},\n\t\t(t) => [\n\t\t\tcheck(\n\t\t\t\t'tokens_kind',\n\t\t\t\tsql`${t.kind} in ('verifyEmail', 'resetPassword', 'secondFactor', 'signInCode')`,\n\t\t\t),\n\t\t\tcheck('tokens_attempts', sql`${t.attempts} >= 0`),\n\t\t\tindex('tokens_user_id').on(t.userId),\n\t\t],\n\t);\n}\n",
|
|
11
|
-
"import { sql } from 'drizzle-orm';\nimport {\n\tboolean,\n\tcheck,\n\tindex,\n\tinteger,\n\tjsonb,\n\ttext,\n\tunique,\n} from 'drizzle-orm/pg-core';\nimport { at, key, type TableFactory } from './columns';\n\n/**\n * Users and their passwords: one row each. `logins` is the port's array,\n * verbatim and in order; its uniqueness is `logins`' primary key.\n */\nexport function usersTable(table: TableFactory) {\n\treturn table(\n\t\t'users',\n\t\t{\n\t\t\t/** The UUIDv7 the core minted. `text`, not `uuid`: the store parses nothing. */\n\t\t\tid: key('id').primaryKey(),\n\t\t\ttype: key('type').notNull(),\n\t\t\tschemaVersion: text('schema_version').notNull(),\n\t\t\tactive: boolean('active').notNull(),\n\t\t\t/** The application's fields. The core validated them already. */\n\t\t\tfields: jsonb('fields').notNull(),\n\t\t\tlogins: text('logins').array().notNull(),\n\t\t\tpasswordHash: text('password_hash'),\n\t\t\tpasswordUpdatedAt: at('password_updated_at'),\n\t\t\t/** `'totp'`, or `null` for a user with no second factor. */\n\t\t\tsecondFactorMethod: text('second_factor_method', { enum: ['totp'] }),\n\t\t\t/** Sealed by the core with the application's key: never the plain secret. */\n\t\t\tsecondFactorSecret: text('second_factor_secret'),\n\t\t\tsecondFactorConfirmedAt: at('second_factor_confirmed_at'),\n\t\t\tsecondFactorLastStep: integer('second_factor_last_step'),\n\t\t\temailVerifiedAt: at('email_verified_at'),\n\t\t\tversion: integer('version').notNull(),\n\t\t\tcreatedAt: at('created_at').notNull(),\n\t\t\tupdatedAt: at('updated_at').notNull(),\n\t\t},\n\t\t(t) => [\n\t\t\t/** What `logins` references, so a login's type is its user's. */\n\t\t\tunique('users_id_type_unique').on(t.id, t.type),\n\t\t\t/** `listUsers` reads one type in id order. */\n\t\t\tindex('users_type_id').on(t.type, t.id),\n\t\t\t/** A password is a hash and when it was set, or neither. */\n\t\t\tcheck(\n\t\t\t\t'users_password_whole',\n\t\t\t\tsql`(${t.passwordHash} is null) = (${t.passwordUpdatedAt} is null)`,\n\t\t\t),\n\t\t\t/**\n\t\t\t * A second factor is a method and a secret, or neither; its\n\t\t\t * confirmation
|
|
11
|
+
"import { sql } from 'drizzle-orm';\nimport {\n\tboolean,\n\tcheck,\n\tindex,\n\tinteger,\n\tjsonb,\n\ttext,\n\tunique,\n} from 'drizzle-orm/pg-core';\nimport { at, key, type TableFactory } from './columns';\n\n/**\n * Users and their passwords: one row each. `logins` is the port's array,\n * verbatim and in order; its uniqueness is `logins`' primary key.\n */\nexport function usersTable(table: TableFactory) {\n\treturn table(\n\t\t'users',\n\t\t{\n\t\t\t/** The UUIDv7 the core minted. `text`, not `uuid`: the store parses nothing. */\n\t\t\tid: key('id').primaryKey(),\n\t\t\ttype: key('type').notNull(),\n\t\t\tschemaVersion: text('schema_version').notNull(),\n\t\t\tactive: boolean('active').notNull(),\n\t\t\t/** The application's fields. The core validated them already. */\n\t\t\tfields: jsonb('fields').notNull(),\n\t\t\tlogins: text('logins').array().notNull(),\n\t\t\tpasswordHash: text('password_hash'),\n\t\t\tpasswordUpdatedAt: at('password_updated_at'),\n\t\t\t/** `'totp'`, or `null` for a user with no second factor. */\n\t\t\tsecondFactorMethod: text('second_factor_method', { enum: ['totp'] }),\n\t\t\t/** Sealed by the core with the application's key: never the plain secret. */\n\t\t\tsecondFactorSecret: text('second_factor_secret'),\n\t\t\tsecondFactorConfirmedAt: at('second_factor_confirmed_at'),\n\t\t\tsecondFactorLastStep: integer('second_factor_last_step'),\n\t\t\t/**\n\t\t\t * The recovery codes' keyed hashes, in order — `null` for none, read\n\t\t\t * as `[]`, so a row written before the column existed reads as no\n\t\t\t * codes.\n\t\t\t */\n\t\t\tsecondFactorRecoveryCodes: text('second_factor_recovery_codes').array(),\n\t\t\temailVerifiedAt: at('email_verified_at'),\n\t\t\tversion: integer('version').notNull(),\n\t\t\tcreatedAt: at('created_at').notNull(),\n\t\t\tupdatedAt: at('updated_at').notNull(),\n\t\t},\n\t\t(t) => [\n\t\t\t/** What `logins` references, so a login's type is its user's. */\n\t\t\tunique('users_id_type_unique').on(t.id, t.type),\n\t\t\t/** `listUsers` reads one type in id order. */\n\t\t\tindex('users_type_id').on(t.type, t.id),\n\t\t\t/** A password is a hash and when it was set, or neither. */\n\t\t\tcheck(\n\t\t\t\t'users_password_whole',\n\t\t\t\tsql`(${t.passwordHash} is null) = (${t.passwordUpdatedAt} is null)`,\n\t\t\t),\n\t\t\t/**\n\t\t\t * A second factor is a method and a secret, or neither; its\n\t\t\t * confirmation, last step and recovery codes exist only beside them.\n\t\t\t */\n\t\t\tcheck(\n\t\t\t\t'users_second_factor_whole',\n\t\t\t\tsql`(${t.secondFactorMethod} is null) = (${t.secondFactorSecret} is null) and (${t.secondFactorMethod} is not null or (${t.secondFactorConfirmedAt} is null and ${t.secondFactorLastStep} is null and ${t.secondFactorRecoveryCodes} is null))`,\n\t\t\t),\n\t\t\t/** `'totp'` is the one method; a step counts up from the epoch. */\n\t\t\tcheck(\n\t\t\t\t'users_second_factor_values',\n\t\t\t\tsql`${t.secondFactorMethod} in ('totp') and ${t.secondFactorLastStep} >= 0`,\n\t\t\t),\n\t\t],\n\t);\n}\n",
|
|
12
12
|
"import { toDataError } from '@nxgt/drizzle';\nimport { JanusError, StoreConflict, StoreFailure } from '@nxgt/janus';\n\n/**\n * **The database's errors, as the port's.** One file, and one default.\n *\n * | What happens | Becomes |\n * | --- | --- |\n * | A login another user of the type holds | `StoreConflict('login')`, carrying `login` and `userType` |\n * | A row with this id already there | nothing: the insert is a retry, and answers what is stored |\n * | A unique violation on **any other constraint** | **`StoreFailure`** |\n * | A check or foreign key refusing a row | **`StoreFailure`** |\n * | Anything else, absolutely | `StoreFailure`, with `@nxgt/drizzle`'s `DataError` as `cause` |\n *\n * The first two are never errors here: a login is written with `on conflict\n * do nothing`, and a login the statement did not write is one somebody else\n * holds — so no conflict is ever told apart by parsing a driver's message.\n *\n * The bold rows go against the obvious reading. A violation the adapter did\n * not mean to cause is an adapter bug, and reporting it as `LOGIN_TAKEN`\n * would tell somebody their login is taken when it is not. The core\n * validated every record before it reached a store, so a constraint refusing\n * one means the adapter and the port disagree — never the caller's fault.\n *\n * **The default is a failure, not an absence.** That branch is what keeps the\n * port's first rule: nothing here answers `null` for an error.\n */\n\nexport type Slot = 'users' | 'sessions' | 'tokens' | 'relations';\n\n/** Runs one port method, and makes every rejection one the port allows. */\nexport async function run<T>(\n\tslot: Slot,\n\toperation: string,\n\tbody: () => Promise<T>,\n): Promise<T> {\n\ttry {\n\t\treturn await body();\n\t} catch (error) {\n\t\t// Already the port's — a conflict, a NOT_FOUND after a read.\n\t\tif (error instanceof JanusError) throw error;\n\t\tthrow new StoreFailure(`${slot}.${operation}: the store could not answer`, {\n\t\t\tslot,\n\t\t\toperation,\n\t\t\tcause: toDataError(error),\n\t\t});\n\t}\n}\n\n/** The refusal the port promises for a taken login. */\nexport function loginTaken(\n\toperation: string,\n\ttype: string,\n\tlogin: string,\n): StoreConflict {\n\treturn new StoreConflict(\n\t\t'login',\n\t\t`${operation}: the login is taken by another ${type}`,\n\t\t{ login, userType: type, operation },\n\t);\n}\n",
|
|
13
13
|
"import type {\n\tCursorPage,\n\tEntity,\n\tRelationTuple,\n\tSubjectSet,\n} from '@nxgt/janus';\nimport type { ObjectPageRequest } from '@nxgt/janus/permissions';\nimport { and, asc, eq, gt, isNotNull, isNull } from 'drizzle-orm';\nimport type { RelationContext } from './context';\nimport { matching, subjectIs } from './tuples';\n\nexport async function has(\n\t{ db, relations }: RelationContext,\n\ttuple: RelationTuple,\n): Promise<boolean> {\n\tconst found = await db\n\t\t.select({ relation: relations.relation })\n\t\t.from(relations)\n\t\t.where(matching(relations, tuple))\n\t\t.limit(1);\n\treturn found.length === 1;\n}\n\nexport async function findSubjectSets(\n\tcontext: RelationContext,\n\tobject: Entity,\n\trelation: string,\n): Promise<SubjectSet[]> {\n\treturn (await holding(context, object, relation, true)).map(\n\t\t(row): SubjectSet => ({\n\t\t\ttype: row.type,\n\t\t\tid: row.id,\n\t\t\trelation: row.relation ?? '',\n\t\t}),\n\t);\n}\n\nexport async function findEntities(\n\tcontext: RelationContext,\n\tobject: Entity,\n\trelation: string,\n): Promise<Entity[]> {\n\treturn (await holding(context, object, relation, false)).map(\n\t\t(row): Entity => ({ type: row.type, id: row.id }),\n\t);\n}\n\n/** One hop from an object: its subject sets, or its entities. */\nfunction holding(\n\t{ db, relations }: RelationContext,\n\tobject: Entity,\n\trelation: string,\n\tsets: boolean,\n) {\n\treturn db\n\t\t.select({\n\t\t\ttype: relations.subjectType,\n\t\t\tid: relations.subjectId,\n\t\t\trelation: relations.subjectRelation,\n\t\t})\n\t\t.from(relations)\n\t\t.where(\n\t\t\tand(\n\t\t\t\teq(relations.objectType, object.type),\n\t\t\t\teq(relations.objectId, object.id),\n\t\t\t\teq(relations.relation, relation),\n\t\t\t\tsets\n\t\t\t\t\t? isNotNull(relations.subjectRelation)\n\t\t\t\t\t: isNull(relations.subjectRelation),\n\t\t\t),\n\t\t);\n}\n\n/** A page of object ids, in id order, after the cursor. */\nexport async function findObjects(\n\t{ db, relations }: RelationContext,\n\t{ type, relation, subject, after, limit }: ObjectPageRequest,\n): Promise<CursorPage<string>> {\n\tconst conditions = [\n\t\tsubjectIs(relations, subject),\n\t\teq(relations.relation, relation),\n\t\teq(relations.objectType, type),\n\t];\n\tif (after !== null) conditions.push(gt(relations.objectId, after));\n\tconst found = await db\n\t\t.select({ id: relations.objectId })\n\t\t.from(relations)\n\t\t.where(and(...conditions))\n\t\t.orderBy(asc(relations.objectId))\n\t\t.limit(limit + 1);\n\tconst items = found.slice(0, limit).map((row) => row.id);\n\tconst last = items.at(-1);\n\treturn {\n\t\titems,\n\t\tnextCursor: found.length > limit && last !== undefined ? last : null,\n\t};\n}\n",
|
|
14
14
|
"import { isSubjectSet, type RelationTuple, type Subject } from '@nxgt/janus';\nimport { and, eq, isNull, type SQL } from 'drizzle-orm';\nimport type { Relations } from './context';\n\n/** Tuples in one order: their notation, compared by code unit. */\nexport function inOrder(\n\ttuples: readonly RelationTuple[],\n): readonly RelationTuple[] {\n\tconst keyOf = (tuple: RelationTuple) =>\n\t\tJSON.stringify([\n\t\t\ttuple.object.type,\n\t\t\ttuple.object.id,\n\t\t\ttuple.relation,\n\t\t\ttuple.subject.type,\n\t\t\ttuple.subject.id,\n\t\t\tisSubjectSet(tuple.subject) ? tuple.subject.relation : null,\n\t\t]);\n\treturn [...tuples].sort((a, b) => {\n\t\tconst [x, y] = [keyOf(a), keyOf(b)];\n\t\treturn x < y ? -1 : x > y ? 1 : 0;\n\t});\n}\n\n/** A tuple as a row: an entity's `subject_relation` is `null`. */\nexport function rowOf(tuple: RelationTuple): Relations['$inferInsert'] {\n\treturn {\n\t\tobjectType: tuple.object.type,\n\t\tobjectId: tuple.object.id,\n\t\trelation: tuple.relation,\n\t\tsubjectType: tuple.subject.type,\n\t\tsubjectId: tuple.subject.id,\n\t\tsubjectRelation: isSubjectSet(tuple.subject)\n\t\t\t? tuple.subject.relation\n\t\t\t: null,\n\t};\n}\n\n/** Exactly this subject: a subject set is not its entity, and the reverse. */\nexport function subjectIs(\n\trelations: Relations,\n\tsubject: Subject,\n): SQL | undefined {\n\treturn and(\n\t\teq(relations.subjectType, subject.type),\n\t\teq(relations.subjectId, subject.id),\n\t\tisSubjectSet(subject)\n\t\t\t? eq(relations.subjectRelation, subject.relation)\n\t\t\t: isNull(relations.subjectRelation),\n\t);\n}\n\n/** Exactly this tuple. */\nexport function matching(\n\trelations: Relations,\n\ttuple: RelationTuple,\n): SQL | undefined {\n\treturn and(\n\t\teq(relations.objectType, tuple.object.type),\n\t\teq(relations.objectId, tuple.object.id),\n\t\teq(relations.relation, tuple.relation),\n\t\tsubjectIs(relations, tuple.subject),\n\t);\n}\n",
|
|
15
15
|
"import { type PgDatabase, withTransaction } from '@nxgt/drizzle/pg';\nimport type { Entity, RelationTuple } from '@nxgt/janus';\nimport { and, eq, or } from 'drizzle-orm';\nimport type { RelationContext, Relations } from './context';\nimport { inOrder, matching, rowOf } from './tuples';\n\n/** One tuple in one statement; more in one transaction. */\nexport async function write(\n\t{ db, relations }: RelationContext,\n\tadd: readonly RelationTuple[],\n\tremove: readonly RelationTuple[],\n): Promise<void> {\n\tif (add.length + remove.length <= 1) return apply(db, relations, add, remove);\n\tawait withTransaction(db, (tx) => apply(tx, relations, add, remove));\n}\n\n/** Removals first, then additions. */\nasync function apply(\n\ttx: PgDatabase,\n\trelations: Relations,\n\tadd: readonly RelationTuple[],\n\tremove: readonly RelationTuple[],\n): Promise<void> {\n\t// In one order, so two writes touching the same tuples take their locks\n\t// alike and never deadlock.\n\tfor (const tuple of inOrder(remove)) {\n\t\tawait tx.delete(relations).where(matching(relations, tuple));\n\t}\n\tif (add.length > 0) {\n\t\t// A stored tuple meets the unique constraint, and is a no-op.\n\t\tawait tx\n\t\t\t.insert(relations)\n\t\t\t.values(inOrder(add).map(rowOf))\n\t\t\t.onConflictDoNothing();\n\t}\n}\n\n/** Every tuple naming the entity, as object or as subject. */\nexport async function deleteEntity(\n\t{ db, relations }: RelationContext,\n\tentity: Entity,\n): Promise<number> {\n\tconst deleted = await db\n\t\t.delete(relations)\n\t\t.where(\n\t\t\tor(\n\t\t\t\tand(\n\t\t\t\t\teq(relations.objectType, entity.type),\n\t\t\t\t\teq(relations.objectId, entity.id),\n\t\t\t\t),\n\t\t\t\tand(\n\t\t\t\t\teq(relations.subjectType, entity.type),\n\t\t\t\t\teq(relations.subjectId, entity.id),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\t.returning({ relation: relations.relation });\n\treturn deleted.length;\n}\n",
|
|
16
16
|
"import type { PgDatabase } from '@nxgt/drizzle/pg';\nimport type { RelationStore } from '@nxgt/janus/permissions';\nimport { type DrizzleAdapterOptions, defineJanusTables } from '../tables';\nimport { run } from '../translate';\nimport type { RelationContext } from './context';\nimport { findEntities, findObjects, findSubjectSets, has } from './reads';\nimport { deleteEntity, write } from './writes';\n\n/**\n * The relation store `permissions()` takes, over one PostgreSQL database\n * through Drizzle — and the one `janus({ relations })` deletes a user's\n * tuples from.\n *\n * ```ts\n * const access = permissions({ model, store: createDrizzleRelations(db) });\n * ```\n *\n * **A write of more than one tuple is a transaction**, `@nxgt/drizzle`'s\n * `withTransaction`: removals first, then additions, all or nothing. It is not\n * retried here — a write is idempotent, so the caller retries.\n */\nexport function createDrizzleRelations(\n\tdb: PgDatabase,\n\toptions: DrizzleAdapterOptions<'relations'> = {},\n): RelationStore {\n\tconst tables = options.tables ?? defineJanusTables();\n\tconst context: RelationContext = { db, relations: tables.relations };\n\tconst run$ = <T>(operation: string, body: () => Promise<T>) =>\n\t\trun('relations', operation, body);\n\n\treturn {\n\t\twrite: ({ add = [], remove = [] }) =>\n\t\t\trun$('write', () => write(context, add, remove)),\n\t\thas: (tuple) => run$('has', () => has(context, tuple)),\n\t\tfindSubjectSets: (object, relation) =>\n\t\t\trun$('findSubjectSets', () => findSubjectSets(context, object, relation)),\n\t\tfindEntities: (object, relation) =>\n\t\t\trun$('findEntities', () => findEntities(context, object, relation)),\n\t\t// The request is unpacked before `run$`, as `write`'s is and as it was\n\t\t// before the split, so a malformed request throws the caller's\n\t\t// TypeError at once instead of reading as an outage.\n\t\tfindObjects: ({ type, relation, subject, after, limit }) =>\n\t\t\trun$('findObjects', () =>\n\t\t\t\tfindObjects(context, { type, relation, subject, after, limit }),\n\t\t\t),\n\t\tdeleteEntity: (entity) =>\n\t\t\trun$('deleteEntity', () => deleteEntity(context, entity)),\n\t};\n}\n",
|
|
17
17
|
"import type { PgDatabase } from '@nxgt/drizzle/pg';\nimport type { SessionStore } from '@nxgt/janus';\nimport { and, eq, isNull, lte, ne, sql } from 'drizzle-orm';\nimport { run } from '../translate';\nimport type { IdentityTables } from './identity-tables';\nimport { stamp, toSession } from './records';\n\nexport function sessionStore(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n): SessionStore {\n\tconst run$ = <T>(operation: string, body: () => Promise<T>) =>\n\t\trun('sessions', operation, body);\n\n\treturn {\n\t\tinsertSession: (record) =>\n\t\t\trun$('insertSession', async () => {\n\t\t\t\t// Idempotent under retry: a session with this id is already there.\n\t\t\t\t// A collision on the token hash is not a retry, and fails.\n\t\t\t\tawait db\n\t\t\t\t\t.insert(tables.sessions)\n\t\t\t\t\t.values(record)\n\t\t\t\t\t.onConflictDoNothing({ target: tables.sessions.id });\n\t\t\t}),\n\n\t\tfindSessionByTokenHash: (tokenHash) =>\n\t\t\trun$('findSessionByTokenHash', async () => {\n\t\t\t\tconst [row] = await db\n\t\t\t\t\t.select()\n\t\t\t\t\t.from(tables.sessions)\n\t\t\t\t\t.where(eq(tables.sessions.tokenHash, tokenHash));\n\t\t\t\treturn row === undefined ? null : toSession(row);\n\t\t\t}),\n\n\t\textendSession: (id, expiresAt) =>\n\t\t\trun$('extendSession', async () => {\n\t\t\t\t// `revoked_at is null` in the condition: an extension racing a\n\t\t\t\t// revocation matches nothing, and never brings the session back.\n\t\t\t\tconst [row] = await db\n\t\t\t\t\t.update(tables.sessions)\n\t\t\t\t\t.set({ expiresAt })\n\t\t\t\t\t.where(\n\t\t\t\t\t\tand(eq(tables.sessions.id, id), isNull(tables.sessions.revokedAt)),\n\t\t\t\t\t)\n\t\t\t\t\t.returning();\n\t\t\t\treturn row === undefined ? null : toSession(row);\n\t\t\t}),\n\n\t\trevokeSession: (id, at) =>\n\t\t\trun$('revokeSession', () => revokeSession(db, tables, id, at)),\n\n\t\trevokeUserSessions: (userId, at, except) =>\n\t\t\trun$('revokeUserSessions', () =>\n\t\t\t\trevokeUserSessions(db, tables, userId, at, except),\n\t\t\t),\n\n\t\tdeleteUserSessions: (userId) =>\n\t\t\trun$('deleteUserSessions', async () => {\n\t\t\t\tconst deleted = await db\n\t\t\t\t\t.delete(tables.sessions)\n\t\t\t\t\t.where(eq(tables.sessions.userId, userId))\n\t\t\t\t\t.returning({ id: tables.sessions.id });\n\t\t\t\treturn deleted.length;\n\t\t\t}),\n\n\t\tdeleteExpiredSessions: (before) =>\n\t\t\trun$('deleteExpiredSessions', async () => {\n\t\t\t\tconst deleted = await db\n\t\t\t\t\t.delete(tables.sessions)\n\t\t\t\t\t.where(lte(tables.sessions.expiresAt, before))\n\t\t\t\t\t.returning({ id: tables.sessions.id });\n\t\t\t\treturn deleted.length;\n\t\t\t}),\n\t};\n}\n\nasync function revokeSession(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n\tid: string,\n\tat: Date,\n): Promise<boolean> {\n\t// `coalesce`, so a session already revoked keeps its first\n\t// `revokedAt` and still counts as matched.\n\tconst matched = await db\n\t\t.update(tables.sessions)\n\t\t.set({\n\t\t\trevokedAt: sql`coalesce(${tables.sessions.revokedAt}, ${stamp(at)})`,\n\t\t})\n\t\t.where(eq(tables.sessions.id, id))\n\t\t.returning({ id: tables.sessions.id });\n\treturn matched.length === 1;\n}\n\nasync function revokeUserSessions(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n\tuserId: string,\n\tat: Date,\n\texcept: string | undefined,\n): Promise<number> {\n\tconst standing = and(\n\t\teq(tables.sessions.userId, userId),\n\t\tisNull(tables.sessions.revokedAt),\n\t);\n\tconst revoked = await db\n\t\t.update(tables.sessions)\n\t\t.set({ revokedAt: at })\n\t\t.where(\n\t\t\texcept === undefined\n\t\t\t\t? standing\n\t\t\t\t: and(standing, ne(tables.sessions.id, except)),\n\t\t)\n\t\t.returning({ id: tables.sessions.id });\n\treturn revoked.length;\n}\n",
|
|
18
|
-
"import type { Id, SessionRecord, UserPatch, UserRecord } from '@nxgt/janus';\nimport { type SQL, sql } from 'drizzle-orm';\nimport type { JanusTables } from '../tables';\n\n// ─── Rows and records ─────────────────────────────────────────────────────\n//\n// Each record is rebuilt field by field: the port's records are exactly what\n// it declares, and a row carries columns the record names differently.\n\ntype UserRow = JanusTables['users']['$inferSelect'];\ntype UserInsert = JanusTables['users']['$inferInsert'];\ntype SessionRow = JanusTables['sessions']['$inferSelect'];\n\nexport function toUserRow(record: UserRecord): UserInsert {\n\treturn {\n\t\tid: record.id,\n\t\ttype: record.type,\n\t\tschemaVersion: record.schemaVersion,\n\t\tactive: record.active,\n\t\tfields: record.fields,\n\t\tlogins: [...record.logins],\n\t\tpasswordHash: record.password?.hash ?? null,\n\t\tpasswordUpdatedAt: record.password?.updatedAt ?? null,\n\t\t...toSecondFactorColumns(record.secondFactor),\n\t\temailVerifiedAt: record.emailVerifiedAt,\n\t\tversion: record.version,\n\t\tcreatedAt: record.createdAt,\n\t\tupdatedAt: record.updatedAt,\n\t};\n}\n\n/**\n * The columns a patch names. A key present with `undefined` is absent, as the\n * port says — never an erasure. `password: null` is named, and removes the\n * password.\n */\nexport function toUserSet(patch: UserPatch): Partial<UserInsert> {\n\tconst set: Partial<UserInsert> = {\n\t\tupdatedAt: patch.updatedAt,\n\t};\n\tif (patch.schemaVersion !== undefined)\n\t\tset.schemaVersion = patch.schemaVersion;\n\tif (patch.active !== undefined) set.active = patch.active;\n\tif (patch.fields !== undefined) set.fields = patch.fields;\n\tif (patch.logins !== undefined) set.logins = [...patch.logins];\n\tif (patch.emailVerifiedAt !== undefined)\n\t\tset.emailVerifiedAt = patch.emailVerifiedAt;\n\tif (patch.password !== undefined) {\n\t\tset.passwordHash = patch.password?.hash ?? null;\n\t\tset.passwordUpdatedAt = patch.password?.updatedAt ?? null;\n\t}\n\tif (patch.secondFactor !== undefined) {\n\t\tObject.assign(set, toSecondFactorColumns(patch.secondFactor));\n\t}\n\treturn set;\n}\n\n
|
|
18
|
+
"import type { Id, SessionRecord, UserPatch, UserRecord } from '@nxgt/janus';\nimport { type SQL, sql } from 'drizzle-orm';\nimport type { JanusTables } from '../tables';\n\n// ─── Rows and records ─────────────────────────────────────────────────────\n//\n// Each record is rebuilt field by field: the port's records are exactly what\n// it declares, and a row carries columns the record names differently.\n\ntype UserRow = JanusTables['users']['$inferSelect'];\ntype UserInsert = JanusTables['users']['$inferInsert'];\ntype SessionRow = JanusTables['sessions']['$inferSelect'];\n\nexport function toUserRow(record: UserRecord): UserInsert {\n\treturn {\n\t\tid: record.id,\n\t\ttype: record.type,\n\t\tschemaVersion: record.schemaVersion,\n\t\tactive: record.active,\n\t\tfields: record.fields,\n\t\tlogins: [...record.logins],\n\t\tpasswordHash: record.password?.hash ?? null,\n\t\tpasswordUpdatedAt: record.password?.updatedAt ?? null,\n\t\t...toSecondFactorColumns(record.secondFactor),\n\t\temailVerifiedAt: record.emailVerifiedAt,\n\t\tversion: record.version,\n\t\tcreatedAt: record.createdAt,\n\t\tupdatedAt: record.updatedAt,\n\t};\n}\n\n/**\n * The columns a patch names. A key present with `undefined` is absent, as the\n * port says — never an erasure. `password: null` is named, and removes the\n * password.\n */\nexport function toUserSet(patch: UserPatch): Partial<UserInsert> {\n\tconst set: Partial<UserInsert> = {\n\t\tupdatedAt: patch.updatedAt,\n\t};\n\tif (patch.schemaVersion !== undefined)\n\t\tset.schemaVersion = patch.schemaVersion;\n\tif (patch.active !== undefined) set.active = patch.active;\n\tif (patch.fields !== undefined) set.fields = patch.fields;\n\tif (patch.logins !== undefined) set.logins = [...patch.logins];\n\tif (patch.emailVerifiedAt !== undefined)\n\t\tset.emailVerifiedAt = patch.emailVerifiedAt;\n\tif (patch.password !== undefined) {\n\t\tset.passwordHash = patch.password?.hash ?? null;\n\t\tset.passwordUpdatedAt = patch.password?.updatedAt ?? null;\n\t}\n\tif (patch.secondFactor !== undefined) {\n\t\tObject.assign(set, toSecondFactorColumns(patch.secondFactor));\n\t}\n\treturn set;\n}\n\n/**\n * A second factor as its five columns, all `null` for none. No recovery\n * codes are written `null` too, never `{}`, and read back as `[]`: a row\n * then holds a code array only while it holds codes, so an instance of 0.3\n * — which knows four columns — can still remove a factor that has none.\n */\nfunction toSecondFactorColumns(secondFactor: UserRecord['secondFactor']) {\n\tconst codes = secondFactor?.recoveryCodes ?? [];\n\treturn {\n\t\tsecondFactorMethod: secondFactor?.method ?? null,\n\t\tsecondFactorSecret: secondFactor?.secret ?? null,\n\t\tsecondFactorConfirmedAt: secondFactor?.confirmedAt ?? null,\n\t\tsecondFactorLastStep: secondFactor?.lastStep ?? null,\n\t\tsecondFactorRecoveryCodes: codes.length === 0 ? null : [...codes],\n\t};\n}\n\nexport function toUser(row: UserRow): UserRecord {\n\treturn {\n\t\tid: row.id as Id,\n\t\ttype: row.type,\n\t\tschemaVersion: row.schemaVersion,\n\t\tactive: row.active,\n\t\tfields: row.fields as UserRecord['fields'],\n\t\tlogins: [...row.logins],\n\t\tpassword:\n\t\t\trow.passwordHash === null || row.passwordUpdatedAt === null\n\t\t\t\t? null\n\t\t\t\t: { hash: row.passwordHash, updatedAt: row.passwordUpdatedAt },\n\t\tsecondFactor:\n\t\t\trow.secondFactorMethod === null || row.secondFactorSecret === null\n\t\t\t\t? null\n\t\t\t\t: {\n\t\t\t\t\t\tmethod: row.secondFactorMethod,\n\t\t\t\t\t\tsecret: row.secondFactorSecret,\n\t\t\t\t\t\tconfirmedAt: row.secondFactorConfirmedAt,\n\t\t\t\t\t\tlastStep: row.secondFactorLastStep,\n\t\t\t\t\t\trecoveryCodes: [...(row.secondFactorRecoveryCodes ?? [])],\n\t\t\t\t\t},\n\t\temailVerifiedAt: row.emailVerifiedAt,\n\t\tversion: row.version,\n\t\tcreatedAt: row.createdAt,\n\t\tupdatedAt: row.updatedAt,\n\t};\n}\n\nexport function toSession(row: SessionRow): SessionRecord {\n\treturn {\n\t\tid: row.id,\n\t\ttokenHash: row.tokenHash,\n\t\tuserId: row.userId as Id,\n\t\tauthenticatedAt: row.authenticatedAt,\n\t\texpiresAt: row.expiresAt,\n\t\trevokedAt: row.revokedAt,\n\t\tcreatedAt: row.createdAt,\n\t};\n}\n\n/**\n * A `Date` inside a `sql` template, as every driver takes it: `timestamptz`\n * from its ISO string. Drizzle's column mapping does not reach a raw\n * template, and postgres.js refuses a `Date` object there.\n */\nexport function stamp(at: Date): SQL {\n\treturn sql`${at.toISOString()}::timestamptz`;\n}\n",
|
|
19
19
|
"import type { PgDatabase } from '@nxgt/drizzle/pg';\nimport type { Id, TokenKind, TokenRecord, TokenStore } from '@nxgt/janus';\nimport { and, eq, isNull, ne, sql } from 'drizzle-orm';\nimport { run } from '../translate';\nimport type { IdentityTables } from './identity-tables';\nimport { stamp } from './records';\n\nexport function tokenStore(db: PgDatabase, tables: IdentityTables): TokenStore {\n\tconst run$ = <T>(operation: string, body: () => Promise<T>) =>\n\t\trun('tokens', operation, body);\n\n\treturn {\n\t\tinsertToken: (record) =>\n\t\t\trun$('insertToken', async () => {\n\t\t\t\t// The hash is the key: a collision is a retry.\n\t\t\t\tawait db.insert(tables.tokens).values(record).onConflictDoNothing();\n\t\t\t}),\n\n\t\tconsumeToken: (tokenHash, kind, at) =>\n\t\t\trun$('consumeToken', () => consumeToken(db, tables, tokenHash, kind, at)),\n\n\t\tcountAttempt: (tokenHash, kind) =>\n\t\t\trun$('countAttempt', () => countAttempt(db, tables, tokenHash, kind)),\n\n\t\tspendUserTokens: (userId, kind, at, except) =>\n\t\t\trun$('spendUserTokens', async () => {\n\t\t\t\t// One statement: each row is written under its lock, and\n\t\t\t\t// re-checked after a concurrent `consumeToken` committed, so the\n\t\t\t\t// two never both spend it.\n\t\t\t\tconst spent = await db\n\t\t\t\t\t.update(tables.tokens)\n\t\t\t\t\t.set({ spentAt: at })\n\t\t\t\t\t.where(\n\t\t\t\t\t\tand(\n\t\t\t\t\t\t\teq(tables.tokens.userId, userId),\n\t\t\t\t\t\t\teq(tables.tokens.kind, kind),\n\t\t\t\t\t\t\tisNull(tables.tokens.spentAt),\n\t\t\t\t\t\t\texcept === undefined\n\t\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t\t: ne(tables.tokens.tokenHash, except),\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\t.returning({ tokenHash: tables.tokens.tokenHash });\n\t\t\t\treturn spent.length;\n\t\t\t}),\n\n\t\tdeleteUserTokens: (userId) =>\n\t\t\trun$('deleteUserTokens', async () => {\n\t\t\t\tconst deleted = await db\n\t\t\t\t\t.delete(tables.tokens)\n\t\t\t\t\t.where(eq(tables.tokens.userId, userId))\n\t\t\t\t\t.returning({ tokenHash: tables.tokens.tokenHash });\n\t\t\t\treturn deleted.length;\n\t\t\t}),\n\t};\n}\n\nasync function consumeToken(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n\ttokenHash: string,\n\tkind: TokenKind,\n\tat: Date,\n): Promise<TokenRecord | null> {\n\t// **One conditional write**, answering the row as it was before\n\t// it. `for update` makes a concurrent call wait for this one and\n\t// then read the spent row, so exactly one caller ever reads\n\t// `spentAt: null`; `coalesce` keeps a first `spentAt`.\n\tconst before = db.$with('before').as(\n\t\tdb\n\t\t\t.select()\n\t\t\t.from(tables.tokens)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(tables.tokens.tokenHash, tokenHash),\n\t\t\t\t\teq(tables.tokens.kind, kind),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.for('update'),\n\t);\n\tconst [spent] = await db\n\t\t.with(before)\n\t\t.update(tables.tokens)\n\t\t.set({\n\t\t\tspentAt: sql`coalesce(${tables.tokens.spentAt}, ${stamp(at)})`,\n\t\t})\n\t\t.from(before)\n\t\t.where(eq(tables.tokens.tokenHash, before.tokenHash))\n\t\t.returning({\n\t\t\ttokenHash: before.tokenHash,\n\t\t\tkind: before.kind,\n\t\t\tuserId: before.userId,\n\t\t\taddress: before.address,\n\t\t\tcodeHash: before.codeHash,\n\t\t\tattempts: before.attempts,\n\t\t\texpiresAt: before.expiresAt,\n\t\t\tspentAt: before.spentAt,\n\t\t\tcreatedAt: before.createdAt,\n\t\t});\n\treturn spent === undefined ? null : { ...spent, userId: spent.userId as Id };\n}\n\nasync function countAttempt(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n\ttokenHash: string,\n\tkind: TokenKind,\n): Promise<TokenRecord | null> {\n\t// **One conditional write**: `attempts + 1` on the row, under the\n\t// row's lock, so twenty concurrent calls answer twenty counts. A\n\t// spent token matches nothing here, and is read as it is below.\n\tconst match = and(\n\t\teq(tables.tokens.tokenHash, tokenHash),\n\t\teq(tables.tokens.kind, kind),\n\t);\n\tconst [counted] = await db\n\t\t.update(tables.tokens)\n\t\t.set({ attempts: sql`${tables.tokens.attempts} + 1` })\n\t\t.where(and(match, isNull(tables.tokens.spentAt)))\n\t\t.returning();\n\tconst row =\n\t\tcounted ?? (await db.select().from(tables.tokens).where(match))[0];\n\treturn row === undefined ? null : { ...row, userId: row.userId as Id };\n}\n",
|
|
20
20
|
"import { type PgDatabase, withTransaction } from '@nxgt/drizzle/pg';\nimport type { UserPatch, UserRecord, UserStore } from '@nxgt/janus';\nimport { NotFoundError, StoreConflict } from '@nxgt/janus';\nimport { and, asc, eq, gt, sql } from 'drizzle-orm';\nimport { run } from '../translate';\nimport type { IdentityTables } from './identity-tables';\nimport { claimLogins } from './logins';\nimport { toUser, toUserRow, toUserSet } from './records';\n\nexport function userStore(db: PgDatabase, tables: IdentityTables): UserStore {\n\tconst run$ = <T>(operation: string, body: () => Promise<T>) =>\n\t\trun('users', operation, body);\n\n\treturn {\n\t\tinsertUser: (record) =>\n\t\t\trun$('insertUser', () => insertUser(db, tables, record)),\n\n\t\tfindUser: (id) => run$('findUser', () => findUser(db, tables, id)),\n\n\t\tfindUserByLogin: (type, login) =>\n\t\t\trun$('findUserByLogin', async () => {\n\t\t\t\tconst [row] = await db\n\t\t\t\t\t.select({ user: tables.users })\n\t\t\t\t\t.from(tables.logins)\n\t\t\t\t\t.innerJoin(tables.users, eq(tables.users.id, tables.logins.userId))\n\t\t\t\t\t.where(\n\t\t\t\t\t\tand(eq(tables.logins.type, type), eq(tables.logins.login, login)),\n\t\t\t\t\t);\n\t\t\t\treturn row === undefined ? null : toUser(row.user);\n\t\t\t}),\n\n\t\tlistUsers: ({ type, after, limit }) =>\n\t\t\trun$('listUsers', async () => {\n\t\t\t\t// One more than the page, to know whether another follows.\n\t\t\t\tconst found = await db\n\t\t\t\t\t.select()\n\t\t\t\t\t.from(tables.users)\n\t\t\t\t\t.where(\n\t\t\t\t\t\tafter === null\n\t\t\t\t\t\t\t? eq(tables.users.type, type)\n\t\t\t\t\t\t\t: and(eq(tables.users.type, type), gt(tables.users.id, after)),\n\t\t\t\t\t)\n\t\t\t\t\t.orderBy(asc(tables.users.id))\n\t\t\t\t\t.limit(limit + 1);\n\t\t\t\tconst items = found.slice(0, limit).map(toUser);\n\t\t\t\tconst last = items.at(-1);\n\t\t\t\treturn {\n\t\t\t\t\titems,\n\t\t\t\t\tnextCursor:\n\t\t\t\t\t\tfound.length > limit && last !== undefined ? last.id : null,\n\t\t\t\t};\n\t\t\t}),\n\n\t\tupdateUser: (id, patch, ifVersion) =>\n\t\t\trun$('updateUser', () =>\n\t\t\t\twithTransaction(db, (tx) =>\n\t\t\t\t\tupdateUser(tx, tables, id, patch, ifVersion),\n\t\t\t\t),\n\t\t\t),\n\n\t\tdeleteUser: (id) =>\n\t\t\trun$('deleteUser', async () => {\n\t\t\t\t// The logins go with it: their foreign key cascades.\n\t\t\t\tconst deleted = await db\n\t\t\t\t\t.delete(tables.users)\n\t\t\t\t\t.where(eq(tables.users.id, id))\n\t\t\t\t\t.returning({ id: tables.users.id });\n\t\t\t\treturn deleted.length === 1;\n\t\t\t}),\n\t};\n}\n\nasync function findUser(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n\tid: string,\n): Promise<UserRecord | null> {\n\tconst [row] = await db\n\t\t.select()\n\t\t.from(tables.users)\n\t\t.where(eq(tables.users.id, id));\n\treturn row === undefined ? null : toUser(row);\n}\n\nasync function insertUser(\n\tdb: PgDatabase,\n\ttables: IdentityTables,\n\trecord: UserRecord,\n): Promise<UserRecord> {\n\tconst inserted = await withTransaction(db, async (tx) => {\n\t\tconst [row] = await tx\n\t\t\t.insert(tables.users)\n\t\t\t.values(toUserRow(record))\n\t\t\t.onConflictDoNothing({ target: tables.users.id })\n\t\t\t.returning();\n\t\tif (row === undefined) return null;\n\t\tawait claimLogins(tx, tables, 'insertUser', record, record.logins);\n\t\treturn toUser(row);\n\t});\n\tif (inserted !== null) return inserted;\n\n\t// A retry whose first attempt landed: the user with this id is\n\t// there, and is the answer — logins included, which are theirs.\n\tconst stored = await findUser(db, tables, record.id);\n\tif (stored !== null) return stored;\n\t// Deleted between the two statements: a retry of nothing.\n\tthrow new NotFoundError('insertUser: the user was deleted meanwhile', {\n\t\tuserId: record.id,\n\t\toperation: 'insertUser',\n\t});\n}\n\nasync function updateUser(\n\ttx: PgDatabase,\n\ttables: IdentityTables,\n\tid: string,\n\tpatch: UserPatch,\n\tifVersion: number,\n): Promise<UserRecord> {\n\tconst [row] = await tx\n\t\t.update(tables.users)\n\t\t.set({\n\t\t\t...toUserSet(patch),\n\t\t\tversion: sql`${tables.users.version} + 1`,\n\t\t})\n\t\t.where(and(eq(tables.users.id, id), eq(tables.users.version, ifVersion)))\n\t\t.returning();\n\n\tif (row === undefined) {\n\t\t// The write matched nothing, which alone cannot tell an unknown\n\t\t// id from a moved version: read again, and say which.\n\t\tconst [stored] = await tx\n\t\t\t.select({ version: tables.users.version })\n\t\t\t.from(tables.users)\n\t\t\t.where(eq(tables.users.id, id));\n\t\tif (stored === undefined) {\n\t\t\tthrow new NotFoundError('updateUser: no user has this id', {\n\t\t\t\tuserId: id,\n\t\t\t\toperation: 'updateUser',\n\t\t\t});\n\t\t}\n\t\tthrow new StoreConflict(\n\t\t\t'version',\n\t\t\t`updateUser: expected version ${ifVersion}, found ${stored.version}`,\n\t\t\t{\n\t\t\t\tuserId: id,\n\t\t\t\texpectedVersion: ifVersion,\n\t\t\t\tactualVersion: stored.version,\n\t\t\t\toperation: 'updateUser',\n\t\t\t},\n\t\t);\n\t}\n\n\tif (patch.logins !== undefined) {\n\t\tawait tx.delete(tables.logins).where(eq(tables.logins.userId, id));\n\t\tawait claimLogins(tx, tables, 'updateUser', row, patch.logins);\n\t}\n\treturn toUser(row);\n}\n",
|
|
21
21
|
"import type { PgDatabase } from '@nxgt/drizzle/pg';\nimport { loginTaken } from '../translate';\nimport type { IdentityTables } from './identity-tables';\n\n/**\n * Writes the logins of one user, and throws the port's conflict for the\n * first one another user of the type holds. `on conflict do nothing`: a\n * login the statement did not write is one somebody else holds, and a\n * concurrent sign-up waits for the first to commit, then writes nothing.\n */\nexport async function claimLogins(\n\ttx: PgDatabase,\n\ttables: IdentityTables,\n\toperation: string,\n\tuser: { readonly id: string; readonly type: string },\n\tlogins: readonly string[],\n): Promise<void> {\n\t// Sorted: two transactions claiming the same logins take their locks in\n\t// one order, and never deadlock — the loser waits, then finds them taken.\n\tconst distinct = [...new Set(logins)].sort();\n\tif (distinct.length === 0) return;\n\tconst written = await tx\n\t\t.insert(tables.logins)\n\t\t.values(\n\t\t\tdistinct.map((login) => ({ type: user.type, login, userId: user.id })),\n\t\t)\n\t\t.onConflictDoNothing()\n\t\t.returning({ login: tables.logins.login });\n\tif (written.length === distinct.length) return;\n\tconst claimed = new Set(written.map((row) => row.login));\n\tconst taken = distinct.find((login) => !claimed.has(login)) ?? '';\n\tthrow loginTaken(operation, user.type, taken);\n}\n",
|
|
22
22
|
"import type { PgDatabase } from '@nxgt/drizzle/pg';\nimport type { JanusStores } from '@nxgt/janus';\nimport { type DrizzleAdapterOptions, defineJanusTables } from '../tables';\nimport type { IdentityTable } from './identity-tables';\nimport { sessionStore } from './sessions';\nimport { tokenStore } from './tokens';\nimport { userStore } from './users';\n\n/**\n * The three stores `janus()` takes, over one PostgreSQL database through\n * Drizzle.\n *\n * ```ts\n * const db = drizzle(process.env.DATABASE_URL);\n * const auth = janus({ user, password: { login: 'email' }, store: createDrizzleStores(db), hasher });\n * ```\n *\n * **Every read is one statement**, whose absence is an empty result, turned\n * into `null` here; everything that arrives as a rejection is a failure. There\n * is no path on which an absence and an outage travel the same channel.\n *\n * **A user is written in one transaction** with its logins, whose table holds\n * their uniqueness — `@nxgt/drizzle`'s `withTransaction`, inside the method,\n * as rule 5 allows. Every other write is one statement.\n *\n * `sessions.deleteExpiredSessions` is implemented: PostgreSQL has no TTL, so\n * `auth.collectExpired()` is how lapsed sessions leave the table.\n *\n * `{ tables }` is what your schema file exports; absent, the tables in the\n * connection's `search_path`.\n */\nexport function createDrizzleStores(\n\tdb: PgDatabase,\n\toptions: DrizzleAdapterOptions<IdentityTable> = {},\n): JanusStores {\n\tconst tables = options.tables ?? defineJanusTables();\n\treturn {\n\t\tusers: userStore(db, tables),\n\t\tsessions: sessionStore(db, tables),\n\t\ttokens: tokenStore(db, tables),\n\t};\n}\n",
|
|
23
23
|
"import type { PgDatabase } from '@nxgt/drizzle/pg';\nimport type { JanusStores } from '@nxgt/janus';\nimport type { RelationStore } from '@nxgt/janus/permissions';\nimport { createDrizzleRelations } from './relations';\nimport { createDrizzleStores } from './stores';\nimport type { DrizzleAdapterOptions } from './tables';\n\n/** The whole adapter over one database, keyed as `janus()` takes it. */\nexport interface DrizzleAdapter {\n\t/** The identity stores: `createDrizzleStores(db)`. */\n\treadonly store: JanusStores;\n\t/** The relation store: `createDrizzleRelations(db)`. */\n\treadonly relations: RelationStore;\n}\n\n/**\n * The identity stores and the relation store over one database, under the\n * names `janus()` takes them — so one spread wires both, and deleting a user\n * deletes every tuple naming them:\n *\n * ```ts\n * const postgres = createDrizzleAdapter(db);\n * const auth = janus({ user, password: { login: 'email' }, hasher, ...postgres });\n * const access = permissions({ model, store: postgres.relations });\n * ```\n *\n * It connects to nothing and creates nothing: `db` is your Drizzle instance,\n * and the tables are your migrations'. `{ tables }` is what your schema file\n * exports, needed when they are in a PostgreSQL schema of their own. An\n * application that only authenticates can take `createDrizzleStores(db)` alone.\n */\nexport function createDrizzleAdapter(\n\tdb: PgDatabase,\n\toptions: DrizzleAdapterOptions = {},\n): DrizzleAdapter {\n\treturn {\n\t\tstore: createDrizzleStores(db, options),\n\t\trelations: createDrizzleRelations(db, options),\n\t};\n}\n"
|
|
24
24
|
],
|
|
25
|
-
"mappings": "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",
|
|
26
|
-
"debugId": "
|
|
25
|
+
"mappings": "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",
|
|
26
|
+
"debugId": "BBBB8A4136A5079264756E2164756E21",
|
|
27
27
|
"names": []
|
|
28
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../src/stores/records.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAM,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,KAAK,GAAG,EAAO,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAO7C,KAAK,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC;AACpD,KAAK,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC;AACvD,KAAK,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC,cAAc,CAAC,CAAC;AAE1D,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAgBxD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAmB/D;
|
|
1
|
+
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../src/stores/records.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAM,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,KAAK,GAAG,EAAO,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAO7C,KAAK,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC;AACpD,KAAK,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC;AACvD,KAAK,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC,cAAc,CAAC,CAAC;AAE1D,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAgBxD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAmB/D;AAmBD,wBAAgB,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CA2B/C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,aAAa,CAUxD;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,IAAI,GAAG,GAAG,CAEnC"}
|
package/dist/tables/index.d.ts
CHANGED
|
@@ -238,6 +238,21 @@ export declare function defineJanusTables(options?: JanusTablesOptions): {
|
|
|
238
238
|
identity: undefined;
|
|
239
239
|
generated: undefined;
|
|
240
240
|
}>;
|
|
241
|
+
secondFactorRecoveryCodes: import("drizzle-orm/pg-core").PgBuildColumn<"users", import("drizzle-orm/pg-core").SetDimensions<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>, 1>, {
|
|
242
|
+
name: string;
|
|
243
|
+
tableName: "users";
|
|
244
|
+
dataType: "string";
|
|
245
|
+
data: string[];
|
|
246
|
+
driverParam: string | string[];
|
|
247
|
+
notNull: false;
|
|
248
|
+
hasDefault: false;
|
|
249
|
+
isPrimaryKey: false;
|
|
250
|
+
isAutoincrement: false;
|
|
251
|
+
hasRuntimeDefault: false;
|
|
252
|
+
enumValues: undefined;
|
|
253
|
+
identity: undefined;
|
|
254
|
+
generated: undefined;
|
|
255
|
+
}>;
|
|
241
256
|
emailVerifiedAt: import("drizzle-orm/pg-core").PgBuildColumn<"users", import("drizzle-orm/pg-core").PgTimestampBuilder, {
|
|
242
257
|
name: string;
|
|
243
258
|
tableName: "users";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tables/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAW,MAAM,qBAAqB,CAAC;AAQ7D;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,6BAA6B;AAC7B,MAAM,WAAW,kBAAkB;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tables/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAW,MAAM,qBAAqB,CAAC;AAQ7D;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,6BAA6B;AAC7B,MAAM,WAAW,kBAAkB;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASjE;AAED,mDAAmD;AACnD,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE/D;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,qBAAqB,CACrC,CAAC,SAAS,MAAM,WAAW,GAAG,MAAM,WAAW;IAE/C,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;CACvC"}
|
package/dist/tables/users.d.ts
CHANGED
|
@@ -195,6 +195,21 @@ export declare function usersTable(table: TableFactory): import("drizzle-orm/pg-
|
|
|
195
195
|
identity: undefined;
|
|
196
196
|
generated: undefined;
|
|
197
197
|
}>;
|
|
198
|
+
secondFactorRecoveryCodes: import("drizzle-orm/pg-core").PgBuildColumn<"users", import("drizzle-orm/pg-core").SetDimensions<import("drizzle-orm/pg-core").PgTextBuilder<[string, ...string[]]>, 1>, {
|
|
199
|
+
name: string;
|
|
200
|
+
tableName: "users";
|
|
201
|
+
dataType: "string";
|
|
202
|
+
data: string[];
|
|
203
|
+
driverParam: string | string[];
|
|
204
|
+
notNull: false;
|
|
205
|
+
hasDefault: false;
|
|
206
|
+
isPrimaryKey: false;
|
|
207
|
+
isAutoincrement: false;
|
|
208
|
+
hasRuntimeDefault: false;
|
|
209
|
+
enumValues: undefined;
|
|
210
|
+
identity: undefined;
|
|
211
|
+
generated: undefined;
|
|
212
|
+
}>;
|
|
198
213
|
emailVerifiedAt: import("drizzle-orm/pg-core").PgBuildColumn<"users", import("drizzle-orm/pg-core").PgTimestampBuilder, {
|
|
199
214
|
name: string;
|
|
200
215
|
tableName: "users";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/tables/users.ts"],"names":[],"mappings":"AAUA,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY
|
|
1
|
+
{"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/tables/users.ts"],"names":[],"mappings":"AAUA,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwD7C"}
|
package/docs/guide/migrations.md
CHANGED
|
@@ -69,13 +69,14 @@ CREATE TABLE "users" (
|
|
|
69
69
|
"second_factor_secret" text,
|
|
70
70
|
"second_factor_confirmed_at" timestamp(3) with time zone,
|
|
71
71
|
"second_factor_last_step" integer,
|
|
72
|
+
"second_factor_recovery_codes" text[],
|
|
72
73
|
"email_verified_at" timestamp(3) with time zone,
|
|
73
74
|
"version" integer NOT NULL,
|
|
74
75
|
"created_at" timestamp(3) with time zone NOT NULL,
|
|
75
76
|
"updated_at" timestamp(3) with time zone NOT NULL,
|
|
76
77
|
CONSTRAINT "users_id_type_unique" UNIQUE("id","type"),
|
|
77
78
|
CONSTRAINT "users_password_whole" CHECK (("password_hash" is null) = ("password_updated_at" is null)),
|
|
78
|
-
CONSTRAINT "users_second_factor_whole" CHECK (("second_factor_method" is null) = ("second_factor_secret" is null) and ("second_factor_method" is not null or ("second_factor_confirmed_at" is null and "second_factor_last_step" is null))),
|
|
79
|
+
CONSTRAINT "users_second_factor_whole" CHECK (("second_factor_method" is null) = ("second_factor_secret" is null) and ("second_factor_method" is not null or ("second_factor_confirmed_at" is null and "second_factor_last_step" is null and "second_factor_recovery_codes" is null))),
|
|
79
80
|
CONSTRAINT "users_second_factor_values" CHECK ("second_factor_method" in ('totp') and "second_factor_last_step" >= 0)
|
|
80
81
|
);
|
|
81
82
|
CREATE TABLE "logins" (
|
|
@@ -131,14 +132,19 @@ migration creates is what the conformance suites ran on.
|
|
|
131
132
|
returned verbatim and in order, and as rows in `logins`, whose primary
|
|
132
133
|
key is the per-type uniqueness. PostgreSQL has no unique index over the
|
|
133
134
|
elements of an array.
|
|
134
|
-
- **A second factor in
|
|
135
|
-
or neither; a confirmation date
|
|
136
|
-
the only method, and a last step never negative. The
|
|
135
|
+
- **A second factor in five columns, checked whole.** A method and a secret,
|
|
136
|
+
or neither; a confirmation date, a last step and recovery codes only beside
|
|
137
|
+
them; `totp` the only method, and a last step never negative. The
|
|
137
138
|
secret is opaque to the store and kept byte for byte: `@nxgt/janus` seals
|
|
138
139
|
it before the store sees it, so a dump of `users` cannot produce a code.
|
|
139
140
|
Its key id is the second part of the value, so
|
|
140
141
|
`where second_factor_secret like 'v1.<id>.%'` finds the secrets still sealed
|
|
141
142
|
with a key being [rotated out](https://github.com/softistx/nxgt-janus/blob/develop/packages/janus/docs/guide/second-factor.md#rotating-the-keys).
|
|
143
|
+
- **Recovery codes as a `text[]`, `null` read as `[]`.** Each element is a
|
|
144
|
+
keyed hash the core wrote, never a code, kept in order. No codes — a
|
|
145
|
+
factor with none left, or no factor at all — is `null`, never `{}`. A row
|
|
146
|
+
from before the column existed reads as a factor with no codes, so an
|
|
147
|
+
upgrade rewrites nothing.
|
|
142
148
|
- **`attempts` is `not null default 0`**, checked `>= 0`, so the rows an upgrade finds read
|
|
143
149
|
as tokens with no attempt counted yet.
|
|
144
150
|
- **No foreign key from sessions and tokens to users.** Deleting a user
|
|
@@ -182,6 +188,31 @@ Those are the statements drizzle-kit writes from the 0.1 tables to the 0.2
|
|
|
182
188
|
ones, in its own order and separated by `--> statement-breakpoint`; with a
|
|
183
189
|
PostgreSQL schema of its own, every name is qualified.
|
|
184
190
|
|
|
191
|
+
### To 0.4: recovery codes
|
|
192
|
+
|
|
193
|
+
**A migration is required.** One nullable column, and the whole-factor check
|
|
194
|
+
extended to it, so the migration rewrites no row and every existing factor
|
|
195
|
+
reads as having no recovery codes until its user is given some:
|
|
196
|
+
|
|
197
|
+
```sql
|
|
198
|
+
ALTER TABLE "users" ADD COLUMN "second_factor_recovery_codes" text[];
|
|
199
|
+
ALTER TABLE "users" DROP CONSTRAINT "users_second_factor_whole", ADD CONSTRAINT "users_second_factor_whole" CHECK (("second_factor_method" is null) = ("second_factor_secret" is null) and ("second_factor_method" is not null or ("second_factor_confirmed_at" is null and "second_factor_last_step" is null and "second_factor_recovery_codes" is null)));
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Those are the statements drizzle-kit writes from the 0.3 tables to the 0.4
|
|
203
|
+
ones. Re-adding the check validates every row, under a lock on `users` for
|
|
204
|
+
the length of one scan; the rows already satisfy it, since the column is new.
|
|
205
|
+
Deployed before the migration, every query on `users` fails with
|
|
206
|
+
`STORE_FAILED`, caused by `column "second_factor_recovery_codes" does not
|
|
207
|
+
exist`.
|
|
208
|
+
|
|
209
|
+
**Finish the rollout before users hold codes.** An instance still on 0.3
|
|
210
|
+
knows four columns: once a user holds recovery codes, its `disable` leaves
|
|
211
|
+
them beside a factor removed, and the check refuses the write with
|
|
212
|
+
`STORE_FAILED`. Nothing is lost — the call fails, and succeeds on an instance
|
|
213
|
+
of 0.4 — but run 0.3 and 0.4 side by side only while no user has been given
|
|
214
|
+
codes, which `@nxgt/janus` does from `secondFactor.activate` on.
|
|
215
|
+
|
|
185
216
|
## Collecting lapsed sessions
|
|
186
217
|
|
|
187
218
|
PostgreSQL has no TTL. A lapsed session is refused on every read, but it stays
|
package/docs/roadmap.md
CHANGED
|
@@ -36,6 +36,12 @@ Nothing yet.
|
|
|
36
36
|
|
|
37
37
|
## Shipped
|
|
38
38
|
|
|
39
|
+
- **Recovery codes on a second factor, v0.4.0** — for `@nxgt/janus` 0.10:
|
|
40
|
+
`users` gains `second_factor_recovery_codes`, a nullable `text[]` of the
|
|
41
|
+
codes' keyed hashes, read in order and `null` as `[]`, and the
|
|
42
|
+
`users_second_factor_whole` check keeps them to a row with a factor. A
|
|
43
|
+
migration, generated by drizzle-kit, adds the column without rewriting a
|
|
44
|
+
row.
|
|
39
45
|
- **A user's tokens spent in one statement, v0.3.0** — for `@nxgt/janus`
|
|
40
46
|
0.7: `spendUserTokens(userId, kind, at, except?)` spends the unspent
|
|
41
47
|
tokens of one user and kind — but `except` — in one `update`, and passes
|
package/docs/troubleshooting.md
CHANGED
|
@@ -33,6 +33,7 @@ How PostgreSQL's errors become the port's:
|
|
|
33
33
|
**Setup**
|
|
34
34
|
- [`STORE_FAILED` caused by `relation "users" does not exist`](#store_failed-caused-by-relation-users-does-not-exist)
|
|
35
35
|
- [`STORE_FAILED` caused by `column "second_factor_method" does not exist`](#store_failed-caused-by-column-second_factor_method-does-not-exist)
|
|
36
|
+
- [`STORE_FAILED` caused by `column "second_factor_recovery_codes" does not exist`](#store_failed-caused-by-column-second_factor_recovery_codes-does-not-exist)
|
|
36
37
|
- [`STORE_FAILED` caused by `column "type" does not exist`](#store_failed-caused-by-column-type-does-not-exist)
|
|
37
38
|
- [`schema "janus" does not exist` while migrating](#schema-janus-does-not-exist-while-migrating)
|
|
38
39
|
- [`syntax error at or near "NULLS"` while migrating](#syntax-error-at-or-near-nulls-while-migrating)
|
|
@@ -204,6 +205,26 @@ bunx drizzle-kit generate --config drizzle.janus.config.ts
|
|
|
204
205
|
bunx drizzle-kit migrate --config drizzle.janus.config.ts
|
|
205
206
|
```
|
|
206
207
|
|
|
208
|
+
### `STORE_FAILED` caused by `column "second_factor_recovery_codes" does not exist`
|
|
209
|
+
|
|
210
|
+
The code is `42703`.
|
|
211
|
+
|
|
212
|
+
**When:** after upgrading to 0.4, on the first query that reads or writes a
|
|
213
|
+
user — a sign-up, a sign-in, `authenticate`.
|
|
214
|
+
|
|
215
|
+
**Why:** `users` gained the `second_factor_recovery_codes` column, which holds
|
|
216
|
+
the recovery codes' hashes, and the migration that adds it was not generated,
|
|
217
|
+
or not applied.
|
|
218
|
+
|
|
219
|
+
**Fix:** generate and apply it before the new version serves requests. It adds
|
|
220
|
+
one nullable column and re-creates the `users_second_factor_whole` check; see
|
|
221
|
+
[To 0.4](guide/migrations.md#to-04-recovery-codes).
|
|
222
|
+
|
|
223
|
+
```sh
|
|
224
|
+
bunx drizzle-kit generate --config drizzle.janus.config.ts
|
|
225
|
+
bunx drizzle-kit migrate --config drizzle.janus.config.ts
|
|
226
|
+
```
|
|
227
|
+
|
|
207
228
|
### `STORE_FAILED` caused by `column "type" does not exist`
|
|
208
229
|
|
|
209
230
|
Also `column "type" of relation "users" does not exist`, or another column of
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxgt/janus-drizzle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "The PostgreSQL adapter for @nxgt/janus on Drizzle and @nxgt/drizzle: users, sessions, one-time tokens and permission tuples",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@electric-sql/pglite": "0.5.8",
|
|
53
53
|
"@nxgt/drizzle": "0.6.1",
|
|
54
|
-
"@nxgt/janus": "^0.
|
|
54
|
+
"@nxgt/janus": "^0.11.0",
|
|
55
55
|
"@types/bun": "^1.4.2",
|
|
56
56
|
"@types/pg": "^8.23.1",
|
|
57
57
|
"drizzle-kit": "1.0.0-rc.4",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
64
|
"@nxgt/drizzle": ">=0.6.1 <1",
|
|
65
|
-
"@nxgt/janus": "^0.
|
|
65
|
+
"@nxgt/janus": "^0.11.0",
|
|
66
66
|
"drizzle-orm": ">=1.0.0-rc.4 <2",
|
|
67
67
|
"typescript": "^6.0.3"
|
|
68
68
|
}
|