@adonis-agora/authkit-server 0.51.0 → 0.51.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.
|
@@ -40,17 +40,31 @@ export default class AuthInteractionController {
|
|
|
40
40
|
* passo login. Sem isto, os re-renders (erro de senha, magic link enviado, lockout…) mandam
|
|
41
41
|
* `authMethods` undefined → a view volta ao default (senha ligada), ignorando a config.
|
|
42
42
|
* `passkeyFirst` depende da conta (resolvido no fluxo principal); aqui cobrimos senha + magic link.
|
|
43
|
+
*
|
|
44
|
+
* `otpEnabled` (disponibilidade do login por OTP: config ligada E store com a
|
|
45
|
+
* capacidade) também sai daqui — é um FATO de disponibilidade de método de login,
|
|
46
|
+
* como `magicLinkAvailable`. Assim TODO render do passo login (identifier, seletor
|
|
47
|
+
* choose-first, magicLinkSent, erro) carrega a flag POR CONSTRUÇÃO: o seletor
|
|
48
|
+
* precisa dela para oferecer a opção "código" ANTES do envio do magic link.
|
|
49
|
+
*
|
|
50
|
+
* `runtimeSettings` pode ser passado já resolvido (ex.: o `show()` já resolveu para
|
|
51
|
+
* maintenance) para evitar uma segunda resolução; ausente, resolve sob demanda.
|
|
43
52
|
*/
|
|
44
|
-
async #loginMethods(ctx, cfg) {
|
|
45
|
-
const
|
|
53
|
+
async #loginMethods(ctx, cfg, runtimeSettings) {
|
|
54
|
+
const settings = runtimeSettings ?? (await getRuntimeSettings(ctx));
|
|
46
55
|
const magicLinkCapableConfig = cfg.passwordless.magicLink && supportsMagicLink(cfg.accountStore);
|
|
47
|
-
const authMethods = await resolveEffectiveAuthMethods(
|
|
56
|
+
const authMethods = await resolveEffectiveAuthMethods(settings, {
|
|
48
57
|
configuredSocialProviders: cfg.social?.providers ?? [],
|
|
49
58
|
magicLinkCapable: magicLinkCapableConfig,
|
|
50
59
|
passkeyCapable: false,
|
|
51
60
|
configOverrides: cfg.authMethods,
|
|
52
61
|
});
|
|
53
|
-
|
|
62
|
+
const otpEnabled = cfg.login.otp.enabled && supportsOtpLogin(cfg.accountStore);
|
|
63
|
+
return {
|
|
64
|
+
authMethods,
|
|
65
|
+
magicLinkAvailable: authMethods.magicLink && magicLinkCapableConfig,
|
|
66
|
+
otpEnabled,
|
|
67
|
+
};
|
|
54
68
|
}
|
|
55
69
|
async show(ctx) {
|
|
56
70
|
const service = await ctx.containerResolver.make('authkit.server');
|
|
@@ -102,36 +116,32 @@ export default class AuthInteractionController {
|
|
|
102
116
|
});
|
|
103
117
|
}
|
|
104
118
|
const email = ctx.session.get(SESSION_KEY);
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
passkeyCapable: false, // passkey-first depends on account — resolved in step 2
|
|
114
|
-
configOverrides: cfg.authMethods,
|
|
115
|
-
});
|
|
119
|
+
// Métodos de login efetivos (magic link, OTP, social) — account-independent
|
|
120
|
+
// neste ponto (passkey-first depende da conta, resolvido no step 2). Reusa o
|
|
121
|
+
// helper compartilhado por TODOS os renders do passo login, para que
|
|
122
|
+
// `authMethods`, `magicLinkAvailable` e `otpEnabled` saiam POR CONSTRUÇÃO
|
|
123
|
+
// (o seletor choose-first precisa de `otpEnabled` para oferecer "código").
|
|
124
|
+
// Passa o runtimeSettings já resolvido para maintenance (sem 2ª resolução).
|
|
125
|
+
const loginMethods = await this.#loginMethods(ctx, cfg, runtimeSettingsForMaintenance);
|
|
126
|
+
const authMethods = loginMethods.authMethods;
|
|
116
127
|
if (!email) {
|
|
117
128
|
// Step 1: identifier (email only).
|
|
118
129
|
// Resolve registration enabled to hide "create account" link when closed.
|
|
119
130
|
const registrationEnabled = await resolveEffectiveRegistration(cfg.registration?.enabled ?? true, runtimeSettingsForMaintenance);
|
|
120
131
|
return render(ctx, 'login', {
|
|
132
|
+
...loginMethods,
|
|
121
133
|
uid: details.uid,
|
|
122
134
|
csrfToken: ctx.request.csrfToken,
|
|
123
135
|
step: 'identifier',
|
|
124
136
|
registrationEnabled,
|
|
125
137
|
brand,
|
|
126
|
-
authMethods,
|
|
127
138
|
});
|
|
128
139
|
}
|
|
129
140
|
// Step 2: password — look up user for personalisation (enumeration-safe: always show step 2)
|
|
130
141
|
const acc = await cfg.accountStore.findByEmail(email);
|
|
131
142
|
const account = acc ? { fullName: acc.name ?? null, globalRoles: acc.globalRoles ?? [] } : null;
|
|
132
|
-
// Passwordless: magic link disponível
|
|
143
|
+
// Passwordless: magic link disponível vem do helper (`loginMethods`).
|
|
133
144
|
// Passkey-first disponível se ligado, o store suporta, a conta tem passkeys E auth_methods permite.
|
|
134
|
-
const magicLinkAvailable = authMethods.magicLink && magicLinkCapableConfig;
|
|
135
145
|
const passkeyFirstCapable = cfg.passwordless.passkeyFirst && !!acc && (await this.hasPasskeys(cfg, acc.id));
|
|
136
146
|
const passkeyFirstAvailable = authMethods.passkey && passkeyFirstCapable;
|
|
137
147
|
// Effective bot protection: may be overridden at runtime via auth_settings.
|
|
@@ -139,16 +149,15 @@ export default class AuthInteractionController {
|
|
|
139
149
|
// Session policy: resolve para exibir o checkbox "manter conectado".
|
|
140
150
|
const sessionPolicyForShow = await resolveEffectiveSessionPolicy(runtimeSettingsForMaintenance, cfg.ttl?.session ? Math.ceil(cfg.ttl.session / 3600) : undefined);
|
|
141
151
|
return render(ctx, 'login', {
|
|
152
|
+
...loginMethods,
|
|
142
153
|
uid: details.uid,
|
|
143
154
|
csrfToken: ctx.request.csrfToken,
|
|
144
155
|
step: 'password',
|
|
145
156
|
email,
|
|
146
157
|
account,
|
|
147
158
|
brand,
|
|
148
|
-
magicLinkAvailable,
|
|
149
159
|
passkeyFirstAvailable,
|
|
150
160
|
botProtection: effectiveBot?.on.includes('login') ? effectiveBot.widget : undefined,
|
|
151
|
-
authMethods,
|
|
152
161
|
rememberEnabled: sessionPolicyForShow.rememberEnabled,
|
|
153
162
|
rememberDays: sessionPolicyForShow.rememberDays,
|
|
154
163
|
});
|
|
@@ -657,6 +666,8 @@ export default class AuthInteractionController {
|
|
|
657
666
|
}
|
|
658
667
|
}
|
|
659
668
|
// Resposta uniforme (não vaza existência de conta).
|
|
669
|
+
// `otpEnabled` vem do spread de `#loginMethods` (mesmo valor do `otpEnabled`
|
|
670
|
+
// local usado acima para decidir a emissão) — não re-declarado aqui.
|
|
660
671
|
return render(ctx, 'login', {
|
|
661
672
|
...(await this.#loginMethods(ctx, cfg)),
|
|
662
673
|
uid,
|
|
@@ -666,7 +677,6 @@ export default class AuthInteractionController {
|
|
|
666
677
|
account: null,
|
|
667
678
|
brand,
|
|
668
679
|
magicLinkSent: true,
|
|
669
|
-
otpEnabled,
|
|
670
680
|
// Prop da tela: qual sub-view do estado `magicLinkSent` mostrar —
|
|
671
681
|
// 'code' (só o campo de código), 'link' (só o aviso de link) ou 'both'
|
|
672
682
|
// (ambos, quando o host não escolheu canal). Back-compat: ausente = 'both'.
|
|
@@ -764,6 +774,8 @@ export default class AuthInteractionController {
|
|
|
764
774
|
maxAttempts: cfg.login.otp.maxAttempts,
|
|
765
775
|
});
|
|
766
776
|
// Re-render da tela "link enviado" com o campo de código + erro localizado.
|
|
777
|
+
// `otpEnabled` vem do spread de `#loginMethods` (aqui, após a guarda acima,
|
|
778
|
+
// vale `true`) — não re-declarado inline.
|
|
767
779
|
const renderOtpError = async (messageKey) => render(ctx, 'login', {
|
|
768
780
|
...(await this.#loginMethods(ctx, cfg)),
|
|
769
781
|
uid,
|
|
@@ -773,7 +785,6 @@ export default class AuthInteractionController {
|
|
|
773
785
|
account: null,
|
|
774
786
|
brand,
|
|
775
787
|
magicLinkSent: true,
|
|
776
|
-
otpEnabled: true,
|
|
777
788
|
magicChannel: magicChannelProp(channel),
|
|
778
789
|
otpError: translate(cfg.messages, messageKey),
|
|
779
790
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/authkit-server",
|
|
3
|
-
"version": "0.51.
|
|
3
|
+
"version": "0.51.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",
|