Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
c77c8242
authored
Mar 15, 2026
by
Seefs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
enhance channel key viewing
parent
e4144d60
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
50 additions
and
92 deletions
+50
-92
controller/passkey.go
+9
-0
controller/secure_verification.go
+41
-92
No files found.
controller/passkey.go
View file @
c77c8242
...
...
@@ -470,6 +470,15 @@ func PasskeyVerifyFinish(c *gin.Context) {
return
}
session
:=
sessions
.
Default
(
c
)
// Mark passkey as ready; /api/verify will convert this into the final secure verification session.
session
.
Set
(
PasskeyReadySessionKey
,
time
.
Now
()
.
Unix
())
session
.
Delete
(
SecureVerificationSessionKey
)
if
err
:=
session
.
Save
();
err
!=
nil
{
common
.
ApiError
(
c
,
fmt
.
Errorf
(
"保存验证状态失败: %v"
,
err
))
return
}
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
true
,
"message"
:
"Passkey 验证成功"
,
...
...
controller/secure_verification.go
View file @
c77c8242
...
...
@@ -7,18 +7,19 @@ import (
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/model"
passkeysvc
"github.com/QuantumNous/new-api/service/passkey"
"github.com/QuantumNous/new-api/setting/system_setting"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
)
const
(
// SecureVerificationSessionKey
安全验证的 session key
// SecureVerificationSessionKey
means the user has fully passed secure verification.
SecureVerificationSessionKey
=
"secure_verified_at"
// PasskeyReadySessionKey means WebAuthn finished and /api/verify can finalize step-up verification.
PasskeyReadySessionKey
=
"secure_passkey_ready_at"
// SecureVerificationTimeout 验证有效期(秒)
SecureVerificationTimeout
=
300
// 5分钟
// PasskeyReadyTimeout passkey ready 标记有效期(秒)
PasskeyReadyTimeout
=
60
)
type
UniversalVerifyRequest
struct
{
...
...
@@ -76,6 +77,7 @@ func UniversalVerify(c *gin.Context) {
// 根据验证方式进行验证
var
verified
bool
var
verifyMethod
string
var
err
error
switch
req
.
Method
{
case
"2fa"
:
...
...
@@ -95,10 +97,16 @@ func UniversalVerify(c *gin.Context) {
common
.
ApiError
(
c
,
fmt
.
Errorf
(
"用户未启用Passkey"
))
return
}
// Passkey 验证需要先调用 PasskeyVerifyBegin 和 PasskeyVerifyFinish
// 这里只是验证 Passkey 验证流程是否已经完成
// 实际上,前端应该先调用这两个接口,然后再调用本接口
verified
=
true
// Passkey 验证逻辑已在 PasskeyVerifyFinish 中完成
// Passkey branch only trusts the short-lived marker written by PasskeyVerifyFinish.
verified
,
err
=
consumePasskeyReady
(
c
)
if
err
!=
nil
{
common
.
ApiError
(
c
,
fmt
.
Errorf
(
"Passkey 验证状态异常: %v"
,
err
))
return
}
if
!
verified
{
common
.
ApiError
(
c
,
fmt
.
Errorf
(
"请先完成 Passkey 验证"
))
return
}
verifyMethod
=
"Passkey"
default
:
...
...
@@ -112,10 +120,8 @@ func UniversalVerify(c *gin.Context) {
}
// 验证成功,在 session 中记录时间戳
session
:=
sessions
.
Default
(
c
)
now
:=
time
.
Now
()
.
Unix
()
session
.
Set
(
SecureVerificationSessionKey
,
now
)
if
err
:=
session
.
Save
();
err
!=
nil
{
now
,
err
:=
setSecureVerificationSession
(
c
)
if
err
!=
nil
{
common
.
ApiError
(
c
,
fmt
.
Errorf
(
"保存验证状态失败: %v"
,
err
))
return
}
...
...
@@ -133,94 +139,37 @@ func UniversalVerify(c *gin.Context) {
})
}
// PasskeyVerifyAndSetSession Passkey 验证完成后设置 session
// 这是一个辅助函数,供 PasskeyVerifyFinish 调用
func
PasskeyVerifyAndSetSession
(
c
*
gin
.
Context
)
{
func
setSecureVerificationSession
(
c
*
gin
.
Context
)
(
int64
,
error
)
{
session
:=
sessions
.
Default
(
c
)
session
.
Delete
(
PasskeyReadySessionKey
)
now
:=
time
.
Now
()
.
Unix
()
session
.
Set
(
SecureVerificationSessionKey
,
now
)
_
=
session
.
Save
()
}
// PasskeyVerifyForSecure 用于安全验证的 Passkey 验证流程
// 整合了 begin 和 finish 流程
func
PasskeyVerifyForSecure
(
c
*
gin
.
Context
)
{
if
!
system_setting
.
GetPasskeySettings
()
.
Enabled
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"管理员未启用 Passkey 登录"
,
})
return
}
userId
:=
c
.
GetInt
(
"id"
)
if
userId
==
0
{
c
.
JSON
(
http
.
StatusUnauthorized
,
gin
.
H
{
"success"
:
false
,
"message"
:
"未登录"
,
})
return
}
user
:=
&
model
.
User
{
Id
:
userId
}
if
err
:=
user
.
FillUserById
();
err
!=
nil
{
common
.
ApiError
(
c
,
fmt
.
Errorf
(
"获取用户信息失败: %v"
,
err
))
return
}
if
user
.
Status
!=
common
.
UserStatusEnabled
{
common
.
ApiError
(
c
,
fmt
.
Errorf
(
"该用户已被禁用"
))
return
}
credential
,
err
:=
model
.
GetPasskeyByUserID
(
userId
)
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"该用户尚未绑定 Passkey"
,
})
return
if
err
:=
session
.
Save
();
err
!=
nil
{
return
0
,
err
}
return
now
,
nil
}
wa
,
err
:=
passkeysvc
.
BuildWebAuthn
(
c
.
Request
)
if
err
!=
nil
{
common
.
ApiError
(
c
,
err
)
return
func
consumePasskeyReady
(
c
*
gin
.
Context
)
(
bool
,
error
)
{
session
:=
sessions
.
Default
(
c
)
readyAtRaw
:=
session
.
Get
(
PasskeyReadySessionKey
)
if
readyAtRaw
==
nil
{
return
false
,
nil
}
waUser
:=
passkeysvc
.
NewWebAuthnUser
(
user
,
credential
)
sessionData
,
err
:=
passkeysvc
.
PopSessionData
(
c
,
passkeysvc
.
VerifySessionKey
)
if
err
!=
nil
{
common
.
ApiError
(
c
,
err
)
return
readyAt
,
ok
:=
readyAtRaw
.
(
int64
)
if
!
ok
{
session
.
Delete
(
PasskeyReadySessionKey
)
_
=
session
.
Save
(
)
return
false
,
fmt
.
Errorf
(
"无效的 Passkey 验证状态"
)
}
_
,
err
=
wa
.
FinishLogin
(
waUser
,
*
sessionData
,
c
.
Request
)
if
err
!=
nil
{
common
.
ApiError
(
c
,
err
)
return
session
.
Delete
(
PasskeyReadySessionKey
)
if
err
:=
session
.
Save
();
err
!=
nil
{
return
false
,
err
}
// 更新凭证的最后使用时间
now
:=
time
.
Now
()
credential
.
LastUsedAt
=
&
now
if
err
:=
model
.
UpsertPasskeyCredential
(
credential
);
err
!=
nil
{
common
.
ApiError
(
c
,
err
)
return
// Expired ready markers cannot be reused.
if
time
.
Now
()
.
Unix
()
-
readyAt
>=
PasskeyReadyTimeout
{
return
false
,
nil
}
// 验证成功,设置 session
PasskeyVerifyAndSetSession
(
c
)
// 记录日志
model
.
RecordLog
(
userId
,
model
.
LogTypeSystem
,
"Passkey 安全验证成功"
)
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
true
,
"message"
:
"Passkey 验证成功"
,
"data"
:
gin
.
H
{
"verified"
:
true
,
"expires_at"
:
time
.
Now
()
.
Unix
()
+
SecureVerificationTimeout
,
},
})
return
true
,
nil
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment