This commit is contained in:
+25
-6
@@ -12,8 +12,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -36,9 +38,14 @@ const (
|
||||
var accountNamePattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_.-]{2,63}$`)
|
||||
|
||||
type Config struct {
|
||||
RPID string
|
||||
RPDisplayName string
|
||||
Origin string
|
||||
RPID string
|
||||
RPDisplayName string
|
||||
Origin string
|
||||
// AllowDevelopmentPort permits an explicit non-default HTTPS port only
|
||||
// for localhost or a reserved .test relying-party ID. Production origins
|
||||
// remain portless, while local applications can terminate trusted HTTPS
|
||||
// without requiring a privileged listener.
|
||||
AllowDevelopmentPort bool
|
||||
EnrollmentLifetime time.Duration
|
||||
RegistrationTTL time.Duration
|
||||
LoginTTL time.Duration
|
||||
@@ -66,7 +73,7 @@ func New(repository Repository, authService *auth.Service, config Config) (*Serv
|
||||
if repository == nil || authService == nil {
|
||||
return nil, errors.New("authwebauthn: repository and auth service are required")
|
||||
}
|
||||
if err := validateOrigin(config.RPID, config.Origin); err != nil {
|
||||
if err := validateOrigin(config.RPID, config.Origin, config.AllowDevelopmentPort); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if strings.TrimSpace(config.RPDisplayName) == "" || len(config.RPDisplayName) > 80 {
|
||||
@@ -725,12 +732,24 @@ func passwordMigrationBinding(userID string) [32]byte {
|
||||
return BindingDigest([]byte("gamertan-web/password-to-passkey/v1\x00" + userID))
|
||||
}
|
||||
|
||||
func validateOrigin(rpID, rawOrigin string) error {
|
||||
func validateOrigin(rpID, rawOrigin string, allowDevelopmentPort bool) error {
|
||||
if strings.TrimSpace(rpID) == "" || strings.TrimSpace(rawOrigin) == "" {
|
||||
return errors.New("authwebauthn: relying-party ID and origin are required")
|
||||
}
|
||||
origin, err := url.Parse(rawOrigin)
|
||||
if err != nil || origin.Scheme != "https" || origin.Hostname() != rpID || origin.Port() != "" || origin.User != nil || origin.Path != "" || origin.RawQuery != "" || origin.Fragment != "" {
|
||||
if err != nil || origin.Scheme != "https" || origin.Hostname() != rpID || origin.User != nil || origin.Path != "" || origin.RawQuery != "" || origin.Fragment != "" {
|
||||
return errors.New("authwebauthn: origin must be the exact HTTPS relying-party origin")
|
||||
}
|
||||
port := origin.Port()
|
||||
if port == "" {
|
||||
if origin.Host != rpID {
|
||||
return errors.New("authwebauthn: origin must be the exact HTTPS relying-party origin")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
developmentRP := rpID == "localhost" || strings.HasSuffix(rpID, ".test")
|
||||
value, portErr := strconv.ParseUint(port, 10, 16)
|
||||
if !allowDevelopmentPort || !developmentRP || portErr != nil || value == 0 || value == 443 || strconv.FormatUint(value, 10) != port || origin.Host != net.JoinHostPort(rpID, port) {
|
||||
return errors.New("authwebauthn: origin must be the exact HTTPS relying-party origin")
|
||||
}
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user