@fonderie/auth 1.2.0 → 1.3.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.cjs +53 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +39 -24
- package/dist/index.js.map +1 -1
- package/dist/middlewares/index.cjs +39 -0
- package/dist/middlewares/index.cjs.map +1 -1
- package/dist/middlewares/index.d.cts +1 -1
- package/dist/middlewares/index.d.ts +1 -1
- package/dist/middlewares/index.js +39 -0
- package/dist/middlewares/index.js.map +1 -1
- package/dist/{session-C54ksYE8.d.cts → session-DbtCnbiG.d.cts} +1 -0
- package/dist/{session-C54ksYE8.d.ts → session-DbtCnbiG.d.ts} +1 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -2,8 +2,8 @@ export { IMfaChallenge, ISession, IUser } from './types.cjs';
|
|
|
2
2
|
import { IFonderieModule, IFonderieApp } from '@fonderie/core';
|
|
3
3
|
import { IStoreAdapter } from '@fonderie/store';
|
|
4
4
|
import { EventBus } from '@fonderie/events';
|
|
5
|
-
import { I as IAuthConfig } from './session-
|
|
6
|
-
export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, M as MESSAGE_KEYS, f as buildAuthAccountLimiter, g as buildAuthIpLimiter, w as withSession } from './session-
|
|
5
|
+
import { I as IAuthConfig } from './session-DbtCnbiG.cjs';
|
|
6
|
+
export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, M as MESSAGE_KEYS, f as buildAuthAccountLimiter, g as buildAuthIpLimiter, w as withSession } from './session-DbtCnbiG.cjs';
|
|
7
7
|
export { IUserDTO, toUserDTO } from './dtos/user.cjs';
|
|
8
8
|
export { requireAuth, validate } from '@fonderie/core/middlewares';
|
|
9
9
|
import { z } from 'zod';
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export { IMfaChallenge, ISession, IUser } from './types.js';
|
|
|
2
2
|
import { IFonderieModule, IFonderieApp } from '@fonderie/core';
|
|
3
3
|
import { IStoreAdapter } from '@fonderie/store';
|
|
4
4
|
import { EventBus } from '@fonderie/events';
|
|
5
|
-
import { I as IAuthConfig } from './session-
|
|
6
|
-
export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, M as MESSAGE_KEYS, f as buildAuthAccountLimiter, g as buildAuthIpLimiter, w as withSession } from './session-
|
|
5
|
+
import { I as IAuthConfig } from './session-DbtCnbiG.js';
|
|
6
|
+
export { A as AUTH_CONFIG_KEYS, a as AuthLimitedRoute, b as AuthMessageKey, c as IAuthRateLimitConfig, d as IAuthRuntimeConfig, e as IAuthSecrets, M as MESSAGE_KEYS, f as buildAuthAccountLimiter, g as buildAuthIpLimiter, w as withSession } from './session-DbtCnbiG.js';
|
|
7
7
|
export { IUserDTO, toUserDTO } from './dtos/user.js';
|
|
8
8
|
export { requireAuth, validate } from '@fonderie/core/middlewares';
|
|
9
9
|
import { z } from 'zod';
|
package/dist/index.js
CHANGED
|
@@ -219,6 +219,7 @@ var EVENT_KEYS = {
|
|
|
219
219
|
};
|
|
220
220
|
|
|
221
221
|
// src/services/jwt.ts
|
|
222
|
+
import { randomUUID } from "crypto";
|
|
222
223
|
import jwt from "jsonwebtoken";
|
|
223
224
|
function issueMfaPendingToken(userId, config, loginMethod) {
|
|
224
225
|
return jwt.sign(
|
|
@@ -235,19 +236,21 @@ function issueMfaPendingToken(userId, config, loginMethod) {
|
|
|
235
236
|
}
|
|
236
237
|
function issueTokenPair(userId, config, options) {
|
|
237
238
|
const duration = config.sessionDuration ?? "7d";
|
|
239
|
+
const accessDuration = config.accessTokenDuration ?? "24h";
|
|
238
240
|
const loginMethod = options.loginMethod;
|
|
239
241
|
const phoneVerified = options.phoneVerified ?? false;
|
|
242
|
+
const sid = randomUUID();
|
|
240
243
|
const accessToken = jwt.sign(
|
|
241
|
-
{ sub: userId, type: "access", loginMethod, phoneVerified },
|
|
244
|
+
{ sub: userId, type: "access", loginMethod, phoneVerified, sid },
|
|
242
245
|
config.jwtSecret,
|
|
243
|
-
{ expiresIn:
|
|
246
|
+
{ expiresIn: accessDuration }
|
|
244
247
|
);
|
|
245
248
|
const refreshToken = jwt.sign(
|
|
246
|
-
{ sub: userId, type: "refresh", loginMethod, phoneVerified },
|
|
249
|
+
{ sub: userId, type: "refresh", loginMethod, phoneVerified, sid },
|
|
247
250
|
config.jwtSecret,
|
|
248
251
|
{ expiresIn: duration }
|
|
249
252
|
);
|
|
250
|
-
return { accessToken, refreshToken };
|
|
253
|
+
return { accessToken, refreshToken, sid };
|
|
251
254
|
}
|
|
252
255
|
function refreshTokenExpiry(token) {
|
|
253
256
|
const decoded = jwt.decode(token);
|
|
@@ -603,12 +606,12 @@ var SessionModel = class {
|
|
|
603
606
|
this.store = store;
|
|
604
607
|
}
|
|
605
608
|
store;
|
|
606
|
-
async create(userId, token, expiresAt) {
|
|
609
|
+
async create(userId, token, expiresAt, sid) {
|
|
607
610
|
await this.store.query(
|
|
608
|
-
`INSERT INTO fonderie_sessions (user_id, token, expires_at)
|
|
609
|
-
VALUES ($1, $2, $3)
|
|
611
|
+
`INSERT INTO fonderie_sessions (user_id, token, expires_at, sid)
|
|
612
|
+
VALUES ($1, $2, $3, $4)
|
|
610
613
|
ON CONFLICT (token) DO NOTHING`,
|
|
611
|
-
[userId, token, expiresAt]
|
|
614
|
+
[userId, token, expiresAt, sid ?? null]
|
|
612
615
|
);
|
|
613
616
|
}
|
|
614
617
|
async delete(token) {
|
|
@@ -621,6 +624,14 @@ var SessionModel = class {
|
|
|
621
624
|
);
|
|
622
625
|
return rows.length > 0;
|
|
623
626
|
}
|
|
627
|
+
// Liveness check for session-bound access tokens (by the sid claim).
|
|
628
|
+
async aliveBySid(sid) {
|
|
629
|
+
const rows = await this.store.query(
|
|
630
|
+
`SELECT id FROM fonderie_sessions WHERE sid = $1 AND expires_at > now()`,
|
|
631
|
+
[sid]
|
|
632
|
+
);
|
|
633
|
+
return rows.length > 0;
|
|
634
|
+
}
|
|
624
635
|
};
|
|
625
636
|
|
|
626
637
|
// src/models/backup-code.model.ts
|
|
@@ -751,10 +762,10 @@ function mfaController(store, config, issuer, bus) {
|
|
|
751
762
|
await users.enableMfa(ctx.user.id);
|
|
752
763
|
}
|
|
753
764
|
}
|
|
754
|
-
const { accessToken, refreshToken } = issueTokenPair(ctx.user.id, config, {
|
|
765
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(ctx.user.id, config, {
|
|
755
766
|
loginMethod: ctx.user.loginMethod
|
|
756
767
|
});
|
|
757
|
-
await sessions.create(ctx.user.id, refreshToken, refreshTokenExpiry(refreshToken));
|
|
768
|
+
await sessions.create(ctx.user.id, refreshToken, refreshTokenExpiry(refreshToken), sid);
|
|
758
769
|
const fullUser = await users.findById(ctx.user.id);
|
|
759
770
|
if (!fullUser) {
|
|
760
771
|
return setApiResponse2(HTTP2.SERVER_ERROR, "SERVER_ERROR", "User not found after MFA verify");
|
|
@@ -1086,10 +1097,10 @@ function authController(store, config, bus) {
|
|
|
1086
1097
|
reqOpts
|
|
1087
1098
|
).catch(() => {
|
|
1088
1099
|
});
|
|
1089
|
-
const { accessToken, refreshToken } = issueTokenPair(user.id, config, {
|
|
1100
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(user.id, config, {
|
|
1090
1101
|
loginMethod: "email"
|
|
1091
1102
|
});
|
|
1092
|
-
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken));
|
|
1103
|
+
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken), sid);
|
|
1093
1104
|
const resolvedRegister = { ...config, ...config.resolve?.(ctx) };
|
|
1094
1105
|
const requiresVerification = !!resolvedRegister.requireVerification && !user.emailVerifiedAt;
|
|
1095
1106
|
return Response.json(
|
|
@@ -1151,10 +1162,10 @@ function authController(store, config, bus) {
|
|
|
1151
1162
|
reqOpts2
|
|
1152
1163
|
).catch(() => {
|
|
1153
1164
|
});
|
|
1154
|
-
const { accessToken, refreshToken } = issueTokenPair(user.id, config, {
|
|
1165
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(user.id, config, {
|
|
1155
1166
|
loginMethod: "phone"
|
|
1156
1167
|
});
|
|
1157
|
-
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken));
|
|
1168
|
+
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken), sid);
|
|
1158
1169
|
return Response.json(
|
|
1159
1170
|
{
|
|
1160
1171
|
reason: "USER_PHONE_REGISTERED",
|
|
@@ -1207,10 +1218,10 @@ function authController(store, config, bus) {
|
|
|
1207
1218
|
mfaToken
|
|
1208
1219
|
});
|
|
1209
1220
|
}
|
|
1210
|
-
const { accessToken, refreshToken } = issueTokenPair(user.id, config, {
|
|
1221
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(user.id, config, {
|
|
1211
1222
|
loginMethod: "email"
|
|
1212
1223
|
});
|
|
1213
|
-
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken));
|
|
1224
|
+
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken), sid);
|
|
1214
1225
|
const resolvedLogin = { ...config, ...config.resolve?.(ctx) };
|
|
1215
1226
|
const requiresVerification = !!resolvedLogin.requireVerification && !user.emailVerifiedAt;
|
|
1216
1227
|
return Response.json(
|
|
@@ -1253,10 +1264,10 @@ function authController(store, config, bus) {
|
|
|
1253
1264
|
recipient: { email: null, phone: normalizePhone(phone2), deviceToken: null }
|
|
1254
1265
|
}).catch(() => {
|
|
1255
1266
|
});
|
|
1256
|
-
const { accessToken, refreshToken } = issueTokenPair(user.id, config, {
|
|
1267
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(user.id, config, {
|
|
1257
1268
|
loginMethod: "phone"
|
|
1258
1269
|
});
|
|
1259
|
-
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken));
|
|
1270
|
+
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken), sid);
|
|
1260
1271
|
return Response.json(
|
|
1261
1272
|
{
|
|
1262
1273
|
reason: "USER_PHONE_OTP_SENT",
|
|
@@ -1317,11 +1328,11 @@ function authController(store, config, bus) {
|
|
|
1317
1328
|
return setApiResponse3(HTTP3.UNAUTHORIZED, "UNAUTHORIZED", "Unauthorized");
|
|
1318
1329
|
}
|
|
1319
1330
|
await sessions.delete(token);
|
|
1320
|
-
const { accessToken, refreshToken } = issueTokenPair(user.id, config, {
|
|
1331
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(user.id, config, {
|
|
1321
1332
|
loginMethod: payload.loginMethod ?? "email",
|
|
1322
1333
|
phoneVerified: payload.phoneVerified ?? false
|
|
1323
1334
|
});
|
|
1324
|
-
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken));
|
|
1335
|
+
await sessions.create(user.id, refreshToken, refreshTokenExpiry(refreshToken), sid);
|
|
1325
1336
|
return Response.json(
|
|
1326
1337
|
{
|
|
1327
1338
|
reason: "TOKENS_REFRESHED",
|
|
@@ -1452,11 +1463,11 @@ function authController(store, config, bus) {
|
|
|
1452
1463
|
);
|
|
1453
1464
|
}
|
|
1454
1465
|
await phoneVerif.deleteByUser(ctx.user.id);
|
|
1455
|
-
const { accessToken, refreshToken } = issueTokenPair(ctx.user.id, config, {
|
|
1466
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(ctx.user.id, config, {
|
|
1456
1467
|
loginMethod: "phone",
|
|
1457
1468
|
phoneVerified: true
|
|
1458
1469
|
});
|
|
1459
|
-
await sessions.create(ctx.user.id, refreshToken, refreshTokenExpiry(refreshToken));
|
|
1470
|
+
await sessions.create(ctx.user.id, refreshToken, refreshTokenExpiry(refreshToken), sid);
|
|
1460
1471
|
const verifiedUser = await users.findById(ctx.user.id);
|
|
1461
1472
|
if (!verifiedUser) {
|
|
1462
1473
|
return setApiResponse3(HTTP3.NOT_FOUND, "NOT_FOUND", "User not found");
|
|
@@ -1874,10 +1885,10 @@ function oauthController(store, config) {
|
|
|
1874
1885
|
if (!fullUser) {
|
|
1875
1886
|
return setApiResponse5(HTTP5.SERVER_ERROR, "SERVER_ERROR", "OAuth login failed");
|
|
1876
1887
|
}
|
|
1877
|
-
const { accessToken, refreshToken } = issueTokenPair(upserted.id, config, {
|
|
1888
|
+
const { accessToken, refreshToken, sid } = issueTokenPair(upserted.id, config, {
|
|
1878
1889
|
loginMethod: "google"
|
|
1879
1890
|
});
|
|
1880
|
-
await sessions.create(upserted.id, refreshToken, refreshTokenExpiry(refreshToken));
|
|
1891
|
+
await sessions.create(upserted.id, refreshToken, refreshTokenExpiry(refreshToken), sid);
|
|
1881
1892
|
return Response.json(
|
|
1882
1893
|
{
|
|
1883
1894
|
reason: "GOOGLE_AUTH_SUCCESS",
|
|
@@ -1959,6 +1970,7 @@ function buildAuthRoutes(store, config, bus) {
|
|
|
1959
1970
|
// src/middlewares/session.ts
|
|
1960
1971
|
function withSession(store, config) {
|
|
1961
1972
|
const users = new UserModel(store);
|
|
1973
|
+
const sessions = new SessionModel(store);
|
|
1962
1974
|
return async (ctx, next) => {
|
|
1963
1975
|
const token = extractToken(ctx.request);
|
|
1964
1976
|
if (!token) {
|
|
@@ -1968,6 +1980,9 @@ function withSession(store, config) {
|
|
|
1968
1980
|
if (!payload || payload.type !== "access") {
|
|
1969
1981
|
return next();
|
|
1970
1982
|
}
|
|
1983
|
+
if (payload.sid && !await sessions.aliveBySid(payload.sid)) {
|
|
1984
|
+
return next();
|
|
1985
|
+
}
|
|
1971
1986
|
const user = await users.findById(payload.sub);
|
|
1972
1987
|
if (!user || user.suspended || user.deletedAt) {
|
|
1973
1988
|
return next();
|