R61 A1-A6:Auth token refresh 主动续期

这个提交包含在:
2026-04-12 16:45:49 +08:00
父节点 7b7a56d7c9
当前提交 dabb03e55b
共修改 5 个文件,包含 213 行新增和 45 行删除
+42 -1
查看文件
@@ -159,6 +159,41 @@ function sanitizeReturnTo(value) {
return trimmed;
}
function parseAccessTokenTtlSeconds(value) {
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
return Math.floor(value);
}
if (typeof value !== "string") {
throw new Error("Invalid access token TTL");
}
const trimmed = value.trim();
if (!trimmed) {
throw new Error("Invalid access token TTL");
}
if (/^\d+$/.test(trimmed)) {
return Number(trimmed);
}
const match = trimmed.match(/^(\d+)\s*([smhd])$/i);
if (!match) {
throw new Error("Invalid access token TTL");
}
const amount = Number(match[1]);
const unit = match[2].toLowerCase();
const secondsByUnit = {
s: 1,
m: 60,
h: 60 * 60,
d: 24 * 60 * 60,
};
return amount * secondsByUnit[unit];
}
async function authRoutes(fastify) {
const loginBodySchema = z.object({
identifier: z.string().min(3),
@@ -174,6 +209,7 @@ async function authRoutes(fastify) {
timeWindow: fastify.config.rateLimitLoginWindow,
})
: undefined;
const accessTokenExpiresIn = parseAccessTokenTtlSeconds(fastify.config.accessTokenTtl);
fastify.post("/login", { preHandler: authRateLimit }, async (request, reply) => {
const parsed = loginBodySchema.safeParse(request.body);
@@ -388,6 +424,7 @@ async function authRoutes(fastify) {
const accessToken = fastify.issueAccessToken(employee);
return sendData(reply, request, {
accessToken,
expiresIn: accessTokenExpiresIn,
mustChangePassword: Boolean(employee.must_change_password),
});
});
@@ -572,7 +609,10 @@ async function authRoutes(fastify) {
);
const accessToken = fastify.issueAccessToken(employee);
return sendData(reply, request, { accessToken });
return sendData(reply, request, {
accessToken,
expiresIn: accessTokenExpiresIn,
});
});
fastify.get("/sso/providers", async (request, reply) => {
@@ -983,6 +1023,7 @@ async function authRoutes(fastify) {
const accessToken = fastify.issueAccessToken(updatedEmployee);
return sendData(reply, request, {
accessToken,
expiresIn: accessTokenExpiresIn,
mustChangePassword: false,
});
});