feat: add Go password session lifecycle

This commit is contained in:
zn-admin committed 2026-08-13 15:34:46 +08:00
1 parent 772795e7eb
commit d0207fcebe
19 files changed
+2483 -16

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
{
"version": 1,
"path": "/api/auth/logout",
"methods": ["GET", "POST"],
"status": 307,
"location": "https://app.example.test/auth/login?loggedOut=1",
"requiresAuthentication": false,
"cookieFixture": "session-cookie-v1.json",
"duplicateBaseCookieWrite": false
}
+112
View File
@@ -0,0 +1,112 @@
{
"version": 1,
"path": "/api/auth/password",
"method": "POST",
"localSessionTtlSeconds": 86400,
"inputCases": [
{
"name": "phone and password are trimmed and user authMode input is ignored",
"body": {
"phone": " 13800138000 ",
"password": " TestPass123 ",
"authMode": "admin",
"next": " /assets "
},
"expectedRedirect": "/assets"
},
{
"name": "username is an accepted phone alias and unknown fields are ignored",
"body": {
"username": " 13800138000 ",
"password": "TestPass123",
"authMode": "something-else",
"ignored": true,
"next": "/create?mode=image#prompt"
},
"expectedRedirect": "/create?mode=image#prompt"
}
],
"safeNextCases": [
{ "input": null, "expected": "/create" },
{ "input": "", "expected": "/create" },
{ "input": "assets", "expected": "/create" },
{ "input": "//evil.example/path", "expected": "/create" },
{ "input": "https://evil.example/path", "expected": "/create" },
{ "input": "/\\evil.example/path", "expected": "/create" },
{ "input": "/api/auth/logout", "expected": "/create" },
{ "input": "/foo/../api/auth/logout", "expected": "/create" },
{ "input": "/foo/%2e%2e/api/auth/logout", "expected": "/create" },
{ "input": "/auth/login?next=/assets", "expected": "/create" },
{ "input": "/auth/admin-login", "expected": "/create" },
{ "input": "/assets/../usage?tab=mine#x", "expected": "/usage?tab=mine#x" },
{ "input": "/assets/.", "expected": "/assets/" },
{ "input": "/assets/%41", "expected": "/assets/%41" },
{ "input": "/assets/%2F/item", "expected": "/assets/%2F/item" },
{ "input": "/assets?tab=mine#recent", "expected": "/assets?tab=mine#recent" }
],
"success": {
"topLevelKeys": ["ok", "redirectTo", "user", "authMode"],
"publicUserKeys": [
"id",
"subject",
"username",
"phone",
"displayName",
"clientId",
"organizationId",
"organizationName",
"role",
"status",
"authorities",
"scope"
],
"forbiddenSerializedKeys": [
"password",
"passwordHash",
"passwordSalt",
"failedLoginCount",
"lockedUntil",
"lastLoginAt",
"sessionVersion",
"issuedAt",
"expiresAt",
"accessToken",
"tokenType"
]
},
"errors": {
"invalidInput": {
"status": 400,
"body": { "error": "手机号和密码不能为空。" }
},
"invalidCredentials": {
"status": 401,
"body": { "error": "手机号或密码错误。" }
},
"disabledAccount": {
"status": 403,
"body": { "error": "账号已停用,请联系管理员。" }
},
"disabledOrganization": {
"status": 403,
"body": { "error": "所属组织已停用,请联系管理员。" }
},
"lockedAccount": {
"status": 423,
"body": { "error": "登录失败次数过多,请 15 分钟后再试。" }
},
"rateLimited": {
"status": 429,
"body": { "error": "请求过于频繁,请稍后再试。" }
},
"unconfigured": {
"status": 503,
"body": { "error": "账号认证配置不完整:ZHINIAN_AUTH_SESSION_SECRET" }
}
},
"rateLimit": {
"attemptsPerIp": 30,
"windowSeconds": 900
},
"cookieFixture": "session-cookie-v1.json"
}