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
75ee6321
authored
Sep 30, 2025
by
Seefs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: passkey rpid detect
parent
92f81c63
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
63 additions
and
72 deletions
+63
-72
service/passkey/service.go
+5
-3
setting/system_setting/passkey.go
+20
-8
web/src/components/settings/SystemSetting.jsx
+38
-61
No files found.
service/passkey/service.go
View file @
75ee6321
...
...
@@ -80,9 +80,11 @@ func BuildWebAuthn(r *http.Request) (*webauthn.WebAuthn, error) {
}
func
resolveOrigins
(
r
*
http
.
Request
,
settings
*
system_setting
.
PasskeySettings
)
([]
string
,
error
)
{
if
len
(
settings
.
Origins
)
>
0
{
origins
:=
make
([]
string
,
0
,
len
(
settings
.
Origins
))
for
_
,
origin
:=
range
settings
.
Origins
{
originsStr
:=
strings
.
TrimSpace
(
settings
.
Origins
)
if
originsStr
!=
""
{
originList
:=
strings
.
Split
(
originsStr
,
","
)
origins
:=
make
([]
string
,
0
,
len
(
originList
))
for
_
,
origin
:=
range
originList
{
trimmed
:=
strings
.
TrimSpace
(
origin
)
if
trimmed
==
""
{
continue
...
...
setting/system_setting/passkey.go
View file @
75ee6321
package
system_setting
import
(
"net/url"
"one-api/common"
"one-api/setting/config"
"strings"
)
type
PasskeySettings
struct
{
Enabled
bool
`json:"enabled"`
RPDisplayName
string
`json:"rp_display_name"`
RPID
string
`json:"rp_id"`
Origins
[]
string
`json:"origins"`
AllowInsecureOrigin
bool
`json:"allow_insecure_origin"`
UserVerification
string
`json:"user_verification"`
AttachmentPreference
string
`json:"attachment_preference"`
Enabled
bool
`json:"enabled"`
RPDisplayName
string
`json:"rp_display_name"`
RPID
string
`json:"rp_id"`
Origins
string
`json:"origins"`
AllowInsecureOrigin
bool
`json:"allow_insecure_origin"`
UserVerification
string
`json:"user_verification"`
AttachmentPreference
string
`json:"attachment_preference"`
}
var
defaultPasskeySettings
=
PasskeySettings
{
Enabled
:
false
,
RPDisplayName
:
common
.
SystemName
,
RPID
:
""
,
Origins
:
[]
string
{}
,
Origins
:
""
,
AllowInsecureOrigin
:
false
,
UserVerification
:
"preferred"
,
AttachmentPreference
:
""
,
...
...
@@ -30,5 +32,15 @@ func init() {
}
func
GetPasskeySettings
()
*
PasskeySettings
{
if
defaultPasskeySettings
.
RPID
==
""
&&
ServerAddress
!=
""
{
// 从ServerAddress提取域名作为RPID
// ServerAddress可能是 "https://newapi.pro" 这种格式
serverAddr
:=
strings
.
TrimSpace
(
ServerAddress
)
if
parsed
,
err
:=
url
.
Parse
(
serverAddr
);
err
==
nil
&&
parsed
.
Host
!=
""
{
defaultPasskeySettings
.
RPID
=
parsed
.
Host
}
else
{
defaultPasskeySettings
.
RPID
=
serverAddr
}
}
return
&
defaultPasskeySettings
}
web/src/components/settings/SystemSetting.jsx
View file @
75ee6321
...
...
@@ -122,7 +122,6 @@ const SystemSetting = () => {
const
[
domainList
,
setDomainList
]
=
useState
([]);
const
[
ipList
,
setIpList
]
=
useState
([]);
const
[
allowedPorts
,
setAllowedPorts
]
=
useState
([]);
const
[
passkeyOrigins
,
setPasskeyOrigins
]
=
useState
([]);
const
getOptions
=
async
()
=>
{
setLoading
(
true
);
...
...
@@ -188,22 +187,19 @@ const SystemSetting = () => {
item
.
value
=
toBoolean
(
item
.
value
);
break
;
case
'passkey.origins'
:
try
{
const
origins
=
item
.
value
?
JSON
.
parse
(
item
.
value
)
:
[];
setPasskeyOrigins
(
Array
.
isArray
(
origins
)
?
origins
:
[]);
item
.
value
=
Array
.
isArray
(
origins
)
?
origins
:
[];
}
catch
(
e
)
{
setPasskeyOrigins
([]);
item
.
value
=
[];
}
// origins是逗号分隔的字符串,直接使用
item
.
value
=
item
.
value
||
''
;
break
;
case
'passkey.rp_display_name'
:
case
'passkey.rp_id'
:
case
'passkey.user_verification'
:
case
'passkey.attachment_preference'
:
// 确保字符串字段不为null/undefined
item
.
value
=
item
.
value
||
''
;
break
;
case
'passkey.user_verification'
:
// 确保有默认值
item
.
value
=
item
.
value
||
'preferred'
;
break
;
case
'Price'
:
case
'MinTopUp'
:
item
.
value
=
parseFloat
(
item
.
value
);
...
...
@@ -611,42 +607,33 @@ const SystemSetting = () => {
};
const
submitPasskeySettings
=
async
()
=>
{
// 使用formApi直接获取当前表单值
const
formValues
=
formApiRef
.
current
?.
getValues
()
||
{};
const
options
=
[];
// 只在值有变化时才提交,并确保空值转换为空字符串
if
(
originInputs
[
'passkey.rp_display_name'
]
!==
inputs
[
'passkey.rp_display_name'
])
{
options
.
push
({
key
:
'passkey.rp_display_name'
,
value
:
inputs
[
'passkey.rp_display_name'
]
||
''
,
});
}
if
(
originInputs
[
'passkey.rp_id'
]
!==
inputs
[
'passkey.rp_id'
])
{
options
.
push
({
key
:
'passkey.rp_id'
,
value
:
inputs
[
'passkey.rp_id'
]
||
''
,
});
}
if
(
originInputs
[
'passkey.user_verification'
]
!==
inputs
[
'passkey.user_verification'
])
{
options
.
push
({
key
:
'passkey.user_verification'
,
value
:
inputs
[
'passkey.user_verification'
]
||
'preferred'
,
});
}
if
(
originInputs
[
'passkey.attachment_preference'
]
!==
inputs
[
'passkey.attachment_preference'
])
{
options
.
push
({
key
:
'passkey.attachment_preference'
,
value
:
inputs
[
'passkey.attachment_preference'
]
||
''
,
});
}
// Origins总是提交,因为它们可能会被用户清空
options
.
push
({
key
:
'passkey.rp_display_name'
,
value
:
formValues
[
'passkey.rp_display_name'
]
||
inputs
[
'passkey.rp_display_name'
]
||
''
,
});
options
.
push
({
key
:
'passkey.rp_id'
,
value
:
formValues
[
'passkey.rp_id'
]
||
inputs
[
'passkey.rp_id'
]
||
''
,
});
options
.
push
({
key
:
'passkey.user_verification'
,
value
:
formValues
[
'passkey.user_verification'
]
||
inputs
[
'passkey.user_verification'
]
||
'preferred'
,
});
options
.
push
({
key
:
'passkey.attachment_preference'
,
value
:
formValues
[
'passkey.attachment_preference'
]
||
inputs
[
'passkey.attachment_preference'
]
||
''
,
});
options
.
push
({
key
:
'passkey.origins'
,
value
:
JSON
.
stringify
(
Array
.
isArray
(
passkeyOrigins
)
?
passkeyOrigins
:
[])
,
value
:
formValues
[
'passkey.origins'
]
||
inputs
[
'passkey.origins'
]
||
''
,
});
if
(
options
.
length
>
0
)
{
await
updateOptions
(
options
);
}
await
updateOptions
(
options
);
};
const
handleCheckboxChange
=
async
(
optionKey
,
event
)
=>
{
...
...
@@ -1037,7 +1024,7 @@ const SystemSetting = () => {
>
<
Col
xs=
{
24
}
sm=
{
24
}
md=
{
24
}
lg=
{
24
}
xl=
{
24
}
>
<
Form
.
Checkbox
field=
'passkey.enabled'
field=
"['passkey.enabled']"
noLabel
onChange=
{
(
e
)
=>
handleCheckboxChange
(
'passkey.enabled'
,
e
)
...
...
@@ -1052,7 +1039,7 @@ const SystemSetting = () => {
>
<
Col
xs=
{
24
}
sm=
{
24
}
md=
{
12
}
lg=
{
12
}
xl=
{
12
}
>
<
Form
.
Input
field=
'passkey.rp_display_name'
field=
"['passkey.rp_display_name']"
label=
{
t
(
'服务显示名称'
)
}
placeholder=
{
t
(
'默认使用系统名称'
)
}
extraText=
{
t
(
'用户注册时看到的网站名称,比如\'我的网站\''
)
}
...
...
@@ -1060,7 +1047,7 @@ const SystemSetting = () => {
</
Col
>
<
Col
xs=
{
24
}
sm=
{
24
}
md=
{
12
}
lg=
{
12
}
xl=
{
12
}
>
<
Form
.
Input
field=
'passkey.rp_id'
field=
"['passkey.rp_id']"
label=
{
t
(
'网站域名标识'
)
}
placeholder=
{
t
(
'例如:example.com'
)
}
extraText=
{
t
(
'留空自动使用当前域名'
)
}
...
...
@@ -1073,7 +1060,7 @@ const SystemSetting = () => {
>
<
Col
xs=
{
24
}
sm=
{
24
}
md=
{
12
}
lg=
{
12
}
xl=
{
12
}
>
<
Form
.
Select
field=
'passkey.user_verification'
field=
"['passkey.user_verification']"
label=
{
t
(
'安全验证级别'
)
}
placeholder=
{
t
(
'是否要求指纹/面容等生物识别'
)
}
optionList=
{
[
...
...
@@ -1086,7 +1073,7 @@ const SystemSetting = () => {
</
Col
>
<
Col
xs=
{
24
}
sm=
{
24
}
md=
{
12
}
lg=
{
12
}
xl=
{
12
}
>
<
Form
.
Select
field=
'passkey.attachment_preference'
field=
"['passkey.attachment_preference']"
label=
{
t
(
'设备类型偏好'
)
}
placeholder=
{
t
(
'选择支持的认证设备类型'
)
}
optionList=
{
[
...
...
@@ -1104,7 +1091,7 @@ const SystemSetting = () => {
>
<
Col
xs=
{
24
}
sm=
{
24
}
md=
{
24
}
lg=
{
24
}
xl=
{
24
}
>
<
Form
.
Checkbox
field=
'passkey.allow_insecure_origin'
field=
"['passkey.allow_insecure_origin']"
noLabel
extraText=
{
t
(
'仅用于开发环境,生产环境应使用 HTTPS'
)
}
onChange=
{
(
e
)
=>
...
...
@@ -1120,21 +1107,11 @@ const SystemSetting = () => {
style=
{
{
marginTop
:
16
}
}
>
<
Col
xs=
{
24
}
sm=
{
24
}
md=
{
24
}
lg=
{
24
}
xl=
{
24
}
>
<
Text
strong
>
{
t
(
'允许的 Origins'
)
}
</
Text
>
<
Text
type=
"secondary"
style=
{
{
display
:
'block'
,
marginBottom
:
8
}
}
>
{
t
(
'留空将自动使用服务器地址,多个 Origin 用于支持多域名部署'
)
}
</
Text
>
<
TagInput
value=
{
passkeyOrigins
}
onChange=
{
(
value
)
=>
{
setPasskeyOrigins
(
value
);
setInputs
(
prev
=>
({
...
prev
,
'passkey.origins'
:
value
}));
}
}
placeholder=
{
t
(
'输入 Origin 后回车,如:https://example.com'
)
}
style=
{
{
width
:
'100%'
}
}
<
Form
.
Input
field=
"['passkey.origins']"
label=
{
t
(
'允许的 Origins'
)
}
placeholder=
{
t
(
'填写带https的域名,逗号分隔'
)
}
extraText=
{
t
(
'空的话则不限制 Origin,多个 Origin 用逗号分隔'
)
}
/>
</
Col
>
</
Row
>
...
...
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