fixed passkey
This commit is contained in:
parent
df52f94868
commit
724fbf52c9
@ -112,8 +112,17 @@ func authAllowedRPOrigins() []string {
|
||||
out = append(out, origin)
|
||||
}
|
||||
|
||||
for _, origin := range configuredPublicAppOrigins() {
|
||||
addOrigin(origin)
|
||||
}
|
||||
|
||||
scheme := "https"
|
||||
if !httpsEnabled() {
|
||||
scheme = "http"
|
||||
}
|
||||
|
||||
for _, host := range localAppHostCandidates() {
|
||||
addOrigin(originForHost("https", host, appPort()))
|
||||
addOrigin(originForHost(scheme, host, appPort()))
|
||||
}
|
||||
|
||||
// Explicit env values remain supported, but they extend the dynamic list
|
||||
@ -125,6 +134,33 @@ func authAllowedRPOrigins() []string {
|
||||
return out
|
||||
}
|
||||
|
||||
func configuredPublicAppOrigins() []string {
|
||||
keys := []string{
|
||||
"APP_PUBLIC_URLS",
|
||||
"APP_PUBLIC_URL",
|
||||
"PUBLIC_APP_URLS",
|
||||
"PUBLIC_APP_URL",
|
||||
"NSFWAPP_URL",
|
||||
}
|
||||
|
||||
out := make([]string, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
raw := strings.TrimSpace(os.Getenv(key))
|
||||
if raw == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, part := range strings.Split(raw, ",") {
|
||||
part = strings.TrimSpace(part)
|
||||
if part != "" {
|
||||
out = append(out, part)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func configuredAuthOrigins() []string {
|
||||
raw := strings.TrimSpace(os.Getenv("AUTH_RP_ORIGINS"))
|
||||
|
||||
@ -381,6 +417,57 @@ func originIsAllowed(origin string, allowed []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func requestHostOriginAllowed(origin string, r *http.Request) bool {
|
||||
normalizedOrigin, ok := canonicalOrigin(origin)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
u, err := url.Parse(normalizedOrigin)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
originHost := normalizeOriginHost(u.Hostname())
|
||||
requestHost, requestPort := splitRequestHostPort(r.Host)
|
||||
if originHost == "" || requestHost == "" || originHost != requestHost {
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.TrimSpace(u.Port()) != requestPort {
|
||||
return false
|
||||
}
|
||||
|
||||
if u.Scheme == "https" {
|
||||
return true
|
||||
}
|
||||
|
||||
return u.Scheme == "http" && isLocalWebAuthnHost(originHost)
|
||||
}
|
||||
|
||||
func splitRequestHostPort(host string) (string, string) {
|
||||
host = strings.TrimSpace(host)
|
||||
if host == "" {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
if h, p, err := net.SplitHostPort(host); err == nil {
|
||||
return normalizeOriginHost(h), strings.TrimSpace(p)
|
||||
}
|
||||
|
||||
return normalizeOriginHost(host), ""
|
||||
}
|
||||
|
||||
func isLocalWebAuthnHost(host string) bool {
|
||||
host = normalizeOriginHost(host)
|
||||
if host == "localhost" {
|
||||
return true
|
||||
}
|
||||
|
||||
ip := net.ParseIP(host)
|
||||
return ip != nil && ip.IsLoopback()
|
||||
}
|
||||
|
||||
func (am *AuthManager) webAuthnForRequest(r *http.Request) (*webauthn.WebAuthn, error) {
|
||||
origin := originFromRequest(r)
|
||||
if origin == "" {
|
||||
@ -388,7 +475,7 @@ func (am *AuthManager) webAuthnForRequest(r *http.Request) (*webauthn.WebAuthn,
|
||||
}
|
||||
|
||||
allowedOrigins := authAllowedRPOrigins()
|
||||
if !originIsAllowed(origin, allowedOrigins) {
|
||||
if !originIsAllowed(origin, allowedOrigins) && !requestHostOriginAllowed(origin, r) {
|
||||
return nil, errors.New("origin not allowed for WebAuthn: " + origin)
|
||||
}
|
||||
|
||||
|
||||
32
backend/auth_passkey_test.go
Normal file
32
backend/auth_passkey_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRequestHostOriginAllowedAcceptsHTTPSFQDN(t *testing.T) {
|
||||
r := httptest.NewRequest(http.MethodPost, "https://l14pbbk95100006.tegdssd.de:9999/api/auth/passkey/login/options", nil)
|
||||
|
||||
if !requestHostOriginAllowed("https://l14pbbk95100006.tegdssd.de:9999", r) {
|
||||
t.Fatal("expected matching HTTPS request host origin to be allowed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestHostOriginAllowedRejectsDifferentHost(t *testing.T) {
|
||||
r := httptest.NewRequest(http.MethodPost, "https://l14pbbk95100006.tegdssd.de:9999/api/auth/passkey/login/options", nil)
|
||||
|
||||
if requestHostOriginAllowed("https://evil.example:9999", r) {
|
||||
t.Fatal("expected different origin host to be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthAllowedRPOriginsIncludesConfiguredPublicURL(t *testing.T) {
|
||||
t.Setenv("APP_PUBLIC_URL", "https://l14pbbk95100006.tegdssd.de:9999")
|
||||
|
||||
origins := authAllowedRPOrigins()
|
||||
if !originIsAllowed("https://l14pbbk95100006.tegdssd.de:9999", origins) {
|
||||
t.Fatalf("expected configured public URL in allowed origins: %#v", origins)
|
||||
}
|
||||
}
|
||||
BIN
backend/dist/nsfwapp-linux-amd64
vendored
BIN
backend/dist/nsfwapp-linux-amd64
vendored
Binary file not shown.
BIN
backend/dist/nsfwapp.exe
vendored
BIN
backend/dist/nsfwapp.exe
vendored
Binary file not shown.
BIN
backend/dist/nsfwapp_amd64.deb
vendored
BIN
backend/dist/nsfwapp_amd64.deb
vendored
Binary file not shown.
@ -941,15 +941,23 @@ func publicAppURLs() []string {
|
||||
out := make([]string, 0, 8)
|
||||
seen := map[string]bool{}
|
||||
|
||||
for _, host := range localAppHostCandidates() {
|
||||
u := originForHost(scheme, host, port)
|
||||
if u == "" || seen[u] {
|
||||
continue
|
||||
addURL := func(raw string) {
|
||||
u, ok := canonicalOrigin(raw)
|
||||
if !ok || seen[u] {
|
||||
return
|
||||
}
|
||||
seen[u] = true
|
||||
out = append(out, u)
|
||||
}
|
||||
|
||||
for _, u := range configuredPublicAppOrigins() {
|
||||
addURL(u)
|
||||
}
|
||||
|
||||
for _, host := range localAppHostCandidates() {
|
||||
addURL(originForHost(scheme, host, port))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
|
||||
@ -115,7 +115,7 @@ export default function LoginPage({ onLoggedIn }: Props) {
|
||||
if (raw.includes('passkey registration verify failed')) {
|
||||
return (
|
||||
raw +
|
||||
'\n\nPrüfe AUTH_RP_ORIGIN und AUTH_RP_ID. Die Werte müssen exakt zur Browser-URL passen.'
|
||||
'\n\nPruefe, ob du den Passkey unter genau dieser Browser-URL registriert hast. Passkeys sind an Hostname und RP-ID gebunden.'
|
||||
)
|
||||
}
|
||||
|
||||
@ -135,7 +135,7 @@ export default function LoginPage({ onLoggedIn }: Props) {
|
||||
) {
|
||||
return (
|
||||
raw +
|
||||
'\n\nDas sieht nach einem Origin/RP-ID Problem aus. Prüfe AUTH_RP_ORIGIN und AUTH_RP_ID.'
|
||||
'\n\nDas sieht nach einem Origin/RP-ID Problem aus. Pruefe, ob Hostname, HTTPS und die Passkey-Registrierung zusammenpassen.'
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1891,7 +1891,7 @@ export default function RecorderSettings({ onAssetsGenerated }: Props) {
|
||||
if (raw.includes('passkey registration verify failed')) {
|
||||
return (
|
||||
raw +
|
||||
'\n\nPrüfe AUTH_RP_ORIGIN und AUTH_RP_ID. Die Werte müssen exakt zur Browser-URL passen.'
|
||||
'\n\nPruefe, ob du den Passkey unter genau dieser Browser-URL registriert hast. Passkeys sind an Hostname und RP-ID gebunden.'
|
||||
)
|
||||
}
|
||||
|
||||
@ -1911,7 +1911,7 @@ export default function RecorderSettings({ onAssetsGenerated }: Props) {
|
||||
) {
|
||||
return (
|
||||
raw +
|
||||
'\n\nDas sieht nach einem Origin/RP-ID Problem aus. Prüfe AUTH_RP_ORIGIN und AUTH_RP_ID.'
|
||||
'\n\nDas sieht nach einem Origin/RP-ID Problem aus. Pruefe, ob Hostname, HTTPS und die Passkey-Registrierung zusammenpassen.'
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user