Repository navigation
fix: harden authentication, sessions, and billing settlement #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1965f11
e03afa8
439e406
64676dd
e37eebe
f5caf7f
e4e80dd
abdfbfb
5f96f5f
1016df7
771bdfd
ac4dfaa
0ed722b
5cb9263
c65ec92
64d179a
c48a5f7
c03ec65
f8d2947
2c84712
5e56f02
a19e5c0
e1c1d2f
38683f1
d83469e
c3c31c8
419bde1
3b68e69
2852eeb
936216c
93f264d
dfa3c42
8280ad7
674d3c0
48e79d6
1d61f30
b3ea556
1eca291
6103ada
ffdbc61
1de5ddc
52b28ba
0e83267
dbebfb3
90a9d74
679de54
35c6d5c
0219723
edc1a08
7fccaf0
2cc12e6
509beff
247a10d
3d94b07
70e621c
2a76df8
f620d79
00c24e4
2a0ba17
5ce3ee1
1e83f0a
6f5f031
3cacf45
2abf152
a0571f5
9170aca
4a762a0
47f0f96
e6035c9
6e4b2c0
0114540
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,10 +36,6 @@ func PasskeyRegisterBegin(c *gin.Context) { | |
| return | ||
| } | ||
|
|
||
| if !requirePasskeyRegistrationVerification(c, user.Id) { | ||
| return | ||
| } | ||
|
|
||
| credential, err := model.GetPasskeyByUserID(user.Id) | ||
| if err != nil && !errors.Is(err, model.ErrPasskeyNotFound) { | ||
| common.ApiError(c, err) | ||
|
|
@@ -100,10 +96,6 @@ func PasskeyRegisterFinish(c *gin.Context) { | |
| return | ||
| } | ||
|
|
||
| if !requirePasskeyRegistrationVerification(c, user.Id) { | ||
| return | ||
| } | ||
|
|
||
| wa, err := passkeysvc.BuildWebAuthn(c.Request) | ||
| if err != nil { | ||
| common.ApiError(c, err) | ||
|
|
@@ -138,10 +130,12 @@ func PasskeyRegisterFinish(c *gin.Context) { | |
| return | ||
| } | ||
|
|
||
| if err := model.UpsertPasskeyCredential(passkeyCredential); err != nil { | ||
| generation, err := model.ReplacePasskeyCredentialAndBumpSessionGeneration(passkeyCredential) | ||
| if err != nil { | ||
| common.ApiError(c, err) | ||
| return | ||
| } | ||
| preserveCurrentSessionAfterCommittedSecurityChange(c, user.Id, generation, "registering a passkey") | ||
|
Comment on lines
+133
to
+138
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift Broken Authentication (CWE-306): Missing Authentication for Critical Function Reachability: External · Exploitability: Moderate Restore server-side verification before replacing a Passkey credential.
Require a recent server-side verification or reauthentication proof before credential replacement. Define a separate bootstrap rule for accounts with no available verification method. As per coding guidelines: “认证与权限应在路由与接口层校验”. #!/usr/bin/env bash
set -euo pipefail
ast-grep outline controller/passkey.go --items all --type function \
--match 'PasskeyRegisterBegin|PasskeyRegisterFinish'
rg -n -C 12 \
'PasskeyRegister(Begin|Finish)|requirePasskeyRegistrationVerification|SecureVerificationRequired|requireSecureVerificationMethod' \
controller router middleware🤖 Prompt for AI AgentsSource: Coding guidelines |
||
|
|
||
| recordUserSecurityAudit(c, user.Id, "user.passkey_register", nil) | ||
| c.JSON(http.StatusOK, gin.H{ | ||
|
|
@@ -164,10 +158,12 @@ func PasskeyDelete(c *gin.Context) { | |
| return | ||
| } | ||
|
|
||
| if err := model.DeletePasskeyByUserID(user.Id); err != nil { | ||
| generation, err := model.DeletePasskeyAndBumpSessionGeneration(user.Id) | ||
| if err != nil { | ||
| common.ApiError(c, err) | ||
| return | ||
| } | ||
| preserveCurrentSessionAfterCommittedSecurityChange(c, user.Id, generation, "deleting a passkey") | ||
|
|
||
| recordUserSecurityAudit(c, user.Id, "user.passkey_delete", nil) | ||
| c.JSON(http.StatusOK, gin.H{ | ||
|
|
@@ -331,7 +327,7 @@ func PasskeyLoginFinish(c *gin.Context) { | |
| } | ||
| now := time.Now() | ||
| updatedCredential.LastUsedAt = &now | ||
| if err := model.UpsertPasskeyCredential(updatedCredential); err != nil { | ||
| if err := model.UpdatePasskeyCredentialAfterAuthentication(updatedCredential); err != nil { | ||
| common.ApiError(c, err) | ||
| return | ||
| } | ||
|
|
@@ -370,7 +366,7 @@ func AdminResetPasskey(c *gin.Context) { | |
| return | ||
| } | ||
|
|
||
| if err := model.DeletePasskeyByUserID(user.Id); err != nil { | ||
| if _, err := model.DeletePasskeyAndBumpSessionGeneration(user.Id); err != nil { | ||
| common.ApiError(c, err) | ||
| return | ||
| } | ||
|
|
@@ -479,16 +475,21 @@ func PasskeyVerifyFinish(c *gin.Context) { | |
| } | ||
|
|
||
| waUser := passkeysvc.NewWebAuthnUser(user, credential) | ||
| _, err = wa.FinishLogin(waUser, *sessionData, c.Request) | ||
| validatedCredential, err := wa.FinishLogin(waUser, *sessionData, c.Request) | ||
| if err != nil { | ||
| common.ApiError(c, err) | ||
| return | ||
| } | ||
| if validatedCredential == nil { | ||
| common.ApiErrorMsg(c, "Passkey 凭证更新失败") | ||
| return | ||
| } | ||
|
|
||
| // 更新凭证的最后使用时间 | ||
| credential.ApplyValidatedCredential(validatedCredential) | ||
| now := time.Now() | ||
| credential.LastUsedAt = &now | ||
| if err := model.UpsertPasskeyCredential(credential); err != nil { | ||
| if err := model.UpdatePasskeyCredentialAfterAuthentication(credential); err != nil { | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| common.ApiError(c, err) | ||
| return | ||
| } | ||
|
|
@@ -531,26 +532,14 @@ func getSessionUser(c *gin.Context) (*model.User, error) { | |
| return user, nil | ||
| } | ||
|
|
||
| func requirePasskeyRegistrationVerification(c *gin.Context, userID int) bool { | ||
| twoFA, err := model.GetTwoFAByUserId(userID) | ||
| if err != nil { | ||
| common.ApiError(c, err) | ||
| return false | ||
| } | ||
| if twoFA == nil || !twoFA.IsEnabled { | ||
| return true | ||
| } | ||
| return requireSecureVerificationMethod(c, secureVerificationMethod2FA) | ||
| } | ||
|
|
||
| func requirePasskeyDeleteVerification(c *gin.Context, userID int) bool { | ||
| twoFA, err := model.GetTwoFAByUserId(userID) | ||
| if err != nil { | ||
| common.ApiError(c, err) | ||
| return false | ||
| } | ||
| if twoFA != nil && twoFA.IsEnabled { | ||
| return requireSecureVerificationMethod(c, secureVerificationMethod2FA) | ||
| return requireSecureVerificationMethod(c, model.SecureVerificationMethod2FA) | ||
| } | ||
|
|
||
| _, err = model.GetPasskeyByUserID(userID) | ||
|
|
@@ -566,7 +555,7 @@ func requirePasskeyDeleteVerification(c *gin.Context, userID int) bool { | |
| return false | ||
| } | ||
|
|
||
| return requireSecureVerificationMethod(c, secureVerificationMethodPasskey) | ||
| return requireSecureVerificationMethod(c, model.SecureVerificationMethodPasskey) | ||
| } | ||
|
|
||
| func requireSecureVerificationMethod(c *gin.Context, method string) bool { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.