diff --git a/internal/handlers/handler_register_webauthn.go b/internal/handlers/handler_register_webauthn.go index 3ee1e66bd..edbaf3fea 100644 --- a/internal/handlers/handler_register_webauthn.go +++ b/internal/handlers/handler_register_webauthn.go @@ -87,18 +87,20 @@ func WebauthnRegistrationPUT(ctx *middlewares.AutheliaCtx) { } var ( - opts *protocol.CredentialCreation + creation *protocol.CredentialCreation ) + opts := []webauthn.RegistrationOption{ + webauthn.WithExclusions(user.WebAuthnCredentialDescriptors()), + webauthn.WithExtensions(map[string]any{"credProps": true}), + webauthn.WithResidentKeyRequirement(protocol.ResidentKeyRequirementDiscouraged), + } + data := session.Webauthn{ - DisplayName: bodyJSON.Description, + Description: bodyJSON.Description, } - extensions := map[string]any{ - "credProps": true, - } - - if opts, data.SessionData, err = w.BeginRegistration(user, webauthn.WithExclusions(user.WebAuthnCredentialDescriptors()), webauthn.WithExtensions(extensions)); err != nil { + if creation, data.SessionData, err = w.BeginRegistration(user, opts...); err != nil { ctx.Logger.Errorf("Unable to create %s registration challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err) respondUnauthorized(ctx, messageUnableToRegisterSecurityKey) @@ -116,7 +118,7 @@ func WebauthnRegistrationPUT(ctx *middlewares.AutheliaCtx) { return } - if err = ctx.SetJSONBody(opts); err != nil { + if err = ctx.SetJSONBody(creation); err != nil { ctx.Logger.Errorf(logFmtErrWriteResponseBody, regulation.AuthTypeWebauthn, userSession.Username, err) respondUnauthorized(ctx, messageUnableToRegisterSecurityKey) @@ -197,34 +199,9 @@ func WebauthnRegistrationPOST(ctx *middlewares.AutheliaCtx) { return } - var discoverable bool + device := model.NewWebauthnDeviceFromCredential(w.Config.RPID, userSession.Username, userSession.Webauthn.Description, credential) - if value, ok := response.ClientExtensionResults["credProps"]; ok { - switch credprops := value.(type) { - case map[string]any: - ctx.Logger.Debug("Is type") - - var v any - - if v, ok = credprops["rk"]; ok { - ctx.Logger.Debug("found rk") - - if discoverable, ok = v.(bool); ok { - ctx.Logger.Debug("found rk bool") - } else { - ctx.Logger.Debugf("not found rk bool %T", v) - } - } else { - ctx.Logger.Debug("not found rk") - } - default: - ctx.Logger.Debugf("type is %T", credprops) - } - } - - device := model.NewWebauthnDeviceFromCredential(w.Config.RPID, userSession.Username, userSession.Webauthn.DisplayName, credential) - - device.Discoverable = discoverable + device.Discoverable = webauthnCredentialCreationIsDiscoverable(ctx, response) if err = ctx.Providers.StorageProvider.SaveWebauthnDevice(ctx, device); err != nil { ctx.Logger.Errorf("Unable to save %s device registration for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err) diff --git a/internal/handlers/handler_sign_webauthn.go b/internal/handlers/handler_sign_webauthn.go index 071333901..523e5a692 100644 --- a/internal/handlers/handler_sign_webauthn.go +++ b/internal/handlers/handler_sign_webauthn.go @@ -45,20 +45,19 @@ func WebauthnAssertionGET(ctx *middlewares.AutheliaCtx) { return } - var opts = []webauthn.LoginOption{ - webauthn.WithAllowedCredentials(user.WebAuthnCredentialDescriptors()), - } - extensions := map[string]any{} if user.HasFIDOU2F() { extensions["appid"] = w.Config.RPOrigins[0] } + var opts = []webauthn.LoginOption{ + webauthn.WithAllowedCredentials(user.WebAuthnCredentialDescriptors()), + } + if len(extensions) != 0 { opts = append(opts, webauthn.WithAssertionExtensions(extensions)) } - var assertion *protocol.CredentialAssertion data := session.Webauthn{} diff --git a/internal/handlers/webauthn.go b/internal/handlers/webauthn.go index f7c10c180..9aff664a4 100644 --- a/internal/handlers/webauthn.go +++ b/internal/handlers/webauthn.go @@ -70,3 +70,34 @@ func newWebauthn(ctx *middlewares.AutheliaCtx) (w *webauthn.WebAuthn, err error) return webauthn.New(config) } + +func webauthnCredentialCreationIsDiscoverable(ctx *middlewares.AutheliaCtx, response *protocol.ParsedCredentialCreationData) (discoverable bool) { + if value, ok := response.ClientExtensionResults["credProps"]; ok { + switch credentialProperties := value.(type) { + case map[string]any: + var v any + + if v, ok = credentialProperties["rk"]; ok { + if discoverable, ok = v.(bool); ok { + ctx.Logger.WithFields(map[string]any{"discoverable": discoverable}).Trace("Determined Credential Discoverability via Client Extension Results") + + return discoverable + } else { + ctx.Logger.WithFields(map[string]any{"discoverable": false}).Trace("Assuming Credential Discoverability is false as the 'rk' field for the 'credProps' extension in the Client Extension Results was not a boolean") + } + } else { + ctx.Logger.WithFields(map[string]any{"discoverable": false}).Trace("Assuming Credential Discoverability is false as the 'rk' field for the 'credProps' extension was missing from the Client Extension Results") + } + + return false + default: + ctx.Logger.WithFields(map[string]any{"discoverable": false}).Trace("Assuming Credential Discoverability is false as the 'credProps' extension in the Client Extension Results does not appear to be a dictionary") + + return false + } + } + + ctx.Logger.WithFields(map[string]any{"discoverable": false}).Trace("Assuming Credential Discoverability is false as the 'credProps' extension is missing from the Client Extension Results") + + return false +} diff --git a/internal/session/types.go b/internal/session/types.go index 697d80c6e..52491b02c 100644 --- a/internal/session/types.go +++ b/internal/session/types.go @@ -48,7 +48,7 @@ type UserSession struct { // Webauthn holds the standard webauthn session data plus some extra. type Webauthn struct { *webauthn.SessionData - DisplayName string + Description string } // Identity of the user who is being verified.