@spfn/auth 0.2.0-beta.70 → 0.2.0-beta.71
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/{authenticate-tmmbh1ux.d.ts → authenticate-NhEb_j27.d.ts} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/nextjs/api.js.map +1 -1
- package/dist/nextjs/server.js +1 -1
- package/dist/nextjs/server.js.map +1 -1
- package/dist/server.d.ts +75 -54
- package/dist/server.js +37 -6
- package/dist/server.js.map +1 -1
- package/package.json +4 -4
package/dist/server.js
CHANGED
|
@@ -5658,6 +5658,25 @@ var init_keys_repository = __esm({
|
|
|
5658
5658
|
).returning();
|
|
5659
5659
|
return result[0] ?? null;
|
|
5660
5660
|
}
|
|
5661
|
+
/**
|
|
5662
|
+
* 사용자의 모든 활성 공개키 revoke (비활성화)
|
|
5663
|
+
*
|
|
5664
|
+
* 비번 변경 시 전체 세션 로그아웃에 사용. authenticate는 활성 키만 검증하므로,
|
|
5665
|
+
* revoke된 키로 서명한 기존 세션의 요청은 즉시 401이 된다.
|
|
5666
|
+
* Write primary 사용
|
|
5667
|
+
*/
|
|
5668
|
+
async revokeAllActiveByUserId(userId, reason) {
|
|
5669
|
+
return await this.db.update(userPublicKeys).set({
|
|
5670
|
+
isActive: false,
|
|
5671
|
+
revokedAt: /* @__PURE__ */ new Date(),
|
|
5672
|
+
revokedReason: reason
|
|
5673
|
+
}).where(
|
|
5674
|
+
and2(
|
|
5675
|
+
eq2(userPublicKeys.userId, userId),
|
|
5676
|
+
eq2(userPublicKeys.isActive, true)
|
|
5677
|
+
)
|
|
5678
|
+
).returning();
|
|
5679
|
+
}
|
|
5661
5680
|
/**
|
|
5662
5681
|
* 공개키 삭제
|
|
5663
5682
|
* Write primary 사용
|
|
@@ -6936,13 +6955,23 @@ function getPublicKeyObject(publicKeyB64) {
|
|
|
6936
6955
|
publicKeyCache.set(publicKeyB64, keyObject);
|
|
6937
6956
|
return keyObject;
|
|
6938
6957
|
}
|
|
6958
|
+
var INSECURE_JWT_SECRET = "dev-secret-key-change-in-production";
|
|
6959
|
+
function getJwtSecret() {
|
|
6960
|
+
const secret = env2.SPFN_AUTH_JWT_SECRET;
|
|
6961
|
+
if ((!secret || secret === INSECURE_JWT_SECRET) && process.env.NODE_ENV === "production") {
|
|
6962
|
+
throw new Error(
|
|
6963
|
+
"SPFN_AUTH_JWT_SECRET must be set to a strong secret in production (the default is public)."
|
|
6964
|
+
);
|
|
6965
|
+
}
|
|
6966
|
+
return secret;
|
|
6967
|
+
}
|
|
6939
6968
|
function generateToken(payload) {
|
|
6940
|
-
return jwt.sign(payload,
|
|
6969
|
+
return jwt.sign(payload, getJwtSecret(), {
|
|
6941
6970
|
expiresIn: env2.SPFN_AUTH_JWT_EXPIRES_IN
|
|
6942
6971
|
});
|
|
6943
6972
|
}
|
|
6944
6973
|
function verifyToken(token) {
|
|
6945
|
-
return jwt.verify(token,
|
|
6974
|
+
return jwt.verify(token, getJwtSecret(), { algorithms: ["HS256"] });
|
|
6946
6975
|
}
|
|
6947
6976
|
function verifyClientToken(token, publicKeyB64, algorithm) {
|
|
6948
6977
|
const publicKeyObject = getPublicKeyObject(publicKeyB64);
|
|
@@ -7082,6 +7111,9 @@ async function validateVerificationCode(target, code, purpose) {
|
|
|
7082
7111
|
if (!record) {
|
|
7083
7112
|
return { valid: false, error: "Invalid verification code" };
|
|
7084
7113
|
}
|
|
7114
|
+
if (record.attempts >= MAX_VERIFICATION_ATTEMPTS) {
|
|
7115
|
+
return { valid: false, error: "Too many attempts, please request a new code" };
|
|
7116
|
+
}
|
|
7085
7117
|
if (record.code !== code) {
|
|
7086
7118
|
await verificationCodesRepository.incrementAttempts(record.id);
|
|
7087
7119
|
return { valid: false, error: "Invalid verification code" };
|
|
@@ -7092,9 +7124,6 @@ async function validateVerificationCode(target, code, purpose) {
|
|
|
7092
7124
|
if (/* @__PURE__ */ new Date() > new Date(record.expiresAt)) {
|
|
7093
7125
|
return { valid: false, error: "Verification code expired" };
|
|
7094
7126
|
}
|
|
7095
|
-
if (record.attempts >= MAX_VERIFICATION_ATTEMPTS) {
|
|
7096
|
-
return { valid: false, error: "Too many attempts, please request a new code" };
|
|
7097
|
-
}
|
|
7098
7127
|
return { valid: true, codeId: record.id };
|
|
7099
7128
|
}
|
|
7100
7129
|
async function markCodeAsUsed(codeId) {
|
|
@@ -7549,6 +7578,7 @@ async function changePasswordService(params) {
|
|
|
7549
7578
|
}
|
|
7550
7579
|
const newPasswordHash = await hashPassword(newPassword);
|
|
7551
7580
|
await usersRepository.updatePassword(userId, newPasswordHash, true);
|
|
7581
|
+
await keysRepository.revokeAllActiveByUserId(userId, "Revoked by password change");
|
|
7552
7582
|
}
|
|
7553
7583
|
|
|
7554
7584
|
// src/server/services/rbac.service.ts
|
|
@@ -9251,7 +9281,8 @@ var acceptInvitation2 = route2.post("/_auth/invitations/accept").input({
|
|
|
9251
9281
|
}),
|
|
9252
9282
|
password: Type.String({
|
|
9253
9283
|
minLength: 8,
|
|
9254
|
-
|
|
9284
|
+
maxLength: 72,
|
|
9285
|
+
description: "User password (8\u201372 characters). bcrypt silently ignores bytes past 72, so longer inputs are rejected rather than truncated."
|
|
9255
9286
|
})
|
|
9256
9287
|
})
|
|
9257
9288
|
}).interceptor({
|