@fonderie/auth 7.6.1 → 7.7.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.
package/dist/index.js CHANGED
@@ -1008,13 +1008,23 @@ var MAX_HISTORY_LIMIT = 200;
1008
1008
 
1009
1009
  // src/services/request-meta.ts
1010
1010
  var UA_MAX = 512;
1011
+ var warnedAboutMissingIdentity = false;
1012
+ function warnIfIdentityless(ctx) {
1013
+ if (warnedAboutMissingIdentity) return;
1014
+ warnedAboutMissingIdentity = true;
1015
+ console.warn(
1016
+ `[auth] recording a security event with no client IP and no user-agent for ${ctx.request.method} ${new URL(ctx.request.url).pathname}. Login history and session rows will show "Unknown device" with no IP for these. This is what a hand-built request looks like: if you call fonderie.handle() directly, forward the caller's user-agent header and pass the resolved IP as \`handle(req, { meta: { clientIp } })\` \u2014 resolve it with resolveClientIp from @fonderie/core/middlewares rather than reading the socket, or a proxied deployment records the proxy. Reported once per process.`
1017
+ );
1018
+ }
1011
1019
  function requestMeta(ctx) {
1012
1020
  const ip = ctx.meta["clientIp"];
1013
1021
  const ua = ctx.request.headers.get("user-agent");
1014
- return {
1022
+ const meta = {
1015
1023
  ipAddress: typeof ip === "string" && ip.length > 0 ? ip : null,
1016
1024
  userAgent: ua && ua.length > 0 ? ua.slice(0, UA_MAX) : null
1017
1025
  };
1026
+ if (meta.ipAddress === null && meta.userAgent === null) warnIfIdentityless(ctx);
1027
+ return meta;
1018
1028
  }
1019
1029
 
1020
1030
  // src/controllers/mfa.controller.ts
@@ -1860,12 +1870,29 @@ function authController(store, config, bus) {
1860
1870
  }
1861
1871
  const pin = raw.trim();
1862
1872
  if (ctx.user.loginMethod === "phone") {
1873
+ const phoneMeta = requestMeta(ctx);
1863
1874
  const record = await phoneVerif.findByUser(ctx.user.id, pin);
1864
1875
  if (!record) {
1876
+ await loginEvents.recordSafe({
1877
+ userId: ctx.user.id,
1878
+ emailAttempted: null,
1879
+ method: "phone",
1880
+ outcome: "failed",
1881
+ failureReason: "invalid_pin",
1882
+ ...phoneMeta
1883
+ });
1865
1884
  return setApiResponse3(HTTP3.BAD_REQUEST, "VERIFICATION_FAILED", "Invalid or expired pin");
1866
1885
  }
1867
1886
  if (/* @__PURE__ */ new Date() > record.expiresAt) {
1868
1887
  await phoneVerif.deleteByUser(ctx.user.id);
1888
+ await loginEvents.recordSafe({
1889
+ userId: ctx.user.id,
1890
+ emailAttempted: null,
1891
+ method: "phone",
1892
+ outcome: "failed",
1893
+ failureReason: "expired_pin",
1894
+ ...phoneMeta
1895
+ });
1869
1896
  return setApiResponse3(
1870
1897
  HTTP3.BAD_REQUEST,
1871
1898
  "VERIFICATION_FAILED",
@@ -1877,7 +1904,14 @@ function authController(store, config, bus) {
1877
1904
  loginMethod: "phone",
1878
1905
  phoneVerified: true
1879
1906
  });
1880
- await sessions.create(ctx.user.id, refreshToken, refreshTokenExpiry(refreshToken), sid, requestMeta(ctx));
1907
+ await sessions.create(ctx.user.id, refreshToken, refreshTokenExpiry(refreshToken), sid, phoneMeta);
1908
+ await loginEvents.recordSafe({
1909
+ userId: ctx.user.id,
1910
+ emailAttempted: null,
1911
+ method: "phone",
1912
+ outcome: "success",
1913
+ ...phoneMeta
1914
+ });
1881
1915
  const verifiedUser = await users.findById(ctx.user.id);
1882
1916
  if (!verifiedUser) {
1883
1917
  return setApiResponse3(HTTP3.NOT_FOUND, "NOT_FOUND", "User not found");