feat: sync deployed v1.0.0-rc.38-custom.42
Sync production source including routing, billing, Astra detection and public uptime. Update custom build instructions and license packaging.
This commit is contained in:
1 parent
aa6c3f50f4
commit
ceddfd3d6b
828 files changed
+122253
-13702
No files matched your search
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -16,27 +17,45 @@ import (
|
||||
webauthn "github.com/go-webauthn/webauthn/webauthn"
|
||||
)
|
||||
|
||||
var ErrRPIDUnavailable = system_setting.ErrPasskeyRPIDUnavailable
|
||||
|
||||
// BuildWebAuthn constructs a WebAuthn instance using the current passkey settings and request context.
|
||||
func BuildWebAuthn(r *http.Request) (*webauthn.WebAuthn, error) {
|
||||
settings := system_setting.GetPasskeySettings()
|
||||
if settings == nil {
|
||||
return nil, errors.New("未找到 Passkey 设置")
|
||||
}
|
||||
return BuildWebAuthnForRPID(r, "")
|
||||
}
|
||||
|
||||
// BuildWebAuthnForRPID uses a single configured RP ID, never a list of IDs to
|
||||
// try against the same signed response. An empty ID is for new registrations.
|
||||
func BuildWebAuthnForRPID(r *http.Request, selectedRPID string) (*webauthn.WebAuthn, error) {
|
||||
settings := system_setting.PasskeySettingsSnapshot()
|
||||
|
||||
displayName := strings.TrimSpace(settings.RPDisplayName)
|
||||
if displayName == "" {
|
||||
displayName = common.SystemName
|
||||
}
|
||||
|
||||
origins, err := resolveOrigins(r, settings)
|
||||
origins, err := resolveOrigins(r, &settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rpID, err := resolveRPID(r, settings, origins)
|
||||
rpID, err := resolveRPID(r, &settings, origins)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if selectedRPID != "" && selectedRPID != rpID {
|
||||
if !slices.Contains(settings.RelyingPartyIDs(), selectedRPID) {
|
||||
return nil, ErrRPIDUnavailable
|
||||
}
|
||||
rpID = selectedRPID
|
||||
}
|
||||
origins = originsForRPID(origins, rpID)
|
||||
if len(origins) == 0 {
|
||||
return nil, ErrRPIDUnavailable
|
||||
}
|
||||
if origin := r.Header.Get("Origin"); origin != "" && !protocol.IsOriginInHaystack(origin, origins) {
|
||||
return nil, ErrRPIDUnavailable
|
||||
}
|
||||
|
||||
selection := protocol.AuthenticatorSelection{
|
||||
ResidentKey: protocol.ResidentKeyRequirementRequired,
|
||||
@@ -73,6 +92,71 @@ func BuildWebAuthn(r *http.Request) (*webauthn.WebAuthn, error) {
|
||||
return webauthn.New(config)
|
||||
}
|
||||
|
||||
// BuildLoginWebAuthn gives a known credential's binding precedence over a
|
||||
// browser's last-successful-domain hint. All choices remain server controlled.
|
||||
func BuildLoginWebAuthn(r *http.Request, hint, credentialRPID string) (*webauthn.WebAuthn, []string, error) {
|
||||
settings := system_setting.PasskeySettingsSnapshot()
|
||||
origins, err := resolveOrigins(r, &settings)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
primary, err := resolveRPID(r, &settings, origins)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
configured := append([]string{primary}, settings.RelyingPartyIDs()...)
|
||||
available := []string{}
|
||||
for _, id := range configured {
|
||||
if credentialRPID != "" && id != credentialRPID {
|
||||
continue
|
||||
}
|
||||
allowedOrigins := originsForRPID(origins, id)
|
||||
if len(allowedOrigins) == 0 || (r.Header.Get("Origin") != "" && !protocol.IsOriginInHaystack(r.Header.Get("Origin"), allowedOrigins)) {
|
||||
continue
|
||||
}
|
||||
if !slices.Contains(available, id) {
|
||||
available = append(available, id)
|
||||
}
|
||||
}
|
||||
if len(available) == 0 {
|
||||
return nil, nil, ErrRPIDUnavailable
|
||||
}
|
||||
selected := credentialRPID
|
||||
if selected == "" {
|
||||
selected = hint
|
||||
}
|
||||
if selected == "" {
|
||||
selected = available[0]
|
||||
}
|
||||
if !slices.Contains(available, selected) {
|
||||
return nil, nil, ErrRPIDUnavailable
|
||||
}
|
||||
wa, err := BuildWebAuthnForRPID(r, selected)
|
||||
return wa, available, err
|
||||
}
|
||||
|
||||
func originsForRPID(origins []string, rpID string) []string {
|
||||
allowed := []string{}
|
||||
for _, origin := range origins {
|
||||
parsed, err := url.Parse(origin)
|
||||
if err != nil || (parsed.Scheme != "https" && parsed.Scheme != "http") || parsed.User != nil {
|
||||
continue
|
||||
}
|
||||
host := strings.ToLower(parsed.Hostname())
|
||||
domain := strings.ToLower(rpID)
|
||||
if !strings.Contains(domain, ".") && domain != "localhost" {
|
||||
if host == domain && parsed.Scheme == "https" {
|
||||
allowed = append(allowed, origin)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if host == domain || strings.HasSuffix(host, "."+domain) {
|
||||
allowed = append(allowed, origin)
|
||||
}
|
||||
}
|
||||
return allowed
|
||||
}
|
||||
|
||||
func resolveOrigins(r *http.Request, settings *system_setting.PasskeySettings) ([]string, error) {
|
||||
originsStr := strings.TrimSpace(settings.Origins)
|
||||
if originsStr != "" {
|
||||
|
||||
@@ -72,6 +72,12 @@ func PopSessionDataFlow(token, purpose string, identity model.AuthSessionIdentit
|
||||
if err := common.UnmarshalJsonStr(flow.Payload, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
if payload.SessionData.RelyingPartyID == "" || payload.SessionData.Expires.IsZero() {
|
||||
return model.ErrAuthFlowInvalid
|
||||
}
|
||||
if !time.Now().Before(payload.SessionData.Expires) {
|
||||
return model.ErrAuthFlowExpired
|
||||
}
|
||||
if purpose == model.AuthFlowPurposePasskeyLogin {
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user