@adonis-agora/authkit-server 0.51.1 → 0.52.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/build/host/views/otp-unlock.edge +1 -1
- package/build/host/views/session-expired.edge +21 -0
- package/build/src/define_config.d.ts +33 -0
- package/build/src/define_config.js +6 -0
- package/build/src/host/i18n.d.ts +8 -0
- package/build/src/host/i18n.js +10 -0
- package/build/src/host/interaction_recovery.d.ts +55 -0
- package/build/src/host/interaction_recovery.js +109 -0
- package/build/src/host/renderers/inertia_renderer.d.ts +1 -1
- package/build/src/provider/interaction_actions.js +21 -2
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
|
-
<html lang="
|
|
2
|
+
<html lang="pt-br"><head><meta charset="utf-8"><title>{{ t('otp_unlock.page_title') }}</title>
|
|
3
3
|
@include('authkit::partials/styles')
|
|
4
4
|
</head>
|
|
5
5
|
<body class="min-h-screen bg-gray-100 flex items-center justify-center p-4">
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="pt-br"><head><meta charset="utf-8"><title>{{ t('session_expired.page_title') }} — {{ brand && brand.appName ? brand.appName : t('common.app_fallback') }}</title>
|
|
3
|
+
@include('authkit::partials/styles')
|
|
4
|
+
</head>
|
|
5
|
+
<body class="min-h-screen flex items-center justify-center bg-gray-50 p-4">
|
|
6
|
+
<div class="w-full max-w-sm bg-white p-8 rounded-2xl shadow-xl ring-1 ring-black/5 text-center">
|
|
7
|
+
<div class="mb-4 flex justify-center">
|
|
8
|
+
<span class="inline-flex h-12 w-12 items-center justify-center rounded-full bg-amber-100 text-amber-600">
|
|
9
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
10
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
11
|
+
</svg>
|
|
12
|
+
</span>
|
|
13
|
+
</div>
|
|
14
|
+
<h1 class="text-xl font-semibold text-gray-900">{{ t('session_expired.title') }}</h1>
|
|
15
|
+
<p class="mt-3 text-sm text-gray-600">{{ t('session_expired.body') }}</p>
|
|
16
|
+
<a href="{{ loginUrl ?? '/account/login' }}"
|
|
17
|
+
class="mt-6 inline-block rounded-lg bg-gray-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-gray-700">
|
|
18
|
+
{{ t('session_expired.login_link') }}
|
|
19
|
+
</a>
|
|
20
|
+
</div>
|
|
21
|
+
</body></html>
|
|
@@ -613,6 +613,30 @@ export interface ResolvedWebauthnConfig {
|
|
|
613
613
|
* `rpName` cai no `fallbackName` (branding/mfaIssuer) quando ausente.
|
|
614
614
|
*/
|
|
615
615
|
export declare function resolveWebauthn(issuer: string, fallbackName: string, input?: WebauthnConfigInput): ResolvedWebauthnConfig;
|
|
616
|
+
/**
|
|
617
|
+
* Recuperação da sessão de interaction OIDC perdida (expirada, cookie velho, F5
|
|
618
|
+
* tardio, restart do servidor). Perder essa sessão é um caso NORMAL: em vez de
|
|
619
|
+
* vazar o `SessionNotFound` cru do `oidc-provider`, o authkit RECUPERA.
|
|
620
|
+
*/
|
|
621
|
+
export interface InteractionRecoveryConfigInput {
|
|
622
|
+
/**
|
|
623
|
+
* - `'screen'` (default): renderiza a tela themeável `session-expired` (view
|
|
624
|
+
* Edge built-in `session-expired`, ou a página React do host quando listada
|
|
625
|
+
* no allowlist do `inertiaRenderer`). Props: `{ loginUrl, brand }`.
|
|
626
|
+
* - `'redirect'`: responde 302 para `redirectTo` (recomeço limpo do login).
|
|
627
|
+
*/
|
|
628
|
+
mode?: 'screen' | 'redirect';
|
|
629
|
+
/**
|
|
630
|
+
* Destino do link "voltar ao login" (screen) / do 302 (redirect). Default:
|
|
631
|
+
* `getAccountLoginUrl()` (respeita `accountLoginUrl`). NÃO aponte para uma URL
|
|
632
|
+
* de interaction — evita loop de redirect.
|
|
633
|
+
*/
|
|
634
|
+
redirectTo?: string;
|
|
635
|
+
}
|
|
636
|
+
export interface ResolvedInteractionRecoveryConfig {
|
|
637
|
+
mode: 'screen' | 'redirect';
|
|
638
|
+
redirectTo?: string;
|
|
639
|
+
}
|
|
616
640
|
export interface AuthServerConfigInput {
|
|
617
641
|
issuer: string;
|
|
618
642
|
adapter: AdapterFactory;
|
|
@@ -670,6 +694,13 @@ export interface AuthServerConfigInput {
|
|
|
670
694
|
* `undefined` e o controller chamava `render(ctx, ...)` diretamente.
|
|
671
695
|
*/
|
|
672
696
|
render?: AuthHostRenderer;
|
|
697
|
+
/**
|
|
698
|
+
* Recuperação graciosa da sessão de interaction OIDC perdida. Default:
|
|
699
|
+
* `{ mode: 'screen' }` — renderiza a tela themeável `session-expired`. Use
|
|
700
|
+
* `{ mode: 'redirect' }` para um 302 ao login. Ver
|
|
701
|
+
* {@link InteractionRecoveryConfigInput}.
|
|
702
|
+
*/
|
|
703
|
+
interactionRecovery?: InteractionRecoveryConfigInput;
|
|
673
704
|
/** Configuração de branding por cliente. */
|
|
674
705
|
branding?: BrandingConfig;
|
|
675
706
|
/** Internacionalização das telas. Default: pt-BR embutido (zero config). */
|
|
@@ -899,6 +930,8 @@ export interface ResolvedServerConfig {
|
|
|
899
930
|
/** Destino default pós-confirmação do console (sudo mode) e demais fallbacks de conta. Default: '/account/security'. */
|
|
900
931
|
accountHome?: string;
|
|
901
932
|
render?: AuthHostRenderer;
|
|
933
|
+
/** Recuperação de sessão de interaction perdida resolvida (default `{ mode: 'screen' }`). */
|
|
934
|
+
interactionRecovery: ResolvedInteractionRecoveryConfig;
|
|
902
935
|
branding?: BrandingConfig;
|
|
903
936
|
social?: AuthSocialConfig;
|
|
904
937
|
patIntrospectionSecret?: string;
|
|
@@ -331,6 +331,12 @@ export function defineConfig(config) {
|
|
|
331
331
|
// sem o peer opcional instalado (só quebraria se a request realmente
|
|
332
332
|
// chegasse sem Edge configurado, exatamente como hoje).
|
|
333
333
|
render: config.render ?? edgeRenderer(),
|
|
334
|
+
// Recuperação de sessão de interaction perdida: default gracioso 'screen'
|
|
335
|
+
// (tela themeável `session-expired`); 'redirect' opt-in para 302 ao login.
|
|
336
|
+
interactionRecovery: {
|
|
337
|
+
mode: config.interactionRecovery?.mode ?? 'screen',
|
|
338
|
+
redirectTo: config.interactionRecovery?.redirectTo,
|
|
339
|
+
},
|
|
334
340
|
branding: config.branding,
|
|
335
341
|
social: config.social,
|
|
336
342
|
patIntrospectionSecret: config.patIntrospectionSecret,
|
package/build/src/host/i18n.d.ts
CHANGED
|
@@ -474,6 +474,10 @@ export declare const DEFAULT_MESSAGES: {
|
|
|
474
474
|
'maintenance.title': string;
|
|
475
475
|
'maintenance.default_message': string;
|
|
476
476
|
'maintenance.admin_login_note': string;
|
|
477
|
+
'session_expired.page_title': string;
|
|
478
|
+
'session_expired.title': string;
|
|
479
|
+
'session_expired.body': string;
|
|
480
|
+
'session_expired.login_link': string;
|
|
477
481
|
'password.policy.min_length': string;
|
|
478
482
|
'password.policy.uppercase': string;
|
|
479
483
|
'password.policy.lowercase': string;
|
|
@@ -1178,6 +1182,10 @@ export declare const PT_BR_MESSAGES: {
|
|
|
1178
1182
|
'maintenance.title': string;
|
|
1179
1183
|
'maintenance.default_message': string;
|
|
1180
1184
|
'maintenance.admin_login_note': string;
|
|
1185
|
+
'session_expired.page_title': string;
|
|
1186
|
+
'session_expired.title': string;
|
|
1187
|
+
'session_expired.body': string;
|
|
1188
|
+
'session_expired.login_link': string;
|
|
1181
1189
|
'password.policy.min_length': string;
|
|
1182
1190
|
'password.policy.uppercase': string;
|
|
1183
1191
|
'password.policy.lowercase': string;
|
package/build/src/host/i18n.js
CHANGED
|
@@ -509,6 +509,11 @@ export const DEFAULT_MESSAGES = {
|
|
|
509
509
|
'maintenance.title': 'Under Maintenance',
|
|
510
510
|
'maintenance.default_message': 'The service is temporarily unavailable for maintenance. Please try again shortly.',
|
|
511
511
|
'maintenance.admin_login_note': 'If you are an administrator, you may still log in to manage the system.',
|
|
512
|
+
// Sessão de login (interaction OIDC) expirada/perdida — recuperação graciosa.
|
|
513
|
+
'session_expired.page_title': 'Session expired',
|
|
514
|
+
'session_expired.title': 'Your session expired',
|
|
515
|
+
'session_expired.body': 'Your login session expired or could not be found. This can happen if you left the page open too long. Please start again.',
|
|
516
|
+
'session_expired.login_link': 'Back to login',
|
|
512
517
|
// Política de senha (validação ao definir uma senha nova) + vazamento (HIBP) + histórico + expiração.
|
|
513
518
|
'password.policy.min_length': 'Password must be at least {min} characters long.',
|
|
514
519
|
'password.policy.uppercase': 'Password must contain at least one uppercase letter.',
|
|
@@ -1290,6 +1295,11 @@ export const PT_BR_MESSAGES = {
|
|
|
1290
1295
|
'maintenance.title': 'Em manutenção',
|
|
1291
1296
|
'maintenance.default_message': 'O serviço está temporariamente indisponível para manutenção. Tente novamente em breve.',
|
|
1292
1297
|
'maintenance.admin_login_note': 'Se você é administrador, ainda pode entrar para gerenciar o sistema.',
|
|
1298
|
+
// Sessão de login (interaction OIDC) expirada/perdida — recuperação graciosa.
|
|
1299
|
+
'session_expired.page_title': 'Sessão expirada',
|
|
1300
|
+
'session_expired.title': 'Sua sessão expirou',
|
|
1301
|
+
'session_expired.body': 'Sua sessão de login expirou ou não foi encontrada. Isso pode acontecer se a página ficou aberta por muito tempo. Recomece o login.',
|
|
1302
|
+
'session_expired.login_link': 'Voltar ao login',
|
|
1293
1303
|
// Política de senha (validação ao definir uma senha nova) + vazamento (HIBP) + histórico + expiração.
|
|
1294
1304
|
'password.policy.min_length': 'A senha deve ter no mínimo {min} caracteres.',
|
|
1295
1305
|
'password.policy.uppercase': 'A senha deve conter ao menos uma letra maiúscula.',
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Exception } from '@adonisjs/core/exceptions';
|
|
2
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
|
+
/**
|
|
4
|
+
* Recuperação graciosa da sessão de interaction OIDC perdida.
|
|
5
|
+
*
|
|
6
|
+
* Quando a sessão de interaction do `oidc-provider` está expirada/perdida
|
|
7
|
+
* (cookie velho, F5 tardio depois do TTL, restart do servidor que limpou o
|
|
8
|
+
* store efêmero), `provider.interactionDetails()` lança um `SessionNotFound`
|
|
9
|
+
* — subclasse de `InvalidRequest` (`error: 'invalid_request'`). Sem tratamento
|
|
10
|
+
* isso vaza para o usuário como um erro cru no meio do login.
|
|
11
|
+
*
|
|
12
|
+
* Perder a sessão de interaction é um caso NORMAL, não um bug do host. Por isso
|
|
13
|
+
* o authkit RECUPERA por padrão: renderiza a tela themeável `session-expired`
|
|
14
|
+
* (default) ou redireciona para o login (opt-in via `interactionRecovery`).
|
|
15
|
+
*
|
|
16
|
+
* O ponto de captura é ÚNICO — `createInteractionActions().details/consent`
|
|
17
|
+
* (ver `src/provider/interaction_actions.ts`) embrulham `interactionDetails`
|
|
18
|
+
* e convertem o `SessionNotFound` nesta exceção. TODOS os handlers de
|
|
19
|
+
* interaction (magic link, OTP, identifier, passkeys, MFA, consent…) passam
|
|
20
|
+
* por lá, então nenhum precisa de try/catch próprio.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Discriminador ROBUSTO de "sessão de interaction perdida".
|
|
24
|
+
*
|
|
25
|
+
* Não faz match por string de mensagem (frágil). Usa o NOME DA CLASSE do erro:
|
|
26
|
+
* o `oidc-provider` seta `this.name = this.constructor.name` na base
|
|
27
|
+
* `OIDCProviderError`, então toda instância de `SessionNotFound` carrega
|
|
28
|
+
* `name === 'SessionNotFound'`. Outros `invalid_request` legítimos (base
|
|
29
|
+
* `InvalidRequest`) têm `name === 'InvalidRequest'` — nunca colidem.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isInteractionSessionLost(err: unknown): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Exceção self-handling (contrato do `@adonisjs/http-server`: se o erro tem um
|
|
34
|
+
* método `handle`, o exception handler do host delega para ele). Assim a
|
|
35
|
+
* recuperação roda de forma centralizada, sem depender de o host customizar o
|
|
36
|
+
* `app/exceptions/handler.ts`.
|
|
37
|
+
*/
|
|
38
|
+
export declare class InteractionSessionLostException extends Exception {
|
|
39
|
+
static status: number;
|
|
40
|
+
static code: string;
|
|
41
|
+
constructor();
|
|
42
|
+
handle(_error: this, ctx: HttpContext): Promise<unknown>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Executa a estratégia de recuperação configurada em `interactionRecovery`:
|
|
46
|
+
*
|
|
47
|
+
* - `mode: 'screen'` (default): renderiza a view `session-expired` (Edge
|
|
48
|
+
* built-in, ou a página React do host quando listada no allowlist do
|
|
49
|
+
* `inertiaRenderer`). Recomeço limpo por um link "voltar ao login".
|
|
50
|
+
* - `mode: 'redirect'`: 302 para `redirectTo` (default: `accountLoginUrl`).
|
|
51
|
+
*
|
|
52
|
+
* NÃO cria loop de redirect: o destino default é o login do console de conta
|
|
53
|
+
* (fluxo separado, com sua própria sessão), nunca uma URL de interaction.
|
|
54
|
+
*/
|
|
55
|
+
export declare function recoverLostInteraction(ctx: HttpContext): Promise<unknown>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Exception } from '@adonisjs/core/exceptions';
|
|
2
|
+
import { getAccountLoginUrl } from './account_login_url.js';
|
|
3
|
+
import { brandFor } from './branding.js';
|
|
4
|
+
/**
|
|
5
|
+
* Recuperação graciosa da sessão de interaction OIDC perdida.
|
|
6
|
+
*
|
|
7
|
+
* Quando a sessão de interaction do `oidc-provider` está expirada/perdida
|
|
8
|
+
* (cookie velho, F5 tardio depois do TTL, restart do servidor que limpou o
|
|
9
|
+
* store efêmero), `provider.interactionDetails()` lança um `SessionNotFound`
|
|
10
|
+
* — subclasse de `InvalidRequest` (`error: 'invalid_request'`). Sem tratamento
|
|
11
|
+
* isso vaza para o usuário como um erro cru no meio do login.
|
|
12
|
+
*
|
|
13
|
+
* Perder a sessão de interaction é um caso NORMAL, não um bug do host. Por isso
|
|
14
|
+
* o authkit RECUPERA por padrão: renderiza a tela themeável `session-expired`
|
|
15
|
+
* (default) ou redireciona para o login (opt-in via `interactionRecovery`).
|
|
16
|
+
*
|
|
17
|
+
* O ponto de captura é ÚNICO — `createInteractionActions().details/consent`
|
|
18
|
+
* (ver `src/provider/interaction_actions.ts`) embrulham `interactionDetails`
|
|
19
|
+
* e convertem o `SessionNotFound` nesta exceção. TODOS os handlers de
|
|
20
|
+
* interaction (magic link, OTP, identifier, passkeys, MFA, consent…) passam
|
|
21
|
+
* por lá, então nenhum precisa de try/catch próprio.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Discriminador ROBUSTO de "sessão de interaction perdida".
|
|
25
|
+
*
|
|
26
|
+
* Não faz match por string de mensagem (frágil). Usa o NOME DA CLASSE do erro:
|
|
27
|
+
* o `oidc-provider` seta `this.name = this.constructor.name` na base
|
|
28
|
+
* `OIDCProviderError`, então toda instância de `SessionNotFound` carrega
|
|
29
|
+
* `name === 'SessionNotFound'`. Outros `invalid_request` legítimos (base
|
|
30
|
+
* `InvalidRequest`) têm `name === 'InvalidRequest'` — nunca colidem.
|
|
31
|
+
*/
|
|
32
|
+
export function isInteractionSessionLost(err) {
|
|
33
|
+
if (!err || typeof err !== 'object')
|
|
34
|
+
return false;
|
|
35
|
+
const name = err.name;
|
|
36
|
+
if (name === 'SessionNotFound')
|
|
37
|
+
return true;
|
|
38
|
+
// Fallback defensivo: alguns transpilers/minificadores podem mexer no `name`;
|
|
39
|
+
// o nome do construtor é a fonte primária de `name` no oidc-provider.
|
|
40
|
+
const ctorName = err.constructor?.name;
|
|
41
|
+
return ctorName === 'SessionNotFound';
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Exceção self-handling (contrato do `@adonisjs/http-server`: se o erro tem um
|
|
45
|
+
* método `handle`, o exception handler do host delega para ele). Assim a
|
|
46
|
+
* recuperação roda de forma centralizada, sem depender de o host customizar o
|
|
47
|
+
* `app/exceptions/handler.ts`.
|
|
48
|
+
*/
|
|
49
|
+
export class InteractionSessionLostException extends Exception {
|
|
50
|
+
static status = 400;
|
|
51
|
+
static code = 'E_AUTHKIT_INTERACTION_SESSION_LOST';
|
|
52
|
+
constructor() {
|
|
53
|
+
super('A sessão de login expirou ou não foi encontrada. Recomece o login.', {
|
|
54
|
+
status: 400,
|
|
55
|
+
code: 'E_AUTHKIT_INTERACTION_SESSION_LOST',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async handle(_error, ctx) {
|
|
59
|
+
return recoverLostInteraction(ctx);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Executa a estratégia de recuperação configurada em `interactionRecovery`:
|
|
64
|
+
*
|
|
65
|
+
* - `mode: 'screen'` (default): renderiza a view `session-expired` (Edge
|
|
66
|
+
* built-in, ou a página React do host quando listada no allowlist do
|
|
67
|
+
* `inertiaRenderer`). Recomeço limpo por um link "voltar ao login".
|
|
68
|
+
* - `mode: 'redirect'`: 302 para `redirectTo` (default: `accountLoginUrl`).
|
|
69
|
+
*
|
|
70
|
+
* NÃO cria loop de redirect: o destino default é o login do console de conta
|
|
71
|
+
* (fluxo separado, com sua própria sessão), nunca uma URL de interaction.
|
|
72
|
+
*/
|
|
73
|
+
export async function recoverLostInteraction(ctx) {
|
|
74
|
+
const service = await ctx.containerResolver.make('authkit.server');
|
|
75
|
+
const cfg = service.config;
|
|
76
|
+
const recovery = cfg.interactionRecovery ?? { mode: 'screen' };
|
|
77
|
+
const loginUrl = recovery.redirectTo ?? getAccountLoginUrl();
|
|
78
|
+
if (recovery.mode === 'redirect') {
|
|
79
|
+
return ctx.response.redirect(loginUrl);
|
|
80
|
+
}
|
|
81
|
+
// mode === 'screen': tela themeável. Brand best-effort — a sessão perdida não
|
|
82
|
+
// carrega o client_id, então usamos o brand default (sem cliente).
|
|
83
|
+
const render = cfg.render;
|
|
84
|
+
const brand = cfg.branding ? brandFor(cfg.branding, undefined, undefined) : undefined;
|
|
85
|
+
if (!render) {
|
|
86
|
+
// Sem renderer configurado — degrada para redirect (nunca 500).
|
|
87
|
+
return ctx.response.redirect(loginUrl);
|
|
88
|
+
}
|
|
89
|
+
ctx.response.status(400);
|
|
90
|
+
const body = await render(ctx, 'session-expired', { loginUrl, brand });
|
|
91
|
+
// ESCREVE o body na resposta. Diferente do dispatch normal de rota — onde o
|
|
92
|
+
// `useReturnValue`/`canWriteResponseBody` do http-server envia o valor
|
|
93
|
+
// retornado — o caminho de exception handler DESCARTA o retorno de
|
|
94
|
+
// `InteractionSessionLostException.handle()`. Por isso o modo `screen`
|
|
95
|
+
// precisa fazer o `send` ele mesmo, senão o corpo nunca sai (400 vazio).
|
|
96
|
+
//
|
|
97
|
+
// Replica fielmente o contrato `canWriteResponseBody` do http-server
|
|
98
|
+
// (`value !== undefined && !hasLazyBody && value !== ctx.response`): o guard
|
|
99
|
+
// `hasLazyBody` evita double-write se algum renderer já tiver escrito, e
|
|
100
|
+
// cobre os DOIS renderers built-in (Edge via `view.render` e Inertia via
|
|
101
|
+
// `inertia.render`) — ambos RETORNAM o payload em vez de escrever.
|
|
102
|
+
const res = ctx.response;
|
|
103
|
+
if (body !== undefined && !res.hasLazyBody && body !== ctx.response) {
|
|
104
|
+
res.send(body);
|
|
105
|
+
}
|
|
106
|
+
// Mantém o `return body` para o caminho de dispatch normal (caso a recuperação
|
|
107
|
+
// seja chamada fora do exception handler); no exception path ele é ignorado.
|
|
108
|
+
return body;
|
|
109
|
+
}
|
|
@@ -45,7 +45,7 @@ export interface InertiaRendererOptions {
|
|
|
45
45
|
* O `(string & {})` mantém o tipo aberto: telas custom (ou de versões mais
|
|
46
46
|
* novas da lib) continuam aceitas sem erro de compilação.
|
|
47
47
|
*/
|
|
48
|
-
export type AuthkitScreen = 'login' | 'signup' | 'consent' | 'forgot' | 'reset' | 'verify-email' | 'mfa-challenge' | 'otp-unlock' | 'maintenance' | 'account/login' | 'account/tokens' | 'account/mfa' | 'account/security' | 'account/apps' | 'account/orgs' | 'account/confirm' | 'account/email-confirmed' | (string & {});
|
|
48
|
+
export type AuthkitScreen = 'login' | 'signup' | 'consent' | 'forgot' | 'reset' | 'verify-email' | 'mfa-challenge' | 'otp-unlock' | 'maintenance' | 'session-expired' | 'account/login' | 'account/tokens' | 'account/mfa' | 'account/security' | 'account/apps' | 'account/orgs' | 'account/confirm' | 'account/email-confirmed' | (string & {});
|
|
49
49
|
/**
|
|
50
50
|
* Renderer do seam para hosts Inertia/React.
|
|
51
51
|
*
|
|
@@ -1,8 +1,27 @@
|
|
|
1
|
+
import { InteractionSessionLostException, isInteractionSessionLost, } from '../host/interaction_recovery.js';
|
|
2
|
+
/**
|
|
3
|
+
* Choke point ÚNICO de `interactionDetails`: converte o `SessionNotFound` do
|
|
4
|
+
* `oidc-provider` (sessão de interaction perdida — caso NORMAL) na exceção
|
|
5
|
+
* self-handling `InteractionSessionLostException`, que recupera graciosamente
|
|
6
|
+
* (tela `session-expired` ou redirect) em vez de vazar um erro cru. Todo outro
|
|
7
|
+
* erro propaga inalterado. Usado por `details` e por `consent`, então nenhum
|
|
8
|
+
* handler de interaction precisa de try/catch próprio.
|
|
9
|
+
*/
|
|
10
|
+
async function interactionDetailsOrRecover(provider, ctx) {
|
|
11
|
+
try {
|
|
12
|
+
return await provider.interactionDetails(ctx.request.request, ctx.response.response);
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
if (isInteractionSessionLost(err))
|
|
16
|
+
throw new InteractionSessionLostException();
|
|
17
|
+
throw err;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
1
20
|
/** Lógica de interaction (login/consent) sobre o provider. Testável com um provider fake. */
|
|
2
21
|
export function createInteractionActions(provider, deps) {
|
|
3
22
|
return {
|
|
4
23
|
async details(ctx) {
|
|
5
|
-
return provider
|
|
24
|
+
return interactionDetailsOrRecover(provider, ctx);
|
|
6
25
|
},
|
|
7
26
|
async login(ctx, { email, password }) {
|
|
8
27
|
if (!deps.verifyCredentials) {
|
|
@@ -31,7 +50,7 @@ export function createInteractionActions(provider, deps) {
|
|
|
31
50
|
return { ok: true };
|
|
32
51
|
},
|
|
33
52
|
async consent(ctx) {
|
|
34
|
-
const details = await provider
|
|
53
|
+
const details = await interactionDetailsOrRecover(provider, ctx);
|
|
35
54
|
const grant = new provider.Grant({
|
|
36
55
|
accountId: details.session.accountId,
|
|
37
56
|
clientId: details.params.client_id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/authkit-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.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",
|