@adonis-agora/authkit-server 0.88.0 → 0.89.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.
@@ -122,6 +122,22 @@ export interface MailHooks {
122
122
  agentName: string;
123
123
  expiresInMinutes: number;
124
124
  }) => Promise<void>;
125
+ /**
126
+ * Disparado num login bem-sucedido a partir de um IP NOVO para a conta (nenhum
127
+ * `login.success` anterior desse IP no audit sink), quando `notifications.newLoginEmail`
128
+ * está ligado. Best-effort, fire-and-forget: uma falha aqui NUNCA quebra o login.
129
+ * Quando ausente mas o mail estiver configurado, o host-kit envia o e-mail default de
130
+ * "novo login". O `login.new_ip_notified` é auditado nos dois casos.
131
+ */
132
+ onNewLogin?: (data: {
133
+ account: {
134
+ id: string;
135
+ email: string;
136
+ };
137
+ ip: string;
138
+ userAgent?: string | null;
139
+ timestamp: string;
140
+ }) => Promise<void>;
125
141
  /**
126
142
  * Disparado num login bem-sucedido a partir de um dispositivo NOVO (sem cookie
127
143
  * de dispositivo confiável válido para a conta). Best-effort, fire-and-forget:
@@ -51,7 +51,12 @@ export async function notifyLoginSuccess(ctx, cfg, input) {
51
51
  resolvedEmail = (await cfg.accountStore.findById(accountId))?.email ?? null;
52
52
  }
53
53
  if (cfg.notifications.newLoginEmail) {
54
- await maybeNotifyNewLogin(ctx, cfg, { accountId, email: resolvedEmail, ip: ip ?? null });
54
+ await maybeNotifyNewLogin(ctx, cfg, {
55
+ accountId,
56
+ email: resolvedEmail,
57
+ ip: ip ?? null,
58
+ userAgent,
59
+ });
55
60
  }
56
61
  if (onNewDevice && cfg.notifications.newDeviceEmail && resolvedEmail) {
57
62
  await notifyNewDevice(ctx, cfg, {
@@ -108,12 +113,12 @@ async function notifyNewDevice(ctx, cfg, data) {
108
113
  }
109
114
  /**
110
115
  * Verifica se o IP é novo para a conta (consultando o audit sink por
111
- * `login.success` do subject) e, se for, envia o e-mail de alerta + audita
112
- * `login.new_ip_notified`. Degrada para no-op quando: sem IP, sem e-mail, sem
116
+ * `login.success` do subject) e, se for, envia o alerta (hook `mail.onNewLogin`,
117
+ * override do host, ou o mailer default) + audita `login.new_ip_notified`. Degrada para no-op quando: sem IP, sem e-mail, sem
113
118
  * sink consultável (`list`), ou já houve um login.success deste IP antes.
114
119
  */
115
120
  async function maybeNotifyNewLogin(ctx, cfg, data) {
116
- const { accountId, email, ip } = data;
121
+ const { accountId, email, ip, userAgent } = data;
117
122
  if (!ip || !email)
118
123
  return;
119
124
  // Sem consulta do histórico não dá pra decidir se o IP é novo → no-op.
@@ -133,7 +138,25 @@ async function maybeNotifyNewLogin(ctx, cfg, data) {
133
138
  if (sameIpCount > 1)
134
139
  return;
135
140
  const when = new Date().toISOString();
136
- await sendNewLoginEmail(ctx, cfg, { email, ip, when });
141
+ if (cfg.mail?.onNewLogin) {
142
+ // Isolado: o mailer default engole os próprios erros, o hook do host não. Sem
143
+ // este try, um hook que lança cortaria o audit abaixo e o alerta de novo
144
+ // dispositivo, que roda depois deste na mesma tarefa.
145
+ try {
146
+ await cfg.mail.onNewLogin({
147
+ account: { id: accountId, email },
148
+ ip,
149
+ userAgent,
150
+ timestamp: when,
151
+ });
152
+ }
153
+ catch (error) {
154
+ ctx.logger?.error?.({ err: error, accountId }, 'authkit: falha no hook mail.onNewLogin');
155
+ }
156
+ }
157
+ else {
158
+ await sendNewLoginEmail(ctx, cfg, { email, ip, when });
159
+ }
137
160
  await cfg.audit?.record({
138
161
  type: 'login.new_ip_notified',
139
162
  accountId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonis-agora/authkit-server",
3
- "version": "0.88.0",
3
+ "version": "0.89.0",
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
  "@adonis-agora/authkit-core": "0.10.1",
109
109
  "@simplewebauthn/browser": "14.0.0",
110
110
  "@simplewebauthn/server": "14.0.3",
111
- "jose": "6.2.13",
111
+ "jose": "6.2.14",
112
112
  "koa": "3.2.1",
113
113
  "koa-mount": "4.2.0",
114
114
  "oidc-provider": "9.12.2",
@@ -117,8 +117,8 @@
117
117
  },
118
118
  "devDependencies": {
119
119
  "@adonis-agora/authkit-react": "0.26.0",
120
- "@adonis-agora/durable": "0.54.1",
121
- "@adonis-agora/telescope": "0.23.0",
120
+ "@adonis-agora/durable": "0.58.2",
121
+ "@adonis-agora/telescope": "0.27.0",
122
122
  "@adonisjs/ally": "6.3.2",
123
123
  "@adonisjs/auth": "10.1.0",
124
124
  "@adonisjs/core": "7.6.0",