@adonis-agora/authkit-server 0.67.0 → 0.68.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.
@@ -138,20 +138,27 @@ export interface LucidAccountStoreOptions {
138
138
  /**
139
139
  * Models Lucid para organizations (multi-tenancy).
140
140
  *
141
- * - `true` → usa os models DEFAULT da lib ({@link defaultOrganizationModels}),
142
- * que já mapeiam as três tabelas lib-owned (`auth_organizations`,
143
- * `auth_organization_members`, `auth_organization_invitations`). É o caminho
144
- * recomendado: as tabelas são criadas/evoluídas pelo `ensureAuthkitSchema`,
145
- * então o mapeamento não é decisão do host.
141
+ * - **Ausente (default)** → usa os models DEFAULT da lib
142
+ * ({@link defaultOrganizationModels}), que já mapeiam as três tabelas
143
+ * lib-owned (`auth_organizations`, `auth_organization_members`,
144
+ * `auth_organization_invitations`). A `OrganizationsCapability` passa a
145
+ * existir por padrão. Isso é seguro porque as rotas do console de conta para
146
+ * orgs JÁ eram montadas por default (`register_auth_host`, bloco `if
147
+ * (mountOrgs)`, com controller capability-probed); antes, sem os models, a
148
+ * tela montada respondia 403; agora ela funciona. Não há tela nova — só uma
149
+ * que deixou de estar quebrada. As tabelas são criadas/evoluídas pelo
150
+ * `ensureAuthkitSchema`, então o mapeamento não é decisão do host.
151
+ * - `true` → idêntico ao default (atalho explícito; é o que apps existentes
152
+ * já usam e continua valendo).
146
153
  * - `{ OrgModel, MemberModel, InvitationModel }` → escape hatch, para quem
147
154
  * guarda as tabelas de auth numa conexão/schema próprios (os defaults não
148
155
  * declaram `static connection`).
149
- * - Ausente → `OrganizationsCapability` AUSENTE no store. Como as rotas
150
- * `/account/orgs*` são montadas por capability-probing, isso deixa a
151
- * feature desligada — silenciosamente, se o host não olhar o
152
- * `authkit:doctor`.
156
+ * - `false` → opt-out explícito: `OrganizationsCapability` AUSENTE no store.
157
+ * Use quando a dependência atual é `supportsOrganizations === false` (ex.:
158
+ * store/rotas que assumem a ausência). O `authkit:doctor` avisa se
159
+ * `organizations.enabled: true` e o store não tem a capability.
153
160
  */
154
- organizationModels?: true | {
161
+ organizationModels?: true | false | {
155
162
  OrgModel: any;
156
163
  MemberModel: any;
157
164
  InvitationModel: any;
@@ -130,7 +130,15 @@ export function lucidAccountStore(Model, options = {}) {
130
130
  const encrypter = options.encrypter === false ? undefined : (options.encrypter ?? appKeyEncrypter());
131
131
  const ProviderIdentityModel = options.providerIdentityModel;
132
132
  const WebauthnCredentialModel = options.webauthnCredentialModel;
133
- const OrgModels = options.organizationModels === true ? defaultOrganizationModels : options.organizationModels;
133
+ // Organizations é padrão: ausente OU `true` → models default da lib. O
134
+ // opt-out explícito é `false`. O trio explícito segue como escape hatch.
135
+ const orgModelsOption = options.organizationModels;
136
+ const usesDefaultOrgModels = orgModelsOption === undefined || orgModelsOption === true;
137
+ const OrgModels = orgModelsOption === false
138
+ ? undefined
139
+ : usesDefaultOrgModels
140
+ ? defaultOrganizationModels
141
+ : orgModelsOption;
134
142
  // RP do WebAuthn: usado nas cerimônias. Default do rpName cai no mfaIssuer.
135
143
  const webauthn = options.webauthn ?? {
136
144
  rpName: mfaIssuer,
@@ -222,17 +230,23 @@ export function lucidAccountStore(Model, options = {}) {
222
230
  // Preferência por usuário de tipos de login: só quando o model tem a coluna
223
231
  // `login_methods` (JSONB). Sem a coluna → capacidade ausente (feature no-op).
224
232
  ...(supportsLoginMethodsColumn(Model) ? buildLoginMethods(ctx) : {}),
225
- // Organizations (multi-tenancy): só quando os três models foram fornecidos.
233
+ // Organizations (multi-tenancy): por padrão usa os models default da lib;
234
+ // ausente só com `organizationModels: false` (opt-out explícito).
226
235
  ...(OrgModels
227
- ? buildOrganizations({
228
- OrgModel: OrgModels.OrgModel,
229
- MemberModel: OrgModels.MemberModel,
230
- InvitationModel: OrgModels.InvitationModel,
231
- findAccountEmail: async (accountId) => {
232
- const row = await Model.find(accountId);
233
- return row?.email ?? null;
234
- },
235
- })
236
+ ? {
237
+ // Origem dos models, para o `authkit:doctor` reportar sem re-inferir.
238
+ // NÃO faz parte do contrato AccountStore.
239
+ __organizationModelsSource: usesDefaultOrgModels ? 'default' : 'explicit',
240
+ ...buildOrganizations({
241
+ OrgModel: OrgModels.OrgModel,
242
+ MemberModel: OrgModels.MemberModel,
243
+ InvitationModel: OrgModels.InvitationModel,
244
+ findAccountEmail: async (accountId) => {
245
+ const row = await Model.find(accountId);
246
+ return row?.email ?? null;
247
+ },
248
+ }),
249
+ }
236
250
  : {}),
237
251
  // Config de senha resolvida — exposta (não-enumerável) para o authkit:doctor
238
252
  // inspecionar policy/checkPwned. NÃO faz parte do contrato AccountStore.
@@ -17,12 +17,13 @@ export interface LucidStoresModels {
17
17
  /**
18
18
  * Organizations (multi-tenancy) — habilita a `OrganizationsCapability`.
19
19
  *
20
- * `true` usa os models default da lib (as três tabelas são lib-owned, criadas
21
- * pelo `ensureAuthkitSchema`), que é o caminho recomendado. O trio explícito
22
- * fica como escape hatch para quem guarda as tabelas de auth numa
20
+ * **Ausente → ligada por padrão** (models default da lib; as três tabelas são
21
+ * lib-owned, criadas pelo `ensureAuthkitSchema`). `true` é o atalho explícito
22
+ * para o mesmo caminho; `false` é o opt-out (capability ausente). O trio
23
+ * explícito fica como escape hatch para quem guarda as tabelas de auth numa
23
24
  * conexão/schema próprios.
24
25
  */
25
- organizations?: true | {
26
+ organizations?: true | false | {
26
27
  OrgModel: any;
27
28
  MemberModel: any;
28
29
  InvitationModel: any;
@@ -49,9 +50,9 @@ export type LucidStoresOptions = Omit<LucidAccountStoreOptions, 'providerIdentit
49
50
  * )
50
51
  * ```
51
52
  *
52
- * `organizations: true` usa os models default da lib. Se as tabelas de auth
53
- * vivem numa conexão/schema próprios, passe o trio explícito
54
- * (`{ OrgModel, MemberModel, InvitationModel }`) — os defaults não declaram
55
- * `static connection`.
53
+ * Organizations é ligada por padrão (models default da lib); `organizations:
54
+ * false` desliga. Se as tabelas de auth vivem numa conexão/schema próprios, passe
55
+ * o trio explícito (`{ OrgModel, MemberModel, InvitationModel }`) — os defaults
56
+ * não declaram `static connection`.
56
57
  */
57
58
  export declare function lucidStores(models: LucidStoresModels, options?: LucidStoresOptions): LucidStoresResult;
@@ -15,10 +15,10 @@ import { lucidAccountStore } from './lucid_account_store.js';
15
15
  * )
16
16
  * ```
17
17
  *
18
- * `organizations: true` usa os models default da lib. Se as tabelas de auth
19
- * vivem numa conexão/schema próprios, passe o trio explícito
20
- * (`{ OrgModel, MemberModel, InvitationModel }`) — os defaults não declaram
21
- * `static connection`.
18
+ * Organizations é ligada por padrão (models default da lib); `organizations:
19
+ * false` desliga. Se as tabelas de auth vivem numa conexão/schema próprios, passe
20
+ * o trio explícito (`{ OrgModel, MemberModel, InvitationModel }`) — os defaults
21
+ * não declaram `static connection`.
22
22
  */
23
23
  export function lucidStores(models, options = {}) {
24
24
  const accountStore = lucidAccountStore(models.account, {
@@ -119,7 +119,11 @@ export declare function checkAccessTokens(input: DoctorInput): Finding | null;
119
119
  /**
120
120
  * Organizations (multi-tenancy). Informa se a capacidade está disponível
121
121
  * (store expõe createOrg) e avisa se `organizations.enabled: true` no config mas
122
- * a capacidade não está presente no store (organizationModels não foram passados).
122
+ * a capacidade não está presente no store.
123
+ *
124
+ * Com `lucidAccountStore()`, a capability existe POR PADRÃO (models default da
125
+ * lib); o aviso abaixo só é alcançável para store não-lucid que não a implementa
126
+ * ou para um store lucid construído com `organizationModels: false` (opt-out).
123
127
  */
124
128
  export declare function checkOrganizations(input: DoctorInput): Finding | null;
125
129
  /**
@@ -488,7 +488,11 @@ export function checkAccessTokens(input) {
488
488
  /**
489
489
  * Organizations (multi-tenancy). Informa se a capacidade está disponível
490
490
  * (store expõe createOrg) e avisa se `organizations.enabled: true` no config mas
491
- * a capacidade não está presente no store (organizationModels não foram passados).
491
+ * a capacidade não está presente no store.
492
+ *
493
+ * Com `lucidAccountStore()`, a capability existe POR PADRÃO (models default da
494
+ * lib); o aviso abaixo só é alcançável para store não-lucid que não a implementa
495
+ * ou para um store lucid construído com `organizationModels: false` (opt-out).
492
496
  */
493
497
  export function checkOrganizations(input) {
494
498
  const cfg = input.authkitConfig;
@@ -501,17 +505,26 @@ export function checkOrganizations(input) {
501
505
  return {
502
506
  level: 'warn',
503
507
  message: 'organizations.enabled: true, but the accountStore has no OrganizationsCapability — ' +
504
- 'pass `organizationModels: true` to `lucidAccountStore()` (usa os models default da lib), ' +
505
- 'ou `{ OrgModel, MemberModel, InvitationModel }` se as tabelas de auth vivem numa ' +
506
- 'conexão/schema próprios. Expected tables: auth_organizations, ' +
508
+ 'with `lucidAccountStore()` it is present by default; if you passed ' +
509
+ '`organizationModels: false`, drop the opt-out (or pass the explicit ' +
510
+ '`{ OrgModel, MemberModel, InvitationModel }` trio when the auth tables live on their ' +
511
+ 'own connection/schema). Expected tables: auth_organizations, ' +
507
512
  'auth_organization_members, auth_organization_invitations.',
508
513
  };
509
514
  }
510
515
  if (storeSupports) {
511
516
  const roles = cfg.organizations?.roles ?? ['owner', 'admin', 'member'];
517
+ // Origem dos models: default da lib (organizationModels ausente/true) ou
518
+ // trio explícito do host. Ausente em store não-lucid → wording genérica.
519
+ const source = store.__organizationModelsSource;
520
+ const models = source === 'default'
521
+ ? 'lib defaults (organizationModels omitted or true)'
522
+ : source === 'explicit'
523
+ ? 'host-provided explicit trio'
524
+ : 'lib defaults or host-provided models';
512
525
  return {
513
526
  level: 'ok',
514
- message: `organizations capability present (roles: ${roles.join(', ')}).`,
527
+ message: `organizations capability present (models: ${models}; roles: ${roles.join(', ')}).`,
515
528
  };
516
529
  }
517
530
  // Auto mode (enabled === undefined) and store doesn't support — silently ok (opt-in).
@@ -26,3 +26,20 @@ export declare function decodeActiveOrgCookie(value: string | null | undefined):
26
26
  * NOTA: o oidc-provider ctx.cookies.get() nunca lança — retorna null se ausente.
27
27
  */
28
28
  export declare function readActiveOrgFromKoaCtx(koaCtx: any): ActiveOrgInfo | null;
29
+ /**
30
+ * Normaliza um valor potencialmente vindo de um payload PERSISTIDO (o `activeOrg`
31
+ * do Grant) para `ActiveOrgInfo`. Retorna null quando a forma não bate — nunca
32
+ * confiamos num objeto só porque ele veio do banco.
33
+ */
34
+ export declare function normalizeActiveOrg(value: unknown): ActiveOrgInfo | null;
35
+ /**
36
+ * Lê a org ativa de um contexto de interaction do HOST (o `HttpContext` do
37
+ * AdonisJS usado pelas `InteractionActions`).
38
+ *
39
+ * No consent a request É do browser, então o cookie está presente — ao contrário
40
+ * do mint do id_token no `/token` (server-a-servidor, sem cookies). O cookie é
41
+ * gravado UNSIGNED por `account_orgs_controller.activate` e lido aqui via
42
+ * `request.cookie` (o MESMO caminho das leituras do console/account API), com
43
+ * fallback para o caminho Koa caso o contexto recebido seja um ctx Koa.
44
+ */
45
+ export declare function readActiveOrgFromHostCtx(ctx: unknown): ActiveOrgInfo | null;
@@ -26,6 +26,27 @@ export function decodeActiveOrgCookie(value) {
26
26
  return null;
27
27
  return { orgId, orgSlug, orgRole };
28
28
  }
29
+ /**
30
+ * Parseia um valor de cookie possivelmente URL-encoded.
31
+ *
32
+ * O jar Koa do oidc-provider (`cookies`) devolve o valor COMO ESTÁ no header — sem
33
+ * URL-decode. Como o host grava o cookie via `response.cookie` (que serializa com
34
+ * `encodeURIComponent`, transformando os TABs em `%09`), é preciso tentar decodificar.
35
+ * Tentamos o valor cru primeiro (hosts que gravem sem encode) e o decodificado depois.
36
+ */
37
+ function parseActiveOrgCookieValue(raw) {
38
+ if (typeof raw !== 'string')
39
+ return null;
40
+ const direct = decodeActiveOrgCookie(raw);
41
+ if (direct)
42
+ return direct;
43
+ try {
44
+ return decodeActiveOrgCookie(decodeURIComponent(raw));
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ }
29
50
  /**
30
51
  * Lê a org ativa de um contexto Koa (oidc-provider). O oidc-provider usa o Keygrip
31
52
  * das `cookieKeys` para assinar os cookies — lemos via `ctx.cookies.get(name, { signed: false })`
@@ -39,9 +60,50 @@ export function decodeActiveOrgCookie(value) {
39
60
  export function readActiveOrgFromKoaCtx(koaCtx) {
40
61
  try {
41
62
  const raw = koaCtx?.cookies?.get?.(ACTIVE_ORG_COOKIE, { signed: false });
42
- return decodeActiveOrgCookie(raw);
63
+ return parseActiveOrgCookieValue(raw);
43
64
  }
44
65
  catch {
45
66
  return null;
46
67
  }
47
68
  }
69
+ /**
70
+ * Normaliza um valor potencialmente vindo de um payload PERSISTIDO (o `activeOrg`
71
+ * do Grant) para `ActiveOrgInfo`. Retorna null quando a forma não bate — nunca
72
+ * confiamos num objeto só porque ele veio do banco.
73
+ */
74
+ export function normalizeActiveOrg(value) {
75
+ if (!value || typeof value !== 'object')
76
+ return null;
77
+ const { orgId, orgSlug, orgRole } = value;
78
+ if (typeof orgId !== 'string' ||
79
+ !orgId ||
80
+ typeof orgSlug !== 'string' ||
81
+ !orgSlug ||
82
+ typeof orgRole !== 'string' ||
83
+ !orgRole) {
84
+ return null;
85
+ }
86
+ return { orgId, orgSlug, orgRole };
87
+ }
88
+ /**
89
+ * Lê a org ativa de um contexto de interaction do HOST (o `HttpContext` do
90
+ * AdonisJS usado pelas `InteractionActions`).
91
+ *
92
+ * No consent a request É do browser, então o cookie está presente — ao contrário
93
+ * do mint do id_token no `/token` (server-a-servidor, sem cookies). O cookie é
94
+ * gravado UNSIGNED por `account_orgs_controller.activate` e lido aqui via
95
+ * `request.cookie` (o MESMO caminho das leituras do console/account API), com
96
+ * fallback para o caminho Koa caso o contexto recebido seja um ctx Koa.
97
+ */
98
+ export function readActiveOrgFromHostCtx(ctx) {
99
+ try {
100
+ const raw = ctx?.request?.cookie?.(ACTIVE_ORG_COOKIE);
101
+ const parsed = parseActiveOrgCookieValue(raw);
102
+ if (parsed)
103
+ return parsed;
104
+ }
105
+ catch {
106
+ // contexto sem `request.cookie` — tenta o caminho Koa abaixo
107
+ }
108
+ return readActiveOrgFromKoaCtx(ctx);
109
+ }
@@ -1,5 +1,6 @@
1
1
  import * as oidc from 'oidc-provider';
2
2
  import { pickModelAdapterClass } from '../adapters/factory.js';
3
+ import { normalizeActiveOrg, readActiveOrgFromKoaCtx } from '../host/active_org_cookie.js';
3
4
  import { createDeviceSources } from './device_sources.js';
4
5
  import { createLogoutSources } from './logout_sources.js';
5
6
  /** Atualiza o holder mutável do TTL de sessão com os valores da setting. */
@@ -117,6 +118,35 @@ export function buildProvider(config, options, sessionTtlHolder, tokenTtlHolder)
117
118
  : {}),
118
119
  })),
119
120
  findAccount: options.findAccount,
121
+ // Grant REAPROVEITADO (consent já lembrado): o org capturado no primeiro
122
+ // consent ficaria VELHO se o usuário trocasse de org no console e voltasse a
123
+ // autenticar sem novo consent. Esta função roda numa request do BROWSER (o
124
+ // authorize), então relemos o cookie aqui e atualizamos o Grant quando
125
+ // divergir — inclusive LIMPANDO quando o cookie some (org desativada). Sem
126
+ // isto o token continuaria emitindo a org antiga até o grant expirar.
127
+ // Assinatura/default confirmados no fonte instalado: `actions/authorization/
128
+ // session.js` chama `configuration.loadExistingGrant(ctx)`.
129
+ loadExistingGrant: async (ctx) => {
130
+ const grantId = ctx.oidc.result?.consent?.grantId || ctx.oidc.session.grantIdFor(ctx.oidc.client.clientId);
131
+ if (!grantId)
132
+ return undefined;
133
+ const grant = await ctx.oidc.provider.Grant.find(grantId);
134
+ if (!grant)
135
+ return undefined;
136
+ const activeOrg = readActiveOrgFromKoaCtx(ctx);
137
+ const current = normalizeActiveOrg(grant.activeOrg);
138
+ const changed = (activeOrg?.orgId ?? null) !== (current?.orgId ?? null) ||
139
+ (activeOrg?.orgSlug ?? null) !== (current?.orgSlug ?? null) ||
140
+ (activeOrg?.orgRole ?? null) !== (current?.orgRole ?? null);
141
+ if (changed) {
142
+ if (activeOrg)
143
+ grant.activeOrg = activeOrg;
144
+ else
145
+ delete grant.activeOrg;
146
+ await grant.save();
147
+ }
148
+ return grant;
149
+ },
120
150
  jwks: config.jwks,
121
151
  cookies: {
122
152
  keys: cookieKeys,
@@ -220,6 +250,29 @@ export function buildProvider(config, options, sessionTtlHolder, tokenTtlHolder)
220
250
  url: (_ctx, interaction) => `/auth/interaction/${interaction.uid}`,
221
251
  },
222
252
  });
253
+ // ── Modelo Grant estendido: persiste a org ativa ─────────────────────────────
254
+ // O oidc-provider v9 NÃO expõe uma opção `models` no config (confirmado no fonte
255
+ // instalado: `provider.js` instancia os modelos por `models.getGrant(this)` e
256
+ // `helpers/defaults.js` não tem chave alguma `models`), e o `Grant` filtra o
257
+ // payload por `IN_PAYLOAD` (`lib/models/grant.js` + `base_model.js`), então
258
+ // qualquer prop de topo fora da lista é DESCARTADA no `save()`/`find()`.
259
+ // Estendemos `IN_PAYLOAD` com `activeOrg` e injetamos a subclasse na INSTÂNCIA
260
+ // do provider via `Object.defineProperty`, que sombreia o getter `Grant` do
261
+ // protótipo. O `name` é fixado em `'Grant'` para que o nome do ADAPTER e o
262
+ // `kind` persistido continuem sendo os MESMOS registros (`BaseModel` usa
263
+ // `constructor.name` para ambos) — sem isso, grants novos/antigos divergiriam.
264
+ const BaseGrant = provider.Grant;
265
+ const GrantWithOrg = class GrantWithOrg extends BaseGrant {
266
+ static get IN_PAYLOAD() {
267
+ return [...BaseGrant.IN_PAYLOAD, 'activeOrg'];
268
+ }
269
+ };
270
+ Object.defineProperty(GrantWithOrg, 'name', { value: 'Grant', configurable: true });
271
+ Object.defineProperty(provider, 'Grant', {
272
+ value: GrantWithOrg,
273
+ writable: true,
274
+ configurable: true,
275
+ });
223
276
  provider.proxy = true;
224
277
  return provider;
225
278
  }
@@ -1,3 +1,4 @@
1
+ import { readActiveOrgFromHostCtx } from '../host/active_org_cookie.js';
1
2
  import { InteractionSessionLostException, isInteractionSessionLost, } from '../host/interaction_recovery.js';
2
3
  /**
3
4
  * Choke point ÚNICO de `interactionDetails`: converte o `SessionNotFound` do
@@ -51,9 +52,15 @@ export function createInteractionActions(provider, deps) {
51
52
  },
52
53
  async consent(ctx) {
53
54
  const details = await interactionDetailsOrRecover(provider, ctx);
55
+ // O consent roda numa request do BROWSER, então a org ativa (cookie) está
56
+ // disponível AQUI — ao contrário do mint do id_token no /token, que é
57
+ // server-a-servidor e não carrega os cookies do usuário. Persistimos a org
58
+ // no Grant para que o fluxo authorization code volte a emitir org_*.
59
+ const activeOrg = readActiveOrgFromHostCtx(ctx);
54
60
  const grant = new provider.Grant({
55
61
  accountId: details.session.accountId,
56
62
  clientId: details.params.client_id,
63
+ ...(activeOrg ? { activeOrg } : {}),
57
64
  });
58
65
  grant.addOIDCScope(String(details.params.scope ?? 'openid'));
59
66
  // Resource Indicators (RFC 8707): quando o authorize/token pede um `resource`
@@ -2,7 +2,7 @@ import { timingSafeEqual } from 'node:crypto';
2
2
  import { NoopRecorder } from '@adonis-agora/authkit-core';
3
3
  import Koa from 'koa';
4
4
  import mount from 'koa-mount';
5
- import { readActiveOrgFromKoaCtx } from '../host/active_org_cookie.js';
5
+ import { normalizeActiveOrg, readActiveOrgFromKoaCtx } from '../host/active_org_cookie.js';
6
6
  import { isFirstPartyClient } from '../host/branding.js';
7
7
  import { listKeyInfos, signingKeyAgeDays } from '../keys/keystore.js';
8
8
  import { wireProviderEvents } from '../observability/wire_provider_events.js';
@@ -82,8 +82,13 @@ export class OidcService {
82
82
  const user = await config.findAccount(sub);
83
83
  if (!user)
84
84
  return undefined;
85
- // Lê a org ativa do cookie de sessão (se organizations estiver disponível).
86
- const activeOrg = readActiveOrgFromKoaCtx(ctx);
85
+ // Fonte PRIMÁRIA: a org persistida no Grant no consent (request do
86
+ // browser). No fluxo authorization code o id_token é mintado no /token
87
+ // — server-a-servidor, SEM cookies — então o cookie abaixo não existe
88
+ // ali. O cookie continua como FALLBACK para fluxos em que o id_token
89
+ // sai no próprio authorize (implicit/hybrid), onde a request é do browser.
90
+ const activeOrg = normalizeActiveOrg(ctx?.oidc?.entities?.Grant?.activeOrg) ??
91
+ readActiveOrgFromKoaCtx(ctx);
87
92
  // GATE de least-privilege: roles globais e claims de org só são emitidas
88
93
  // para clients FIRST-PARTY. Capturamos o clientId aqui (fora do closure
89
94
  // `claims()`, pois o `ctx` da request vive neste escopo).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonis-agora/authkit-server",
3
- "version": "0.67.0",
3
+ "version": "0.68.1",
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",