@adonis-agora/authkit-server 0.59.0 → 0.60.0

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.
@@ -1,8 +1,10 @@
1
1
  import '../augmentations.js';
2
2
  import { randomUUID } from 'node:crypto';
3
- import { supportsProviderIdentity } from '../../accounts/account_store.js';
3
+ import { supportsLoginMethodsPreference, supportsMagicLink, supportsPasskeys, supportsProviderIdentity, } from '../../accounts/account_store.js';
4
4
  import { assertLoginAllowed } from '../login_attempt.js';
5
5
  import { resolveRuntimeSettingsOrNoop } from '../runtime_settings.js';
6
+ import { resolveEffectiveAuthMethods } from '../runtime_toggles.js';
7
+ import { resolveEffectiveUserLoginMethods, } from '../user_login_methods.js';
6
8
  const UID_SESSION_KEY = 'authkit_social_uid';
7
9
  /**
8
10
  * `AllyService.use()` é tipado contra a interface `SocialProviders`, que só é
@@ -13,6 +15,19 @@ const UID_SESSION_KEY = 'authkit_social_uid';
13
15
  function useProvider(ctx, provider) {
14
16
  return ctx.ally.use(provider);
15
17
  }
18
+ /**
19
+ * Métodos globais efetivos para o gate por usuário do callback social.
20
+ * Espelha o `#loginMethods` do interaction controller (mesma fonte de
21
+ * capabilities), sem acoplar os dois controllers.
22
+ */
23
+ async function resolveGlobalAuthMethodsForUser(cfg, settings) {
24
+ return await resolveEffectiveAuthMethods(settings, {
25
+ configuredSocialProviders: cfg.social?.providers ?? [],
26
+ magicLinkCapable: cfg.passwordless?.magicLink && supportsMagicLink(cfg.accountStore),
27
+ passkeyCapable: supportsPasskeys(cfg.accountStore),
28
+ configOverrides: cfg.authMethods,
29
+ });
30
+ }
16
31
  export default class AuthSocialController {
17
32
  /** GET /auth/:provider/redirect/:uid — guarda o uid e redireciona para o provider OAuth. */
18
33
  async redirect(ctx) {
@@ -118,6 +133,19 @@ export default class AuthSocialController {
118
133
  ctx.session.forget(UID_SESSION_KEY);
119
134
  return ctx.response.redirect(backToLogin);
120
135
  }
136
+ // Preferência por usuário: método social desligado para esta conta → recusa.
137
+ // Aplica-se a contas EXISTENTES (identidade ligada ou e-mail conhecido); uma
138
+ // conta recém-criada pelo callback não tem preferência gravada, então herda
139
+ // os globais — o gate é no-op para ela por construção.
140
+ const existingUser = user;
141
+ if (supportsLoginMethodsPreference(store)) {
142
+ const pref = await store.getLoginMethods(existingUser.id);
143
+ const scoped = resolveEffectiveUserLoginMethods(await resolveGlobalAuthMethodsForUser(cfg, settings), pref);
144
+ if (scoped.social.length === 0) {
145
+ ctx.session.forget(UID_SESSION_KEY);
146
+ return ctx.response.redirect(backToLogin);
147
+ }
148
+ }
121
149
  ctx.session.forget(UID_SESSION_KEY);
122
150
  // Conclui a interaction OIDC para este usuário (escreve o 303 de volta ao authorize).
123
151
  // O cookie de interaction (path '/') sobrevive ao round-trip do provider OAuth.
@@ -607,6 +607,9 @@ export function registerAuthHost(router, opts = {}) {
607
607
  router.delete(`${apiBase}/apps/:clientId`, [C.accountApi, 'revokeApp']);
608
608
  // MFA + passkeys.
609
609
  router.get(`${apiBase}/mfa`, [C.accountApi, 'mfaStatus']);
610
+ // Login methods preference (self-service, por usuário).
611
+ router.get(`${apiBase}/login-methods`, [C.accountApi, 'getLoginMethods']);
612
+ router.put(`${apiBase}/login-methods`, [C.accountApi, 'updateLoginMethods']);
610
613
  router.get(`${apiBase}/passkeys`, [C.accountApi, 'listPasskeys']);
611
614
  router.delete(`${apiBase}/passkeys/:id`, [C.accountApi, 'removePasskey']);
612
615
  // PATs.