@adonis-agora/authkit-server 1.0.0-rc.11 → 1.0.0-rc.13
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/CHANGELOG.md +26 -0
- package/README.md +1 -1
- package/build/index.d.ts +2 -1
- package/build/index.js +1 -0
- package/build/providers/authkit_server_provider.js +15 -4
- package/build/src/accounts/lucid_account_store.js +4 -3
- package/build/src/accounts/lucid_store/mfa.js +1 -1
- package/build/src/accounts/lucid_store/organization_models.d.ts +9 -5
- package/build/src/accounts/lucid_store/organization_models.js +16 -12
- package/build/src/accounts/lucid_store/password_hygiene.js +4 -2
- package/build/src/accounts/lucid_store/shared.d.ts +6 -1
- package/build/src/accounts/lucid_store/shared.js +7 -6
- package/build/src/accounts/lucid_store/webauthn.js +1 -1
- package/build/src/adapters/database_adapter.d.ts +7 -1
- package/build/src/adapters/database_adapter.js +24 -13
- package/build/src/adapters/factory.d.ts +7 -0
- package/build/src/adapters/factory.js +6 -2
- package/build/src/agents/delegation_store.d.ts +5 -1
- package/build/src/agents/delegation_store.js +34 -21
- package/build/src/agents/extension.d.ts +16 -2
- package/build/src/agents/registry_store.d.ts +1 -0
- package/build/src/agents/registry_store.js +10 -8
- package/build/src/agents/runtime.js +6 -2
- package/build/src/commands/expire_scan_command.js +2 -3
- package/build/src/commands/settings_commands.js +4 -4
- package/build/src/define_config.d.ts +8 -0
- package/build/src/define_config.js +23 -6
- package/build/src/doctor/agora_checks.js +13 -0
- package/build/src/doctor/checks.d.ts +31 -1
- package/build/src/doctor/checks.js +118 -3
- package/build/src/doctor/db_schema_check.d.ts +6 -0
- package/build/src/doctor/db_schema_check.js +87 -0
- package/build/src/doctor/doctor_input.js +2 -0
- package/build/src/host/admin_sessions_service.js +6 -2
- package/build/src/host/csrf.d.ts +3 -0
- package/build/src/host/csrf.js +27 -7
- package/build/src/host/runtime_settings.d.ts +15 -0
- package/build/src/host/runtime_settings.js +27 -12
- package/build/src/keys/keystore_manager.js +2 -1
- package/build/src/keys/keystore_vault.d.ts +9 -1
- package/build/src/keys/keystore_vault.js +27 -9
- package/build/src/schema/ensure.d.ts +9 -0
- package/build/src/schema/ensure.js +46 -19
- package/build/src/schema/tables_location.d.ts +31 -2
- package/build/src/schema/tables_location.js +64 -0
- package/package.json +3 -3
- package/skills/authkit-idp-setup/SKILL.md +13 -7
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* }
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
|
-
import { authkitTablesClient } from './tables_location.js';
|
|
26
|
+
import { assertSchemaSupported, authkitTable, authkitTablesClient, createDbSchema, validateSchemaName, } from './tables_location.js';
|
|
27
27
|
/** Busca por conta (`account_id`) e limpeza dos expirados (`expires_at`). */
|
|
28
28
|
const OIDC_PAYLOAD_INDEXES = [
|
|
29
29
|
{ name: 'authkit_oidc_payloads_account_idx', columns: ['model_name', 'account_id'] },
|
|
@@ -139,12 +139,12 @@ const TABLES = [
|
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
141
|
name: 'auth_organization_members',
|
|
142
|
-
create: (t) => {
|
|
142
|
+
create: (t, ctx) => {
|
|
143
143
|
t.string('id').primary();
|
|
144
144
|
t.string('organization_id')
|
|
145
145
|
.notNullable()
|
|
146
146
|
.references('id')
|
|
147
|
-
.inTable('auth_organizations')
|
|
147
|
+
.inTable(ctx.table('auth_organizations'))
|
|
148
148
|
.onDelete('CASCADE');
|
|
149
149
|
t.string('account_id').notNullable();
|
|
150
150
|
t.string('role').notNullable().defaultTo('member');
|
|
@@ -186,12 +186,12 @@ const TABLES = [
|
|
|
186
186
|
},
|
|
187
187
|
{
|
|
188
188
|
name: 'auth_organization_invitations',
|
|
189
|
-
create: (t) => {
|
|
189
|
+
create: (t, ctx) => {
|
|
190
190
|
t.string('id').primary();
|
|
191
191
|
t.string('organization_id')
|
|
192
192
|
.notNullable()
|
|
193
193
|
.references('id')
|
|
194
|
-
.inTable('auth_organizations')
|
|
194
|
+
.inTable(ctx.table('auth_organizations'))
|
|
195
195
|
.onDelete('CASCADE');
|
|
196
196
|
t.string('email').notNullable();
|
|
197
197
|
t.string('role').notNullable().defaultTo('member');
|
|
@@ -335,6 +335,8 @@ const TABLES = [
|
|
|
335
335
|
},
|
|
336
336
|
},
|
|
337
337
|
];
|
|
338
|
+
/** Os nomes (sem schema) das tabelas que {@link ensureAuthkitSchema} garante. */
|
|
339
|
+
export const AUTHKIT_TABLE_NAMES = TABLES.map((t) => t.name);
|
|
338
340
|
/** Linhas de um `rawQuery`, no formato de cada driver (pg, mysql2, sqlite). */
|
|
339
341
|
function catalogRows(result) {
|
|
340
342
|
if (Array.isArray(result))
|
|
@@ -420,7 +422,10 @@ async function columnExists(conn, table, column) {
|
|
|
420
422
|
async function indexExists(conn, table, name) {
|
|
421
423
|
const dialect = conn.dialect?.name;
|
|
422
424
|
if (dialect === 'postgres' || dialect === 'redshift') {
|
|
423
|
-
|
|
425
|
+
// Índice mora no schema da tabela: com schema explícito, o nome vai qualificado.
|
|
426
|
+
const [schema] = mysqlRelation(table);
|
|
427
|
+
const relation = pgRelation(schema ? `${schema}.${name}` : name);
|
|
428
|
+
const found = catalogRows(await conn.rawQuery('select to_regclass(?) as oid', [relation]));
|
|
424
429
|
return found[0]?.oid != null;
|
|
425
430
|
}
|
|
426
431
|
if (dialect === 'mysql') {
|
|
@@ -441,11 +446,12 @@ async function indexExists(conn, table, name) {
|
|
|
441
446
|
* Cria o índice quando falta. Idempotente: um índice já existente (inclusive
|
|
442
447
|
* criado por outra instância no mesmo boot) não é erro.
|
|
443
448
|
*/
|
|
444
|
-
async function ensureIndex(conn,
|
|
449
|
+
async function ensureIndex(conn, loc, name, index) {
|
|
450
|
+
const table = authkitTable(loc, name);
|
|
445
451
|
if ((await indexExists(conn, table, index.name)) === true)
|
|
446
452
|
return false;
|
|
447
453
|
try {
|
|
448
|
-
await conn.
|
|
454
|
+
await ddl(conn, loc).alterTable(name, (t) => t.index(index.columns, index.name));
|
|
449
455
|
return true;
|
|
450
456
|
}
|
|
451
457
|
catch (error) {
|
|
@@ -458,12 +464,23 @@ async function ensureIndex(conn, table, index) {
|
|
|
458
464
|
throw error;
|
|
459
465
|
}
|
|
460
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* O schema builder das tabelas do authkit: com `schema`, `withSchema` — o knex escreve
|
|
469
|
+
* `"schema"."tabela"` em todo DDL e mantém os nomes de índice derivados da tabela pura.
|
|
470
|
+
*/
|
|
471
|
+
function ddl(conn, loc) {
|
|
472
|
+
return loc.schema ? conn.schema.withSchema(loc.schema) : conn.schema;
|
|
473
|
+
}
|
|
461
474
|
/** Cria/completa as tabelas (o miolo de {@link ensureAuthkitSchema}). */
|
|
462
|
-
async function ensureTables(conn, tables, report) {
|
|
475
|
+
async function ensureTables(conn, loc, tables, report) {
|
|
476
|
+
const context = { table: (name) => authkitTable(loc, name) };
|
|
477
|
+
if (loc.schema)
|
|
478
|
+
await createDbSchema(conn, loc.schema);
|
|
463
479
|
for (const def of tables) {
|
|
464
|
-
|
|
480
|
+
const table = authkitTable(loc, def.name);
|
|
481
|
+
if (!(await tableExists(conn, table))) {
|
|
465
482
|
try {
|
|
466
|
-
await conn.
|
|
483
|
+
await ddl(conn, loc).createTable(def.name, (t) => def.create(t, context));
|
|
467
484
|
report.created.push(def.name);
|
|
468
485
|
continue;
|
|
469
486
|
}
|
|
@@ -472,20 +489,20 @@ async function ensureTables(conn, tables, report) {
|
|
|
472
489
|
* Corrida entre instâncias subindo juntas: se outra instância criou
|
|
473
490
|
* a tabela entre o probe e o CREATE, segue o jogo; senão, propaga.
|
|
474
491
|
*/
|
|
475
|
-
if (!(await tableExists(conn,
|
|
492
|
+
if (!(await tableExists(conn, table)))
|
|
476
493
|
throw error;
|
|
477
494
|
}
|
|
478
495
|
}
|
|
479
496
|
for (const [column, add] of Object.entries(def.columns)) {
|
|
480
|
-
if (await columnExists(conn,
|
|
497
|
+
if (await columnExists(conn, table, column))
|
|
481
498
|
continue;
|
|
482
|
-
await conn.
|
|
499
|
+
await ddl(conn, loc).alterTable(def.name, (t) => add(t));
|
|
483
500
|
if (!report.altered[def.name])
|
|
484
501
|
report.altered[def.name] = [];
|
|
485
502
|
report.altered[def.name].push(column);
|
|
486
503
|
}
|
|
487
504
|
for (const index of def.indexes ?? []) {
|
|
488
|
-
if (!(await ensureIndex(conn, def.name, index)))
|
|
505
|
+
if (!(await ensureIndex(conn, loc, def.name, index)))
|
|
489
506
|
continue;
|
|
490
507
|
if (!report.indexed)
|
|
491
508
|
report.indexed = {};
|
|
@@ -495,18 +512,26 @@ async function ensureTables(conn, tables, report) {
|
|
|
495
512
|
}
|
|
496
513
|
}
|
|
497
514
|
}
|
|
515
|
+
function resolveLocation(options) {
|
|
516
|
+
return {
|
|
517
|
+
connection: options.connection,
|
|
518
|
+
schema: validateSchemaName(options.schema, 'schema'),
|
|
519
|
+
};
|
|
520
|
+
}
|
|
498
521
|
/**
|
|
499
522
|
* Garante tabelas lib-owned no formato {@link AuthkitTable} — o que `ensureAuthkitSchema` faz com as
|
|
500
523
|
* do authkit: cria as que faltam, adiciona colunas e índices novos, nunca dropa nem altera. Para
|
|
501
524
|
* uma extension expor o próprio `ensure*Schema()` para migrations do host. Estável no 1.x.
|
|
502
525
|
*/
|
|
503
526
|
export async function ensureAuthkitTables(db, tables, options = {}) {
|
|
504
|
-
const
|
|
527
|
+
const loc = resolveLocation(options);
|
|
528
|
+
const conn = authkitTablesClient(db, loc);
|
|
529
|
+
assertSchemaSupported(conn, loc.schema);
|
|
505
530
|
const report = {
|
|
506
531
|
created: [],
|
|
507
532
|
altered: {},
|
|
508
533
|
};
|
|
509
|
-
await ensureTables(conn, tables, report);
|
|
534
|
+
await ensureTables(conn, loc, tables, report);
|
|
510
535
|
return report;
|
|
511
536
|
}
|
|
512
537
|
/**
|
|
@@ -518,13 +543,15 @@ export async function ensureAuthkitTables(db, tables, options = {}) {
|
|
|
518
543
|
* (`this.db`, um `QueryClient`/`TransactionClient`, usado como está).
|
|
519
544
|
*/
|
|
520
545
|
export async function ensureAuthkitSchema(db, options = {}) {
|
|
521
|
-
const
|
|
546
|
+
const loc = resolveLocation(options);
|
|
547
|
+
const conn = authkitTablesClient(db, loc);
|
|
548
|
+
assertSchemaSupported(conn, loc.schema);
|
|
522
549
|
const report = {
|
|
523
550
|
created: [],
|
|
524
551
|
altered: {},
|
|
525
552
|
loginMethods: { table: options.accountTable ?? 'users', ensured: false },
|
|
526
553
|
};
|
|
527
|
-
await ensureTables(conn, [...TABLES, ...(options.tables ?? [])], report);
|
|
554
|
+
await ensureTables(conn, loc, [...TABLES, ...(options.tables ?? [])], report);
|
|
528
555
|
// Coluna `login_methods` na tabela da CONTA — a tabela é HOST-owned (não está
|
|
529
556
|
// em TABLES porque o nome/shape são decisão do app), mas a LIB consome a
|
|
530
557
|
// coluna (LoginMethodsPreferenceCapability). Alguns deploys de host só migram
|
|
@@ -4,19 +4,48 @@
|
|
|
4
4
|
* `authkit_keystore` of the Lucid keystore, and the tables of agent extensions).
|
|
5
5
|
*
|
|
6
6
|
* The rule: every lib-owned table goes through `schema.connection` of `defineConfig`
|
|
7
|
-
* (default: the primary Lucid connection)
|
|
8
|
-
*
|
|
7
|
+
* (default: the primary Lucid connection) and, with `schema.name`, lives in that database
|
|
8
|
+
* schema (MySQL: that database). The app's account table is not lib-owned: it stays on the
|
|
9
|
+
* account model's connection and table (`static connection`/`static table` of the model).
|
|
10
|
+
*
|
|
11
|
+
* The schema is written INTO every statement (`"auth"."auth_settings"`). Never put
|
|
12
|
+
* AuthKit's tables in another schema with a connection `searchPath`: knex sends
|
|
13
|
+
* `SET search_path` on the session, and behind PgBouncer in transaction mode that leaks to
|
|
14
|
+
* other clients' connections.
|
|
9
15
|
*/
|
|
10
16
|
export interface AuthkitTablesLocation {
|
|
11
17
|
/** Lucid connection name. `undefined` = the primary connection. */
|
|
12
18
|
connection?: string;
|
|
19
|
+
/** Database schema of the tables (MySQL: the database). `undefined` = the connection's default. */
|
|
20
|
+
schema?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Raised for a `schema.name` the database cannot use. The provider fails the boot with it. */
|
|
23
|
+
export declare class AuthkitSchemaError extends Error {
|
|
24
|
+
constructor(message: string);
|
|
13
25
|
}
|
|
26
|
+
/** Throws {@link AuthkitSchemaError} unless `name` is a plain identifier of at most 63 chars. */
|
|
27
|
+
export declare function validateSchemaName(name: unknown, option?: string): string | undefined;
|
|
28
|
+
/** `schema.table` when a schema is set (knex quotes each part: `"schema"."table"`), else `table`. */
|
|
29
|
+
export declare function authkitTable(location: AuthkitTablesLocation | undefined, table: string): string;
|
|
14
30
|
/**
|
|
15
31
|
* The query client for AuthKit's tables. `db` is the Lucid manager (`Database`) or a
|
|
16
32
|
* client the caller already holds (a migration's `this.db`, a transaction), which is
|
|
17
33
|
* used as is.
|
|
18
34
|
*/
|
|
19
35
|
export declare function authkitTablesClient(db: any, location?: AuthkitTablesLocation): any;
|
|
36
|
+
/** The Lucid dialect name of a client or manager (`postgres`, `mysql`, `better-sqlite3`…). */
|
|
37
|
+
export declare function dialectOf(client: any): string | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Throws {@link AuthkitSchemaError} when `schema` is set on a dialect AuthKit cannot use it on:
|
|
40
|
+
* SQLite (no schemas) and any dialect other than Postgres and MySQL. AuthKit never ignores the
|
|
41
|
+
* option nor falls back to unqualified tables.
|
|
42
|
+
*/
|
|
43
|
+
export declare function assertSchemaSupported(client: any, schema: string | undefined): void;
|
|
44
|
+
/**
|
|
45
|
+
* `CREATE SCHEMA IF NOT EXISTS` (Postgres) / `CREATE DATABASE IF NOT EXISTS` (MySQL). A role
|
|
46
|
+
* that cannot create it gets an {@link AuthkitSchemaError} naming the schema and the grant.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createDbSchema(client: any, schema: string): Promise<void>;
|
|
20
49
|
/**
|
|
21
50
|
* Hidden hook a store built by `lucidAccountStore` exposes so `defineConfig` can tell it
|
|
22
51
|
* where AuthKit's tables live (the store is built before `defineConfig` sees the `schema`
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
/** Raised for a `schema.name` the database cannot use. The provider fails the boot with it. */
|
|
2
|
+
export class AuthkitSchemaError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = 'AuthkitSchemaError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
const SCHEMA_NAME = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
9
|
+
/** Throws {@link AuthkitSchemaError} unless `name` is a plain identifier of at most 63 chars. */
|
|
10
|
+
export function validateSchemaName(name, option = 'schema.name') {
|
|
11
|
+
if (name === undefined)
|
|
12
|
+
return undefined;
|
|
13
|
+
if (typeof name !== 'string' || name.length > 63 || !SCHEMA_NAME.test(name)) {
|
|
14
|
+
throw new AuthkitSchemaError(`authkit: ${option} must match /^[A-Za-z_][A-Za-z0-9_$]*$/ and have at most 63 characters (got ${JSON.stringify(name)})`);
|
|
15
|
+
}
|
|
16
|
+
return name;
|
|
17
|
+
}
|
|
18
|
+
/** `schema.table` when a schema is set (knex quotes each part: `"schema"."table"`), else `table`. */
|
|
19
|
+
export function authkitTable(location, table) {
|
|
20
|
+
return location?.schema ? `${location.schema}.${table}` : table;
|
|
21
|
+
}
|
|
1
22
|
/**
|
|
2
23
|
* The query client for AuthKit's tables. `db` is the Lucid manager (`Database`) or a
|
|
3
24
|
* client the caller already holds (a migration's `this.db`, a transaction), which is
|
|
@@ -8,6 +29,49 @@ export function authkitTablesClient(db, location = {}) {
|
|
|
8
29
|
return db;
|
|
9
30
|
return location.connection ? db.connection(location.connection) : db.connection();
|
|
10
31
|
}
|
|
32
|
+
const SQLITE_DIALECTS = new Set(['sqlite3', 'better-sqlite3', 'libsql']);
|
|
33
|
+
/** Dialects AuthKit writes and creates schemas on. */
|
|
34
|
+
const SCHEMA_DIALECTS = new Set(['postgres', 'redshift', 'mysql']);
|
|
35
|
+
/** The Lucid dialect name of a client or manager (`postgres`, `mysql`, `better-sqlite3`…). */
|
|
36
|
+
export function dialectOf(client) {
|
|
37
|
+
return client?.dialect?.name;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Throws {@link AuthkitSchemaError} when `schema` is set on a dialect AuthKit cannot use it on:
|
|
41
|
+
* SQLite (no schemas) and any dialect other than Postgres and MySQL. AuthKit never ignores the
|
|
42
|
+
* option nor falls back to unqualified tables.
|
|
43
|
+
*/
|
|
44
|
+
export function assertSchemaSupported(client, schema) {
|
|
45
|
+
if (!schema)
|
|
46
|
+
return;
|
|
47
|
+
const dialect = dialectOf(client);
|
|
48
|
+
if (dialect && SCHEMA_DIALECTS.has(dialect))
|
|
49
|
+
return;
|
|
50
|
+
throw new AuthkitSchemaError(dialect && SQLITE_DIALECTS.has(dialect)
|
|
51
|
+
? `authkit: schema.name "${schema}" is set, but the connection uses ${dialect}, which has no schemas. Remove schema.name or use Postgres or MySQL.`
|
|
52
|
+
: `authkit: schema.name "${schema}" is set, but the connection uses ${dialect ?? 'an unknown dialect'}; schema.name supports Postgres and MySQL. Remove schema.name or use one of them.`);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* `CREATE SCHEMA IF NOT EXISTS` (Postgres) / `CREATE DATABASE IF NOT EXISTS` (MySQL). A role
|
|
56
|
+
* that cannot create it gets an {@link AuthkitSchemaError} naming the schema and the grant.
|
|
57
|
+
*/
|
|
58
|
+
export async function createDbSchema(client, schema) {
|
|
59
|
+
assertSchemaSupported(client, schema);
|
|
60
|
+
const dialect = dialectOf(client);
|
|
61
|
+
const mysql = dialect === 'mysql';
|
|
62
|
+
const sql = mysql
|
|
63
|
+
? `create database if not exists \`${schema}\``
|
|
64
|
+
: `create schema if not exists "${schema}"`;
|
|
65
|
+
try {
|
|
66
|
+
await client.rawQuery(sql);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
const grant = mysql
|
|
70
|
+
? `GRANT CREATE ON *.* (or create it: CREATE DATABASE \`${schema}\`)`
|
|
71
|
+
: `GRANT CREATE ON DATABASE <db> TO <role> (or create it: CREATE SCHEMA "${schema}")`;
|
|
72
|
+
throw new AuthkitSchemaError(`authkit: could not create schema "${schema}" (${error?.message ?? error}). The database role needs ${grant}.`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
11
75
|
/**
|
|
12
76
|
* Hidden hook a store built by `lucidAccountStore` exposes so `defineConfig` can tell it
|
|
13
77
|
* where AuthKit's tables live (the store is built before `defineConfig` sees the `schema`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/authkit-server",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.13",
|
|
4
4
|
"description": "AdonisJS OIDC/OAuth2 provider (Identity Provider) toolkit: ejectable auth server with sessions, rate-limiting, MFA/TOTP, audit log, federated logout and OpenTelemetry metrics.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "dudousxd",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
110
|
"dependencies": {
|
|
111
|
-
"@adonis-agora/authkit-core": "1.0.0-rc.
|
|
111
|
+
"@adonis-agora/authkit-core": "1.0.0-rc.2",
|
|
112
112
|
"@simplewebauthn/browser": "14.0.0",
|
|
113
113
|
"@simplewebauthn/server": "14.1.0",
|
|
114
114
|
"jose": "6.2.16",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"qrcode": "1.5.4"
|
|
120
120
|
},
|
|
121
121
|
"devDependencies": {
|
|
122
|
-
"@adonis-agora/authkit-react": "1.0.0-rc.
|
|
122
|
+
"@adonis-agora/authkit-react": "1.0.0-rc.5",
|
|
123
123
|
"@adonis-agora/durable": "0.60.0",
|
|
124
124
|
"@adonis-agora/telescope": "0.27.1",
|
|
125
125
|
"@adonisjs/ally": "6.3.2",
|
|
@@ -4,7 +4,7 @@ description: >-
|
|
|
4
4
|
Set up the @adonis-agora/authkit-server OIDC/OAuth2 Authorization Server (Identity
|
|
5
5
|
Provider) in an AdonisJS app. Covers `node ace add @adonis-agora/authkit-server`,
|
|
6
6
|
defineConfig({ issuer, adapter, jwks, ttl, globalRolesClaim, accountStore }) with
|
|
7
|
-
adapters.redis({ connection }) vs adapters.database({ connection? }),
|
|
7
|
+
adapters.redis({ connection }) vs adapters.database({ connection?, schema? }),
|
|
8
8
|
lucidAccountStore(AuthUser), JWKS management ({ source: 'managed' | 'jwks' } or
|
|
9
9
|
'auto', keystore store file/drive/lucid/redis/hashicorp vaults plus
|
|
10
10
|
@adonis-agora/authkit-vault-aws/azure/gcp createKeystoreVault add-ons),
|
|
@@ -83,12 +83,18 @@ export default authServerConfig
|
|
|
83
83
|
|
|
84
84
|
`adapter` is where oidc-provider persists grants/tokens/codes: `adapters.redis({
|
|
85
85
|
connection })` requires `@adonisjs/redis` configured; `adapters.database({
|
|
86
|
-
connection? })` uses Lucid and stores artifacts in `authkit_oidc_payloads`, which
|
|
87
|
-
`ensureAuthkitSchema` creates on boot (with `schema: { autoManage: false }`, call
|
|
88
|
-
|
|
86
|
+
connection?, schema? })` uses Lucid and stores artifacts in `authkit_oidc_payloads`, which
|
|
87
|
+
`ensureAuthkitSchema` creates on boot (with `schema: { autoManage: false }`, call
|
|
88
|
+
`ensureAuthkitSchema(db)` — or `ensureAuthkitSchema(db, { schema })` when `schema.name`
|
|
89
|
+
is set — from a migration). The `schema` defaults to `schema.name` of `defineConfig`.
|
|
90
|
+
The table carries an indexed `account_id` column so per-account
|
|
89
91
|
session lookups and revocation stay in SQL; rows with a NULL `account_id` are still
|
|
90
|
-
found and get classified on read, so no backfill is needed.
|
|
91
|
-
MySQL and SQLite;
|
|
92
|
+
found and get classified on read, so no backfill is needed. Without `schema.name` it
|
|
93
|
+
works on Postgres, MySQL and SQLite; with `schema: { name: 'auth' }` every AuthKit
|
|
94
|
+
table lives in that DB schema (`"auth"."auth_settings"`), the boot creates it
|
|
95
|
+
(`CREATE SCHEMA IF NOT EXISTS`; MySQL: `CREATE DATABASE IF NOT EXISTS`), and SQLite
|
|
96
|
+
fails the boot instead of ignoring the option. Never use `searchPath` for this
|
|
97
|
+
(it leaks across clients behind PgBouncer in transaction mode). Expired rows are pruned every 10 minutes per connection. `accountStore` is the identity contract — `lucidAccountStore(AuthUser)`
|
|
92
98
|
derives `findAccount`/`verifyCredentials` from your user model.
|
|
93
99
|
|
|
94
100
|
Source: `stubs/config/authkit.stub`, `src/define_config.ts` (`AuthServerConfigInput`),
|
|
@@ -112,7 +118,7 @@ blob lives); `{ source: 'jwks', keys }` supplies keys inline.
|
|
|
112
118
|
|
|
113
119
|
The managed keystore's `store` accepts a path shortcut (`store: 'tmp/keystore.json'`)
|
|
114
120
|
or `{ driver: ... }`: built-in vaults are `file`, `drive` (a `@adonisjs/drive` disk),
|
|
115
|
-
`lucid` (table created on first write), `redis`, and `hashicorp`; for cloud secret
|
|
121
|
+
`lucid` (table created on first write; `schema` overrides, default `schema.name`), `redis`, and `hashicorp`; for cloud secret
|
|
116
122
|
managers install the add-ons `@adonis-agora/authkit-vault-aws` / `-azure` / `-gcp`
|
|
117
123
|
and pass their `createKeystoreVault(...)` result as `store` (any object with a
|
|
118
124
|
`read()` method is accepted). With no `store` at all, keys are ephemeral per boot.
|