refactor: webauthn naming (#5243)

Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>
pull/5244/head
James Elliott 2023-04-15 02:04:42 +10:00 committed by GitHub
parent 37a49b21af
commit 2733fc040c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 395 additions and 396 deletions

View File

@ -29,9 +29,9 @@ tags:
- name: User Information
description: User configuration endpoints
{{- end }}
{{- if (or .TOTP .Webauthn .Duo) }}
{{- if (or .TOTP .WebAuthn .Duo) }}
- name: Second Factor
description: TOTP, Webauthn and Duo endpoints
description: TOTP, WebAuthn and Duo endpoints
externalDocs:
url: https://www.authelia.com/configuration/second-factor/introduction/
{{- end }}
@ -721,13 +721,13 @@ paths:
security:
- authelia_auth: []
{{- end }}
{{- if .Webauthn }}
{{- if .WebAuthn }}
/api/secondfactor/webauthn/assertion:
get:
tags:
- Second Factor
summary: Second Factor Authentication - Webauthn (Request)
description: This endpoint starts the second factor authentication process with the FIDO2 Webauthn credential.
summary: Second Factor Authentication - WebAuthn (Request)
description: This endpoint starts the second factor authentication process with the FIDO2 WebAuthn credential.
responses:
"200":
description: Successful Operation
@ -742,8 +742,8 @@ paths:
post:
tags:
- Second Factor
summary: Second Factor Authentication - Webauthn
description: This endpoint completes the second factor authentication process with the FIDO2 Webauthn credential.
summary: Second Factor Authentication - WebAuthn
description: This endpoint completes the second factor authentication process with the FIDO2 WebAuthn credential.
requestBody:
required: true
content:
@ -765,9 +765,9 @@ paths:
post:
tags:
- Second Factor
summary: Identity Verification Webauthn Credential Creation
summary: Identity Verification WebAuthn Credential Creation
description: >
This endpoint performs identity verification to begin the FIDO2 Webauthn credential attestation process
This endpoint performs identity verification to begin the FIDO2 WebAuthn credential attestation process
(registration).
The session generated from this endpoint must be utilised for the subsequent steps in the
@ -785,9 +785,9 @@ paths:
post:
tags:
- Second Factor
summary: Identity Verification FIDO2 Webauthn Credential Validation
summary: Identity Verification FIDO2 WebAuthn Credential Validation
description: >
This endpoint performs identity and token verification, upon success generates a FIDO2 Webauthn device
This endpoint performs identity and token verification, upon success generates a FIDO2 WebAuthn device
attestation challenge (registration).
The session cookie generated from the `/api/secondfactor/webauthn/identity/start` endpoint must be utilised
@ -811,8 +811,8 @@ paths:
post:
tags:
- Second Factor
summary: Webauthn Credential Attestation
description: This endpoint performs Webauthn credential attestation (registration).
summary: WebAuthn Credential Attestation
description: This endpoint performs WebAuthn credential attestation (registration).
requestBody:
required: true
content:
@ -1878,7 +1878,7 @@ components:
type: string
example: 'otpauth://totp/{{ .Domain | default "example.com" }}:john?algorithm=SHA1&digits=6&issuer=auth.{{ .Domain | default "example.com" }}&period=30&secret=5ZH7Y5CTFWOXN7EOLGBMMXADRNQFHVUDZSYKCN5HMFAIRSLAWY3Q'
{{- end }}
{{- if .Webauthn }}
{{- if .WebAuthn }}
webauthn.PublicKeyCredential:
type: object
properties:

View File

@ -214,13 +214,13 @@ totp:
##
## Parameters used for WebAuthn.
webauthn:
## Disable Webauthn.
## Disable WebAuthn.
disable: false
## Adjust the interaction timeout for Webauthn dialogues.
## Adjust the interaction timeout for WebAuthn dialogues.
timeout: 60s
## The display name the browser should show the user for when using Webauthn to login/register.
## The display name the browser should show the user for when using WebAuthn to login/register.
display_name: Authelia
## Conveyance preference controls if we collect the attestation statement including the AAGUID from the device.
@ -1167,7 +1167,7 @@ regulation:
##
## Notification Provider
##
## Notifications are sent to users when they require a password reset, a Webauthn registration or a TOTP registration.
## Notifications are sent to users when they require a password reset, a WebAuthn registration or a TOTP registration.
## The available providers are: filesystem, smtp. You must use only one of these providers.
notifier:
## You can disable the notifier startup check by setting this to true.

View File

@ -451,7 +451,7 @@ func (ctx *CmdCtx) StorageUserWebAuthnExportRunE(cmd *cobra.Command, args []stri
}
for page := 0; true; page++ {
if devices, err = ctx.providers.StorageProvider.LoadWebauthnDevices(ctx, limit, page); err != nil {
if devices, err = ctx.providers.StorageProvider.LoadWebAuthnDevices(ctx, limit, page); err != nil {
return err
}
@ -522,7 +522,7 @@ func (ctx *CmdCtx) StorageUserWebAuthnImportRunE(cmd *cobra.Command, args []stri
}
for _, device := range export.WebAuthnDevices {
if err = ctx.providers.StorageProvider.SaveWebauthnDevice(ctx, device); err != nil {
if err = ctx.providers.StorageProvider.SaveWebAuthnDevice(ctx, device); err != nil {
return err
}
}
@ -550,10 +550,10 @@ func (ctx *CmdCtx) StorageUserWebAuthnListRunE(cmd *cobra.Command, args []string
user := args[0]
devices, err = ctx.providers.StorageProvider.LoadWebauthnDevicesByUsername(ctx, user)
devices, err = ctx.providers.StorageProvider.LoadWebAuthnDevicesByUsername(ctx, user)
switch {
case len(devices) == 0 || (err != nil && errors.Is(err, storage.ErrNoWebauthnDevice)):
case len(devices) == 0 || (err != nil && errors.Is(err, storage.ErrNoWebAuthnDevice)):
return fmt.Errorf("user '%s' has no webauthn devices", user)
case err != nil:
return fmt.Errorf("can't list devices for user '%s': %w", user, err)
@ -586,7 +586,7 @@ func (ctx *CmdCtx) StorageUserWebAuthnListAllRunE(_ *cobra.Command, _ []string)
output := strings.Builder{}
for page := 0; true; page++ {
if devices, err = ctx.providers.StorageProvider.LoadWebauthnDevices(ctx, limit, page); err != nil {
if devices, err = ctx.providers.StorageProvider.LoadWebAuthnDevices(ctx, limit, page); err != nil {
return fmt.Errorf("failed to list devices: %w", err)
}
@ -629,13 +629,13 @@ func (ctx *CmdCtx) StorageUserWebAuthnDeleteRunE(cmd *cobra.Command, args []stri
}
if byKID {
if err = ctx.providers.StorageProvider.DeleteWebauthnDevice(ctx, kid); err != nil {
if err = ctx.providers.StorageProvider.DeleteWebAuthnDevice(ctx, kid); err != nil {
return fmt.Errorf("failed to delete webauthn device with kid '%s': %w", kid, err)
}
fmt.Printf("Successfully deleted WebAuthn device with key id '%s'\n", kid)
} else {
err = ctx.providers.StorageProvider.DeleteWebauthnDeviceByUsername(ctx, user, description)
err = ctx.providers.StorageProvider.DeleteWebAuthnDeviceByUsername(ctx, user, description)
if all {
if err != nil {

View File

@ -214,13 +214,13 @@ totp:
##
## Parameters used for WebAuthn.
webauthn:
## Disable Webauthn.
## Disable WebAuthn.
disable: false
## Adjust the interaction timeout for Webauthn dialogues.
## Adjust the interaction timeout for WebAuthn dialogues.
timeout: 60s
## The display name the browser should show the user for when using Webauthn to login/register.
## The display name the browser should show the user for when using WebAuthn to login/register.
display_name: Authelia
## Conveyance preference controls if we collect the attestation statement including the AAGUID from the device.
@ -1167,7 +1167,7 @@ regulation:
##
## Notification Provider
##
## Notifications are sent to users when they require a password reset, a Webauthn registration or a TOTP registration.
## Notifications are sent to users when they require a password reset, a WebAuthn registration or a TOTP registration.
## The available providers are: filesystem, smtp. You must use only one of these providers.
notifier:
## You can disable the notifier startup check by setting this to true.

View File

@ -21,7 +21,7 @@ type Configuration struct {
Notifier NotifierConfiguration `koanf:"notifier"`
Server ServerConfiguration `koanf:"server"`
Telemetry TelemetryConfig `koanf:"telemetry"`
Webauthn WebauthnConfiguration `koanf:"webauthn"`
WebAuthn WebAuthnConfiguration `koanf:"webauthn"`
PasswordPolicy PasswordPolicyConfiguration `koanf:"password_policy"`
PrivacyPolicy PrivacyPolicy `koanf:"privacy_policy"`
}

View File

@ -6,8 +6,8 @@ import (
"github.com/go-webauthn/webauthn/protocol"
)
// WebauthnConfiguration represents the webauthn config.
type WebauthnConfiguration struct {
// WebAuthnConfiguration represents the webauthn config.
type WebAuthnConfiguration struct {
Disable bool `koanf:"disable"`
DisplayName string `koanf:"display_name"`
@ -17,8 +17,8 @@ type WebauthnConfiguration struct {
Timeout time.Duration `koanf:"timeout"`
}
// DefaultWebauthnConfiguration describes the default values for the WebauthnConfiguration.
var DefaultWebauthnConfiguration = WebauthnConfiguration{
// DefaultWebAuthnConfiguration describes the default values for the WebAuthnConfiguration.
var DefaultWebAuthnConfiguration = WebAuthnConfiguration{
DisplayName: "Authelia",
Timeout: time.Second * 60,

View File

@ -43,7 +43,7 @@ func ValidateConfiguration(config *schema.Configuration, validator *schema.Struc
ValidateTOTP(config, validator)
ValidateWebauthn(config, validator)
ValidateWebAuthn(config, validator)
ValidateAuthenticationBackend(&config.AuthenticationBackend, validator)
@ -89,7 +89,7 @@ func validateDefault2FAMethod(config *schema.Configuration, validator *schema.St
enabledMethods = append(enabledMethods, "totp")
}
if !config.Webauthn.Disable {
if !config.WebAuthn.Disable {
enabledMethods = append(enabledMethods, "webauthn")
}

View File

@ -188,7 +188,7 @@ func TestValidateDefault2FAMethod(t *testing.T) {
},
},
{
desc: "ShouldAllowConfiguredMethodWebauthn",
desc: "ShouldAllowConfiguredMethodWebAuthn",
have: &schema.Configuration{
Default2FAMethod: "webauthn",
DuoAPI: schema.DuoAPIConfiguration{
@ -225,7 +225,7 @@ func TestValidateDefault2FAMethod(t *testing.T) {
},
},
{
desc: "ShouldNotAllowDisabledMethodWebauthn",
desc: "ShouldNotAllowDisabledMethodWebAuthn",
have: &schema.Configuration{
Default2FAMethod: "webauthn",
DuoAPI: schema.DuoAPIConfiguration{
@ -233,7 +233,7 @@ func TestValidateDefault2FAMethod(t *testing.T) {
IntegrationKey: "another key",
Hostname: "none",
},
Webauthn: schema.WebauthnConfiguration{Disable: true},
WebAuthn: schema.WebAuthnConfiguration{Disable: true},
},
expectedErrs: []string{
"option 'default_2fa_method' must be one of the enabled options 'totp' or 'mobile_push' but it's configured as 'webauthn'",

View File

@ -197,10 +197,10 @@ const (
"configured to an unsafe value, it should be above 8 but it's configured to %d"
)
// Webauthn Error constants.
// WebAuthn Error constants.
const (
errFmtWebauthnConveyancePreference = "webauthn: option 'attestation_conveyance_preference' must be one of %s but it's configured as '%s'"
errFmtWebauthnUserVerification = "webauthn: option 'user_verification' must be one of %s but it's configured as '%s'"
errFmtWebAuthnConveyancePreference = "webauthn: option 'attestation_conveyance_preference' must be one of %s but it's configured as '%s'"
errFmtWebAuthnUserVerification = "webauthn: option 'user_verification' must be one of %s but it's configured as '%s'"
)
// Access Control error constants.
@ -378,8 +378,8 @@ var (
validThemeNames = []string{"light", "dark", "grey", auto}
validSessionSameSiteValues = []string{"none", "lax", "strict"}
validLogLevels = []string{"trace", "debug", "info", "warn", "error"}
validWebauthnConveyancePreferences = []string{string(protocol.PreferNoAttestation), string(protocol.PreferIndirectAttestation), string(protocol.PreferDirectAttestation)}
validWebauthnUserVerificationRequirement = []string{string(protocol.VerificationDiscouraged), string(protocol.VerificationPreferred), string(protocol.VerificationRequired)}
validWebAuthnConveyancePreferences = []string{string(protocol.PreferNoAttestation), string(protocol.PreferIndirectAttestation), string(protocol.PreferDirectAttestation)}
validWebAuthnUserVerificationRequirement = []string{string(protocol.VerificationDiscouraged), string(protocol.VerificationPreferred), string(protocol.VerificationRequired)}
validRFC7231HTTPMethodVerbs = []string{"GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "TRACE", "CONNECT", "OPTIONS"}
validRFC4918HTTPMethodVerbs = []string{"COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "UNLOCK"}
)

View File

@ -7,27 +7,27 @@ import (
"github.com/authelia/authelia/v4/internal/utils"
)
// ValidateWebauthn validates and update Webauthn configuration.
func ValidateWebauthn(config *schema.Configuration, validator *schema.StructValidator) {
if config.Webauthn.DisplayName == "" {
config.Webauthn.DisplayName = schema.DefaultWebauthnConfiguration.DisplayName
// ValidateWebAuthn validates and update WebAuthn configuration.
func ValidateWebAuthn(config *schema.Configuration, validator *schema.StructValidator) {
if config.WebAuthn.DisplayName == "" {
config.WebAuthn.DisplayName = schema.DefaultWebAuthnConfiguration.DisplayName
}
if config.Webauthn.Timeout <= 0 {
config.Webauthn.Timeout = schema.DefaultWebauthnConfiguration.Timeout
if config.WebAuthn.Timeout <= 0 {
config.WebAuthn.Timeout = schema.DefaultWebAuthnConfiguration.Timeout
}
switch {
case config.Webauthn.ConveyancePreference == "":
config.Webauthn.ConveyancePreference = schema.DefaultWebauthnConfiguration.ConveyancePreference
case !utils.IsStringInSlice(string(config.Webauthn.ConveyancePreference), validWebauthnConveyancePreferences):
validator.Push(fmt.Errorf(errFmtWebauthnConveyancePreference, strJoinOr(validWebauthnConveyancePreferences), config.Webauthn.ConveyancePreference))
case config.WebAuthn.ConveyancePreference == "":
config.WebAuthn.ConveyancePreference = schema.DefaultWebAuthnConfiguration.ConveyancePreference
case !utils.IsStringInSlice(string(config.WebAuthn.ConveyancePreference), validWebAuthnConveyancePreferences):
validator.Push(fmt.Errorf(errFmtWebAuthnConveyancePreference, strJoinOr(validWebAuthnConveyancePreferences), config.WebAuthn.ConveyancePreference))
}
switch {
case config.Webauthn.UserVerification == "":
config.Webauthn.UserVerification = schema.DefaultWebauthnConfiguration.UserVerification
case !utils.IsStringInSlice(string(config.Webauthn.UserVerification), validWebauthnUserVerificationRequirement):
validator.Push(fmt.Errorf(errFmtWebauthnUserVerification, strJoinOr(validWebauthnConveyancePreferences), config.Webauthn.UserVerification))
case config.WebAuthn.UserVerification == "":
config.WebAuthn.UserVerification = schema.DefaultWebAuthnConfiguration.UserVerification
case !utils.IsStringInSlice(string(config.WebAuthn.UserVerification), validWebAuthnUserVerificationRequirement):
validator.Push(fmt.Errorf(errFmtWebAuthnUserVerification, strJoinOr(validWebAuthnConveyancePreferences), config.WebAuthn.UserVerification))
}
}

View File

@ -11,39 +11,39 @@ import (
"github.com/authelia/authelia/v4/internal/configuration/schema"
)
func TestWebauthnShouldSetDefaultValues(t *testing.T) {
func TestWebAuthnShouldSetDefaultValues(t *testing.T) {
validator := schema.NewStructValidator()
config := &schema.Configuration{
Webauthn: schema.WebauthnConfiguration{},
WebAuthn: schema.WebAuthnConfiguration{},
}
ValidateWebauthn(config, validator)
ValidateWebAuthn(config, validator)
require.Len(t, validator.Errors(), 0)
assert.Equal(t, schema.DefaultWebauthnConfiguration.DisplayName, config.Webauthn.DisplayName)
assert.Equal(t, schema.DefaultWebauthnConfiguration.Timeout, config.Webauthn.Timeout)
assert.Equal(t, schema.DefaultWebauthnConfiguration.ConveyancePreference, config.Webauthn.ConveyancePreference)
assert.Equal(t, schema.DefaultWebauthnConfiguration.UserVerification, config.Webauthn.UserVerification)
assert.Equal(t, schema.DefaultWebAuthnConfiguration.DisplayName, config.WebAuthn.DisplayName)
assert.Equal(t, schema.DefaultWebAuthnConfiguration.Timeout, config.WebAuthn.Timeout)
assert.Equal(t, schema.DefaultWebAuthnConfiguration.ConveyancePreference, config.WebAuthn.ConveyancePreference)
assert.Equal(t, schema.DefaultWebAuthnConfiguration.UserVerification, config.WebAuthn.UserVerification)
}
func TestWebauthnShouldSetDefaultTimeoutWhenNegative(t *testing.T) {
func TestWebAuthnShouldSetDefaultTimeoutWhenNegative(t *testing.T) {
validator := schema.NewStructValidator()
config := &schema.Configuration{
Webauthn: schema.WebauthnConfiguration{
WebAuthn: schema.WebAuthnConfiguration{
Timeout: -1,
},
}
ValidateWebauthn(config, validator)
ValidateWebAuthn(config, validator)
require.Len(t, validator.Errors(), 0)
assert.Equal(t, schema.DefaultWebauthnConfiguration.Timeout, config.Webauthn.Timeout)
assert.Equal(t, schema.DefaultWebAuthnConfiguration.Timeout, config.WebAuthn.Timeout)
}
func TestWebauthnShouldNotSetDefaultValuesWhenConfigured(t *testing.T) {
func TestWebAuthnShouldNotSetDefaultValuesWhenConfigured(t *testing.T) {
validator := schema.NewStructValidator()
config := &schema.Configuration{
Webauthn: schema.WebauthnConfiguration{
WebAuthn: schema.WebAuthnConfiguration{
DisplayName: "Test",
Timeout: time.Second * 50,
ConveyancePreference: protocol.PreferNoAttestation,
@ -51,37 +51,37 @@ func TestWebauthnShouldNotSetDefaultValuesWhenConfigured(t *testing.T) {
},
}
ValidateWebauthn(config, validator)
ValidateWebAuthn(config, validator)
require.Len(t, validator.Errors(), 0)
assert.Equal(t, "Test", config.Webauthn.DisplayName)
assert.Equal(t, time.Second*50, config.Webauthn.Timeout)
assert.Equal(t, protocol.PreferNoAttestation, config.Webauthn.ConveyancePreference)
assert.Equal(t, protocol.VerificationDiscouraged, config.Webauthn.UserVerification)
assert.Equal(t, "Test", config.WebAuthn.DisplayName)
assert.Equal(t, time.Second*50, config.WebAuthn.Timeout)
assert.Equal(t, protocol.PreferNoAttestation, config.WebAuthn.ConveyancePreference)
assert.Equal(t, protocol.VerificationDiscouraged, config.WebAuthn.UserVerification)
config.Webauthn.ConveyancePreference = protocol.PreferIndirectAttestation
config.Webauthn.UserVerification = protocol.VerificationPreferred
config.WebAuthn.ConveyancePreference = protocol.PreferIndirectAttestation
config.WebAuthn.UserVerification = protocol.VerificationPreferred
ValidateWebauthn(config, validator)
ValidateWebAuthn(config, validator)
require.Len(t, validator.Errors(), 0)
assert.Equal(t, protocol.PreferIndirectAttestation, config.Webauthn.ConveyancePreference)
assert.Equal(t, protocol.VerificationPreferred, config.Webauthn.UserVerification)
assert.Equal(t, protocol.PreferIndirectAttestation, config.WebAuthn.ConveyancePreference)
assert.Equal(t, protocol.VerificationPreferred, config.WebAuthn.UserVerification)
config.Webauthn.ConveyancePreference = protocol.PreferDirectAttestation
config.Webauthn.UserVerification = protocol.VerificationRequired
config.WebAuthn.ConveyancePreference = protocol.PreferDirectAttestation
config.WebAuthn.UserVerification = protocol.VerificationRequired
ValidateWebauthn(config, validator)
ValidateWebAuthn(config, validator)
require.Len(t, validator.Errors(), 0)
assert.Equal(t, protocol.PreferDirectAttestation, config.Webauthn.ConveyancePreference)
assert.Equal(t, protocol.VerificationRequired, config.Webauthn.UserVerification)
assert.Equal(t, protocol.PreferDirectAttestation, config.WebAuthn.ConveyancePreference)
assert.Equal(t, protocol.VerificationRequired, config.WebAuthn.UserVerification)
}
func TestWebauthnShouldRaiseErrorsOnInvalidOptions(t *testing.T) {
func TestWebAuthnShouldRaiseErrorsOnInvalidOptions(t *testing.T) {
validator := schema.NewStructValidator()
config := &schema.Configuration{
Webauthn: schema.WebauthnConfiguration{
WebAuthn: schema.WebAuthnConfiguration{
DisplayName: "Test",
Timeout: time.Second * 50,
ConveyancePreference: "no",
@ -89,7 +89,7 @@ func TestWebauthnShouldRaiseErrorsOnInvalidOptions(t *testing.T) {
},
}
ValidateWebauthn(config, validator)
ValidateWebAuthn(config, validator)
require.Len(t, validator.Errors(), 2)

View File

@ -8,8 +8,8 @@ const (
// ActionTOTPRegistration is the string representation of the action for which the token has been produced.
ActionTOTPRegistration = "RegisterTOTPDevice"
// ActionWebauthnRegistration is the string representation of the action for which the token has been produced.
ActionWebauthnRegistration = "RegisterWebauthnDevice"
// ActionWebAuthnRegistration is the string representation of the action for which the token has been produced.
ActionWebAuthnRegistration = "RegisterWebAuthnDevice"
// ActionResetPassword is the string representation of the action for which the token has been produced.
ActionResetPassword = "ResetPassword"

View File

@ -36,7 +36,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldHaveAllConfiguredMethods
TOTP: schema.TOTPConfiguration{
Disable: false,
},
Webauthn: schema.WebauthnConfiguration{
WebAuthn: schema.WebAuthnConfiguration{
Disable: false,
},
AccessControl: schema.AccessControlConfiguration{
@ -66,7 +66,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveTOTPFromAvailableM
TOTP: schema.TOTPConfiguration{
Disable: true,
},
Webauthn: schema.WebauthnConfiguration{
WebAuthn: schema.WebAuthnConfiguration{
Disable: false,
},
AccessControl: schema.AccessControlConfiguration{
@ -88,7 +88,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveTOTPFromAvailableM
})
}
func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveWebauthnFromAvailableMethodsWhenDisabled() {
func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveWebAuthnFromAvailableMethodsWhenDisabled() {
s.mock.Ctx.Configuration = schema.Configuration{
DuoAPI: schema.DuoAPIConfiguration{
Disable: false,
@ -96,7 +96,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveWebauthnFromAvaila
TOTP: schema.TOTPConfiguration{
Disable: false,
},
Webauthn: schema.WebauthnConfiguration{
WebAuthn: schema.WebAuthnConfiguration{
Disable: true,
},
AccessControl: schema.AccessControlConfiguration{
@ -126,7 +126,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveDuoFromAvailableMe
TOTP: schema.TOTPConfiguration{
Disable: false,
},
Webauthn: schema.WebauthnConfiguration{
WebAuthn: schema.WebAuthnConfiguration{
Disable: false,
},
AccessControl: schema.AccessControlConfiguration{
@ -156,7 +156,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenNoTw
TOTP: schema.TOTPConfiguration{
Disable: false,
},
Webauthn: schema.WebauthnConfiguration{
WebAuthn: schema.WebAuthnConfiguration{
Disable: false,
},
AccessControl: schema.AccessControlConfiguration{
@ -186,7 +186,7 @@ func (s *SecondFactorAvailableMethodsFixture) TestShouldRemoveAllMethodsWhenAllD
TOTP: schema.TOTPConfiguration{
Disable: true,
},
Webauthn: schema.WebauthnConfiguration{
WebAuthn: schema.WebAuthnConfiguration{
Disable: true,
},
AccessControl: schema.AccessControlConfiguration{

View File

@ -18,19 +18,19 @@ var WebauthnIdentityStart = middlewares.IdentityVerificationStart(middlewares.Id
MailTitle: "Register your key",
MailButtonContent: "Register",
TargetEndpoint: "/webauthn/register",
ActionClaim: ActionWebauthnRegistration,
ActionClaim: ActionWebAuthnRegistration,
IdentityRetrieverFunc: identityRetrieverFromSession,
}, nil)
// WebauthnIdentityFinish the handler for finishing the identity validation.
var WebauthnIdentityFinish = middlewares.IdentityVerificationFinish(
middlewares.IdentityVerificationFinishArgs{
ActionClaim: ActionWebauthnRegistration,
ActionClaim: ActionWebAuthnRegistration,
IsTokenUserValidFunc: isTokenUserValidFor2FARegistration,
}, SecondFactorWebauthnAttestationGET)
}, SecondFactorWebAuthnAttestationGET)
// SecondFactorWebauthnAttestationGET returns the attestation challenge from the server.
func SecondFactorWebauthnAttestationGET(ctx *middlewares.AutheliaCtx, _ string) {
// SecondFactorWebAuthnAttestationGET returns the attestation challenge from the server.
func SecondFactorWebAuthnAttestationGET(ctx *middlewares.AutheliaCtx, _ string) {
var (
w *webauthn.WebAuthn
user *model.WebAuthnUser
@ -39,15 +39,15 @@ func SecondFactorWebauthnAttestationGET(ctx *middlewares.AutheliaCtx, _ string)
)
if userSession, err = ctx.GetSession(); err != nil {
ctx.Logger.WithError(err).Errorf("Error occurred retrieving session for %s attestation challenge", regulation.AuthTypeWebauthn)
ctx.Logger.WithError(err).Errorf("Error occurred retrieving session for %s attestation challenge", regulation.AuthTypeWebAuthn)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
return
}
if w, err = newWebauthn(ctx); err != nil {
ctx.Logger.Errorf("Unable to create %s attestation challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
if w, err = newWebAuthn(ctx); err != nil {
ctx.Logger.Errorf("Unable to create %s attestation challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@ -55,7 +55,7 @@ func SecondFactorWebauthnAttestationGET(ctx *middlewares.AutheliaCtx, _ string)
}
if user, err = getWebAuthnUser(ctx, userSession); err != nil {
ctx.Logger.Errorf("Unable to load %s devices for assertion challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf("Unable to load %s devices for assertion challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -64,8 +64,8 @@ func SecondFactorWebauthnAttestationGET(ctx *middlewares.AutheliaCtx, _ string)
var credentialCreation *protocol.CredentialCreation
if credentialCreation, userSession.Webauthn, err = w.BeginRegistration(user); err != nil {
ctx.Logger.Errorf("Unable to create %s attestation challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
if credentialCreation, userSession.WebAuthn, err = w.BeginRegistration(user); err != nil {
ctx.Logger.Errorf("Unable to create %s attestation challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@ -73,7 +73,7 @@ func SecondFactorWebauthnAttestationGET(ctx *middlewares.AutheliaCtx, _ string)
}
if err = ctx.SaveSession(userSession); err != nil {
ctx.Logger.Errorf(logFmtErrSessionSave, "attestation challenge", regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf(logFmtErrSessionSave, "attestation challenge", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@ -81,7 +81,7 @@ func SecondFactorWebauthnAttestationGET(ctx *middlewares.AutheliaCtx, _ string)
}
if err = ctx.SetJSONBody(credentialCreation); err != nil {
ctx.Logger.Errorf(logFmtErrWriteResponseBody, regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf(logFmtErrWriteResponseBody, regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@ -89,8 +89,8 @@ func SecondFactorWebauthnAttestationGET(ctx *middlewares.AutheliaCtx, _ string)
}
}
// WebauthnAttestationPOST processes the attestation challenge response from the client.
func WebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
// WebAuthnAttestationPOST processes the attestation challenge response from the client.
func WebAuthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
var (
err error
w *webauthn.WebAuthn
@ -103,23 +103,23 @@ func WebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
)
if userSession, err = ctx.GetSession(); err != nil {
ctx.Logger.WithError(err).Errorf("Error occurred retrieving session for %s attestation response", regulation.AuthTypeWebauthn)
ctx.Logger.WithError(err).Errorf("Error occurred retrieving session for %s attestation response", regulation.AuthTypeWebAuthn)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
return
}
if userSession.Webauthn == nil {
ctx.Logger.Errorf("Webauthn session data is not present in order to handle attestation for user '%s'. This could indicate a user trying to POST to the wrong endpoint, or the session data is not present for the browser they used.", userSession.Username)
if userSession.WebAuthn == nil {
ctx.Logger.Errorf("WebAuthn session data is not present in order to handle attestation for user '%s'. This could indicate a user trying to POST to the wrong endpoint, or the session data is not present for the browser they used.", userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
return
}
if w, err = newWebauthn(ctx); err != nil {
ctx.Logger.Errorf("Unable to configure %s during assertion challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
if w, err = newWebAuthn(ctx); err != nil {
ctx.Logger.Errorf("Unable to configure %s during assertion challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@ -127,7 +127,7 @@ func WebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
}
if attestationResponse, err = protocol.ParseCredentialCreationResponseBody(bytes.NewReader(ctx.PostBody())); err != nil {
ctx.Logger.Errorf("Unable to parse %s assertionfor user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf("Unable to parse %s assertionfor user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -135,15 +135,15 @@ func WebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
}
if user, err = getWebAuthnUser(ctx, userSession); err != nil {
ctx.Logger.Errorf("Unable to load %s devices for assertion challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf("Unable to load %s devices for assertion challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
return
}
if credential, err = w.CreateCredential(user, *userSession.Webauthn, attestationResponse); err != nil {
ctx.Logger.Errorf("Unable to load %s devices for assertion challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
if credential, err = w.CreateCredential(user, *userSession.WebAuthn, attestationResponse); err != nil {
ctx.Logger.Errorf("Unable to load %s devices for assertion challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -152,21 +152,21 @@ func WebauthnAttestationPOST(ctx *middlewares.AutheliaCtx) {
device := model.NewWebAuthnDeviceFromCredential(w.Config.RPID, userSession.Username, "Primary", credential)
if err = ctx.Providers.StorageProvider.SaveWebauthnDevice(ctx, device); err != nil {
ctx.Logger.Errorf("Unable to load %s devices for assertion challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
if err = ctx.Providers.StorageProvider.SaveWebAuthnDevice(ctx, device); err != nil {
ctx.Logger.Errorf("Unable to load %s devices for assertion challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
return
}
userSession.Webauthn = nil
userSession.WebAuthn = nil
if err = ctx.SaveSession(userSession); err != nil {
ctx.Logger.Errorf(logFmtErrSessionSave, "removal of the attestation challenge", regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf(logFmtErrSessionSave, "removal of the attestation challenge", regulation.AuthTypeWebAuthn, userSession.Username, err)
}
ctx.ReplyOK()
ctx.SetStatusCode(fasthttp.StatusCreated)
ctxLogEvent(ctx, userSession.Username, "Second Factor Method Added", map[string]any{"Action": "Second Factor Method Added", "Category": "Webauthn Credential", "Device Name": "Primary"})
ctxLogEvent(ctx, userSession.Username, "Second Factor Method Added", map[string]any{"Action": "Second Factor Method Added", "Category": "WebAuthn Credential", "Device Name": "Primary"})
}

View File

@ -12,8 +12,8 @@ import (
"github.com/authelia/authelia/v4/internal/session"
)
// WebauthnAssertionGET handler starts the assertion ceremony.
func WebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
// WebAuthnAssertionGET handler starts the assertion ceremony.
func WebAuthnAssertionGET(ctx *middlewares.AutheliaCtx) {
var (
w *webauthn.WebAuthn
user *model.WebAuthnUser
@ -29,8 +29,8 @@ func WebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
return
}
if w, err = newWebauthn(ctx); err != nil {
ctx.Logger.Errorf("Unable to configure %s during assertion challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
if w, err = newWebAuthn(ctx); err != nil {
ctx.Logger.Errorf("Unable to configure %s during assertion challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -38,7 +38,7 @@ func WebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
}
if user, err = getWebAuthnUser(ctx, userSession); err != nil {
ctx.Logger.Errorf("Unable to create %s assertion challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf("Unable to create %s assertion challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -61,8 +61,8 @@ func WebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
var assertion *protocol.CredentialAssertion
if assertion, userSession.Webauthn, err = w.BeginLogin(user, opts...); err != nil {
ctx.Logger.Errorf("Unable to create %s assertion challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
if assertion, userSession.WebAuthn, err = w.BeginLogin(user, opts...); err != nil {
ctx.Logger.Errorf("Unable to create %s assertion challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -70,7 +70,7 @@ func WebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
}
if err = ctx.SaveSession(userSession); err != nil {
ctx.Logger.Errorf(logFmtErrSessionSave, "assertion challenge", regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf(logFmtErrSessionSave, "assertion challenge", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -78,7 +78,7 @@ func WebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
}
if err = ctx.SetJSONBody(assertion); err != nil {
ctx.Logger.Errorf(logFmtErrWriteResponseBody, regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf(logFmtErrWriteResponseBody, regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -86,21 +86,21 @@ func WebauthnAssertionGET(ctx *middlewares.AutheliaCtx) {
}
}
// WebauthnAssertionPOST handler completes the assertion ceremony after verifying the challenge.
// WebAuthnAssertionPOST handler completes the assertion ceremony after verifying the challenge.
//
//nolint:gocyclo
func WebauthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
var (
userSession session.UserSession
err error
w *webauthn.WebAuthn
bodyJSON bodySignWebauthnRequest
bodyJSON bodySignWebAuthnRequest
)
if err = ctx.ParseBody(&bodyJSON); err != nil {
ctx.Logger.Errorf(logFmtErrParseRequestBody, regulation.AuthTypeWebauthn, err)
ctx.Logger.Errorf(logFmtErrParseRequestBody, regulation.AuthTypeWebAuthn, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -115,16 +115,16 @@ func WebauthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
return
}
if userSession.Webauthn == nil {
ctx.Logger.Errorf("Webauthn session data is not present in order to handle assertion for user '%s'. This could indicate a user trying to POST to the wrong endpoint, or the session data is not present for the browser they used.", userSession.Username)
if userSession.WebAuthn == nil {
ctx.Logger.Errorf("WebAuthn session data is not present in order to handle assertion for user '%s'. This could indicate a user trying to POST to the wrong endpoint, or the session data is not present for the browser they used.", userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
return
}
if w, err = newWebauthn(ctx); err != nil {
ctx.Logger.Errorf("Unable to configure %s during assertion challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
if w, err = newWebAuthn(ctx); err != nil {
ctx.Logger.Errorf("Unable to configure %s during assertion challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -138,7 +138,7 @@ func WebauthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
)
if assertionResponse, err = protocol.ParseCredentialRequestResponseBody(bytes.NewReader(ctx.PostBody())); err != nil {
ctx.Logger.Errorf("Unable to parse %s assertionfor user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf("Unable to parse %s assertionfor user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -146,15 +146,15 @@ func WebauthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
}
if user, err = getWebAuthnUser(ctx, userSession); err != nil {
ctx.Logger.Errorf("Unable to load %s devices for assertion challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf("Unable to load %s devices for assertion challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
return
}
if credential, err = w.ValidateLogin(user, *userSession.Webauthn, assertionResponse); err != nil {
_ = markAuthenticationAttempt(ctx, false, nil, userSession.Username, regulation.AuthTypeWebauthn, err)
if credential, err = w.ValidateLogin(user, *userSession.WebAuthn, assertionResponse); err != nil {
_ = markAuthenticationAttempt(ctx, false, nil, userSession.Username, regulation.AuthTypeWebAuthn, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -169,8 +169,8 @@ func WebauthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
found = true
if err = ctx.Providers.StorageProvider.UpdateWebauthnDeviceSignIn(ctx, device.ID, device.RPID, device.LastUsedAt, device.SignCount, device.CloneWarning); err != nil {
ctx.Logger.Errorf("Unable to save %s device signin count for assertion challenge for user '%s': %+v", regulation.AuthTypeWebauthn, userSession.Username, err)
if err = ctx.Providers.StorageProvider.UpdateWebAuthnDeviceSignIn(ctx, device.ID, device.RPID, device.LastUsedAt, device.SignCount, device.CloneWarning); err != nil {
ctx.Logger.Errorf("Unable to save %s device signin count for assertion challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -182,7 +182,7 @@ func WebauthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
}
if !found {
ctx.Logger.Errorf("Unable to save %s device signin count for assertion challenge for user '%s' device '%x' count '%d': unable to find device", regulation.AuthTypeWebauthn, userSession.Username, credential.ID, credential.Authenticator.SignCount)
ctx.Logger.Errorf("Unable to save %s device signin count for assertion challenge for user '%s' device '%x' count '%d': unable to find device", regulation.AuthTypeWebAuthn, userSession.Username, credential.ID, credential.Authenticator.SignCount)
respondUnauthorized(ctx, messageMFAValidationFailed)
@ -190,25 +190,25 @@ func WebauthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
}
if err = ctx.RegenerateSession(); err != nil {
ctx.Logger.Errorf(logFmtErrSessionRegenerate, regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf(logFmtErrSessionRegenerate, regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)
return
}
if err = markAuthenticationAttempt(ctx, true, nil, userSession.Username, regulation.AuthTypeWebauthn, nil); err != nil {
if err = markAuthenticationAttempt(ctx, true, nil, userSession.Username, regulation.AuthTypeWebAuthn, nil); err != nil {
respondUnauthorized(ctx, messageMFAValidationFailed)
return
}
userSession.SetTwoFactorWebauthn(ctx.Clock.Now(),
userSession.SetTwoFactorWebAuthn(ctx.Clock.Now(),
assertionResponse.Response.AuthenticatorData.Flags.UserPresent(),
assertionResponse.Response.AuthenticatorData.Flags.UserVerified())
if err = ctx.SaveSession(userSession); err != nil {
ctx.Logger.Errorf(logFmtErrSessionSave, "removal of the assertion challenge and authentication time", regulation.AuthTypeWebauthn, userSession.Username, err)
ctx.Logger.Errorf(logFmtErrSessionSave, "removal of the assertion challenge and authentication time", regulation.AuthTypeWebAuthn, userSession.Username, err)
respondUnauthorized(ctx, messageMFAValidationFailed)

View File

@ -30,8 +30,8 @@ type bodySignTOTPRequest struct {
WorkflowID string `json:"workflowID"`
}
// bodySignWebauthnRequest is the model of the request body of WebAuthn 2FA authentication endpoint.
type bodySignWebauthnRequest struct {
// bodySignWebAuthnRequest is the model of the request body of WebAuthn 2FA authentication endpoint.
type bodySignWebAuthnRequest struct {
TargetURL string `json:"targetURL"`
Workflow string `json:"workflow"`
WorkflowID string `json:"workflowID"`

View File

@ -22,14 +22,14 @@ func getWebAuthnUser(ctx *middlewares.AutheliaCtx, userSession session.UserSessi
user.DisplayName = user.Username
}
if user.Devices, err = ctx.Providers.StorageProvider.LoadWebauthnDevicesByUsername(ctx, userSession.Username); err != nil {
if user.Devices, err = ctx.Providers.StorageProvider.LoadWebAuthnDevicesByUsername(ctx, userSession.Username); err != nil {
return nil, err
}
return user, nil
}
func newWebauthn(ctx *middlewares.AutheliaCtx) (w *webauthn.WebAuthn, err error) {
func newWebAuthn(ctx *middlewares.AutheliaCtx) (w *webauthn.WebAuthn, err error) {
var (
u *url.URL
)
@ -42,22 +42,22 @@ func newWebauthn(ctx *middlewares.AutheliaCtx) (w *webauthn.WebAuthn, err error)
origin := fmt.Sprintf("%s://%s", u.Scheme, u.Host)
config := &webauthn.Config{
RPDisplayName: ctx.Configuration.Webauthn.DisplayName,
RPDisplayName: ctx.Configuration.WebAuthn.DisplayName,
RPID: rpID,
RPOrigin: origin,
RPIcon: "",
AttestationPreference: ctx.Configuration.Webauthn.ConveyancePreference,
AttestationPreference: ctx.Configuration.WebAuthn.ConveyancePreference,
AuthenticatorSelection: protocol.AuthenticatorSelection{
AuthenticatorAttachment: protocol.CrossPlatform,
UserVerification: ctx.Configuration.Webauthn.UserVerification,
UserVerification: ctx.Configuration.WebAuthn.UserVerification,
RequireResidentKey: protocol.ResidentKeyNotRequired(),
},
Timeout: int(ctx.Configuration.Webauthn.Timeout.Milliseconds()),
Timeout: int(ctx.Configuration.WebAuthn.Timeout.Milliseconds()),
}
ctx.Logger.Tracef("Creating new Webauthn RP instance with ID %s and Origins %s", config.RPID, config.RPOrigin)
ctx.Logger.Tracef("Creating new WebAuthn RP instance with ID %s and Origins %s", config.RPID, config.RPOrigin)
return webauthn.New(config)
}

View File

@ -13,7 +13,7 @@ import (
"github.com/authelia/authelia/v4/internal/session"
)
func TestWebauthnGetUser(t *testing.T) {
func TestWebAuthnGetUser(t *testing.T) {
ctx := mocks.NewMockAutheliaCtx(t)
userSession := session.UserSession{
@ -21,7 +21,7 @@ func TestWebauthnGetUser(t *testing.T) {
DisplayName: "John Smith",
}
ctx.StorageMock.EXPECT().LoadWebauthnDevicesByUsername(ctx.Ctx, "john").Return([]model.WebAuthnDevice{
ctx.StorageMock.EXPECT().LoadWebAuthnDevicesByUsername(ctx.Ctx, "john").Return([]model.WebAuthnDevice{
{
ID: 1,
RPID: "https://example.com",
@ -99,14 +99,14 @@ func TestWebauthnGetUser(t *testing.T) {
assert.Equal(t, protocol.AuthenticatorTransport("nfc"), descriptors[1].Transport[1])
}
func TestWebauthnGetUserWithoutDisplayName(t *testing.T) {
func TestWebAuthnGetUserWithoutDisplayName(t *testing.T) {
ctx := mocks.NewMockAutheliaCtx(t)
userSession := session.UserSession{
Username: "john",
}
ctx.StorageMock.EXPECT().LoadWebauthnDevicesByUsername(ctx.Ctx, "john").Return([]model.WebAuthnDevice{
ctx.StorageMock.EXPECT().LoadWebAuthnDevicesByUsername(ctx.Ctx, "john").Return([]model.WebAuthnDevice{
{
ID: 1,
RPID: "https://example.com",
@ -129,14 +129,14 @@ func TestWebauthnGetUserWithoutDisplayName(t *testing.T) {
assert.Equal(t, "john", user.DisplayName)
}
func TestWebauthnGetUserWithErr(t *testing.T) {
func TestWebAuthnGetUserWithErr(t *testing.T) {
ctx := mocks.NewMockAutheliaCtx(t)
userSession := session.UserSession{
Username: "john",
}
ctx.StorageMock.EXPECT().LoadWebauthnDevicesByUsername(ctx.Ctx, "john").Return(nil, errors.New("not found"))
ctx.StorageMock.EXPECT().LoadWebAuthnDevicesByUsername(ctx.Ctx, "john").Return(nil, errors.New("not found"))
user, err := getWebAuthnUser(ctx.Ctx, userSession)
@ -144,24 +144,24 @@ func TestWebauthnGetUserWithErr(t *testing.T) {
assert.Nil(t, user)
}
func TestWebauthnNewWebauthnShouldReturnErrWhenHeadersNotAvailable(t *testing.T) {
func TestWebAuthnNewWebAuthnShouldReturnErrWhenHeadersNotAvailable(t *testing.T) {
ctx := mocks.NewMockAutheliaCtx(t)
ctx.Ctx.Request.Header.Del("X-Forwarded-Host")
w, err := newWebauthn(ctx.Ctx)
w, err := newWebAuthn(ctx.Ctx)
assert.Nil(t, w)
assert.EqualError(t, err, "missing required X-Forwarded-Host header")
}
func TestWebauthnNewWebauthnShouldReturnErrWhenWebauthnNotConfigured(t *testing.T) {
func TestWebAuthnNewWebAuthnShouldReturnErrWhenWebAuthnNotConfigured(t *testing.T) {
ctx := mocks.NewMockAutheliaCtx(t)
ctx.Ctx.Request.Header.Set("X-Forwarded-Host", "example.com")
ctx.Ctx.Request.Header.Set("X-Forwarded-URI", "/")
ctx.Ctx.Request.Header.Set("X-Forwarded-Proto", "https")
w, err := newWebauthn(ctx.Ctx)
w, err := newWebAuthn(ctx.Ctx)
assert.Nil(t, w)
assert.EqualError(t, err, "Configuration error: Missing RPDisplayName")

View File

@ -49,7 +49,7 @@ func (ctx *AutheliaCtx) AvailableSecondFactorMethods() (methods []string) {
methods = append(methods, model.SecondFactorMethodTOTP)
}
if !ctx.Configuration.Webauthn.Disable {
if !ctx.Configuration.WebAuthn.Disable {
methods = append(methods, model.SecondFactorMethodWebAuthn)
}

View File

@ -245,7 +245,7 @@ func TestShouldReturnCorrectSecondFactorMethods(t *testing.T) {
assert.Equal(t, []string{model.SecondFactorMethodWebAuthn, model.SecondFactorMethodDuo}, mock.Ctx.AvailableSecondFactorMethods())
mock.Ctx.Configuration.Webauthn.Disable = true
mock.Ctx.Configuration.WebAuthn.Disable = true
assert.Equal(t, []string{model.SecondFactorMethodDuo}, mock.Ctx.AvailableSecondFactorMethods())

View File

@ -39,7 +39,6 @@ func (m *MockStorage) EXPECT() *MockStorageMockRecorder {
return m.recorder
}
// AppendAuthenticationLog mocks base method.
func (m *MockStorage) AppendAuthenticationLog(arg0 context.Context, arg1 model.AuthenticationAttempt) error {
m.ctrl.T.Helper()
@ -167,32 +166,32 @@ func (mr *MockStorageMockRecorder) DeleteTOTPConfiguration(arg0, arg1 interface{
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTOTPConfiguration", reflect.TypeOf((*MockStorage)(nil).DeleteTOTPConfiguration), arg0, arg1)
}
// DeleteWebauthnDevice mocks base method.
func (m *MockStorage) DeleteWebauthnDevice(arg0 context.Context, arg1 string) error {
// DeleteWebAuthnDevice mocks base method.
func (m *MockStorage) DeleteWebAuthnDevice(arg0 context.Context, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteWebauthnDevice", arg0, arg1)
ret := m.ctrl.Call(m, "DeleteWebAuthnDevice", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteWebauthnDevice indicates an expected call of DeleteWebauthnDevice.
func (mr *MockStorageMockRecorder) DeleteWebauthnDevice(arg0, arg1 interface{}) *gomock.Call {
// DeleteWebAuthnDevice indicates an expected call of DeleteWebAuthnDevice.
func (mr *MockStorageMockRecorder) DeleteWebAuthnDevice(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWebauthnDevice", reflect.TypeOf((*MockStorage)(nil).DeleteWebauthnDevice), arg0, arg1)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWebAuthnDevice", reflect.TypeOf((*MockStorage)(nil).DeleteWebAuthnDevice), arg0, arg1)
}
// DeleteWebauthnDeviceByUsername mocks base method.
func (m *MockStorage) DeleteWebauthnDeviceByUsername(arg0 context.Context, arg1, arg2 string) error {
// DeleteWebAuthnDeviceByUsername mocks base method.
func (m *MockStorage) DeleteWebAuthnDeviceByUsername(arg0 context.Context, arg1, arg2 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteWebauthnDeviceByUsername", arg0, arg1, arg2)
ret := m.ctrl.Call(m, "DeleteWebAuthnDeviceByUsername", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteWebauthnDeviceByUsername indicates an expected call of DeleteWebauthnDeviceByUsername.
func (mr *MockStorageMockRecorder) DeleteWebauthnDeviceByUsername(arg0, arg1, arg2 interface{}) *gomock.Call {
// DeleteWebAuthnDeviceByUsername indicates an expected call of DeleteWebAuthnDeviceByUsername.
func (mr *MockStorageMockRecorder) DeleteWebAuthnDeviceByUsername(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWebauthnDeviceByUsername", reflect.TypeOf((*MockStorage)(nil).DeleteWebauthnDeviceByUsername), arg0, arg1, arg2)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWebAuthnDeviceByUsername", reflect.TypeOf((*MockStorage)(nil).DeleteWebAuthnDeviceByUsername), arg0, arg1, arg2)
}
// FindIdentityVerification mocks base method.
@ -420,34 +419,34 @@ func (mr *MockStorageMockRecorder) LoadUserOpaqueIdentifiers(arg0 interface{}) *
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadUserOpaqueIdentifiers", reflect.TypeOf((*MockStorage)(nil).LoadUserOpaqueIdentifiers), arg0)
}
// LoadWebauthnDevices mocks base method.
func (m *MockStorage) LoadWebauthnDevices(arg0 context.Context, arg1, arg2 int) ([]model.WebAuthnDevice, error) {
// LoadWebAuthnDevices mocks base method.
func (m *MockStorage) LoadWebAuthnDevices(arg0 context.Context, arg1, arg2 int) ([]model.WebAuthnDevice, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LoadWebauthnDevices", arg0, arg1, arg2)
ret := m.ctrl.Call(m, "LoadWebAuthnDevices", arg0, arg1, arg2)
ret0, _ := ret[0].([]model.WebAuthnDevice)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// LoadWebauthnDevices indicates an expected call of LoadWebauthnDevices.
func (mr *MockStorageMockRecorder) LoadWebauthnDevices(arg0, arg1, arg2 interface{}) *gomock.Call {
// LoadWebAuthnDevices indicates an expected call of LoadWebAuthnDevices.
func (mr *MockStorageMockRecorder) LoadWebAuthnDevices(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWebauthnDevices", reflect.TypeOf((*MockStorage)(nil).LoadWebauthnDevices), arg0, arg1, arg2)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWebAuthnDevices", reflect.TypeOf((*MockStorage)(nil).LoadWebAuthnDevices), arg0, arg1, arg2)
}
// LoadWebauthnDevicesByUsername mocks base method.
func (m *MockStorage) LoadWebauthnDevicesByUsername(arg0 context.Context, arg1 string) ([]model.WebAuthnDevice, error) {
// LoadWebAuthnDevicesByUsername mocks base method.
func (m *MockStorage) LoadWebAuthnDevicesByUsername(arg0 context.Context, arg1 string) ([]model.WebAuthnDevice, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LoadWebauthnDevicesByUsername", arg0, arg1)
ret := m.ctrl.Call(m, "LoadWebAuthnDevicesByUsername", arg0, arg1)
ret0, _ := ret[0].([]model.WebAuthnDevice)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// LoadWebauthnDevicesByUsername indicates an expected call of LoadWebauthnDevicesByUsername.
func (mr *MockStorageMockRecorder) LoadWebauthnDevicesByUsername(arg0, arg1 interface{}) *gomock.Call {
// LoadWebAuthnDevicesByUsername indicates an expected call of LoadWebAuthnDevicesByUsername.
func (mr *MockStorageMockRecorder) LoadWebAuthnDevicesByUsername(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWebauthnDevicesByUsername", reflect.TypeOf((*MockStorage)(nil).LoadWebauthnDevicesByUsername), arg0, arg1)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWebAuthnDevicesByUsername", reflect.TypeOf((*MockStorage)(nil).LoadWebAuthnDevicesByUsername), arg0, arg1)
}
// RevokeOAuth2PARContext mocks base method.
@ -689,18 +688,18 @@ func (mr *MockStorageMockRecorder) SaveUserOpaqueIdentifier(arg0, arg1 interface
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveUserOpaqueIdentifier", reflect.TypeOf((*MockStorage)(nil).SaveUserOpaqueIdentifier), arg0, arg1)
}
// SaveWebauthnDevice mocks base method.
func (m *MockStorage) SaveWebauthnDevice(arg0 context.Context, arg1 model.WebAuthnDevice) error {
// SaveWebAuthnDevice mocks base method.
func (m *MockStorage) SaveWebAuthnDevice(arg0 context.Context, arg1 model.WebAuthnDevice) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SaveWebauthnDevice", arg0, arg1)
ret := m.ctrl.Call(m, "SaveWebAuthnDevice", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// SaveWebauthnDevice indicates an expected call of SaveWebauthnDevice.
func (mr *MockStorageMockRecorder) SaveWebauthnDevice(arg0, arg1 interface{}) *gomock.Call {
// SaveWebAuthnDevice indicates an expected call of SaveWebAuthnDevice.
func (mr *MockStorageMockRecorder) SaveWebAuthnDevice(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveWebauthnDevice", reflect.TypeOf((*MockStorage)(nil).SaveWebauthnDevice), arg0, arg1)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveWebAuthnDevice", reflect.TypeOf((*MockStorage)(nil).SaveWebAuthnDevice), arg0, arg1)
}
// SchemaEncryptionChangeKey mocks base method.
@ -864,16 +863,16 @@ func (mr *MockStorageMockRecorder) UpdateTOTPConfigurationSignIn(arg0, arg1, arg
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTOTPConfigurationSignIn", reflect.TypeOf((*MockStorage)(nil).UpdateTOTPConfigurationSignIn), arg0, arg1, arg2)
}
// UpdateWebauthnDeviceSignIn mocks base method.
func (m *MockStorage) UpdateWebauthnDeviceSignIn(arg0 context.Context, arg1 int, arg2 string, arg3 sql.NullTime, arg4 uint32, arg5 bool) error {
// UpdateWebAuthnDeviceSignIn mocks base method.
func (m *MockStorage) UpdateWebAuthnDeviceSignIn(arg0 context.Context, arg1 int, arg2 string, arg3 sql.NullTime, arg4 uint32, arg5 bool) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateWebauthnDeviceSignIn", arg0, arg1, arg2, arg3, arg4, arg5)
ret := m.ctrl.Call(m, "UpdateWebAuthnDeviceSignIn", arg0, arg1, arg2, arg3, arg4, arg5)
ret0, _ := ret[0].(error)
return ret0
}
// UpdateWebauthnDeviceSignIn indicates an expected call of UpdateWebauthnDeviceSignIn.
func (mr *MockStorageMockRecorder) UpdateWebauthnDeviceSignIn(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
// UpdateWebAuthnDeviceSignIn indicates an expected call of UpdateWebAuthnDeviceSignIn.
func (mr *MockStorageMockRecorder) UpdateWebAuthnDeviceSignIn(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWebauthnDeviceSignIn", reflect.TypeOf((*MockStorage)(nil).UpdateWebauthnDeviceSignIn), arg0, arg1, arg2, arg3, arg4, arg5)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWebAuthnDeviceSignIn", reflect.TypeOf((*MockStorage)(nil).UpdateWebAuthnDeviceSignIn), arg0, arg1, arg2, arg3, arg4, arg5)
}

View File

@ -5,9 +5,9 @@ type AuthenticationMethodsReferences struct {
UsernameAndPassword bool
TOTP bool
Duo bool
Webauthn bool
WebauthnUserPresence bool
WebauthnUserVerified bool
WebAuthn bool
WebAuthnUserPresence bool
WebAuthnUserVerified bool
}
// FactorKnowledge returns true if a "something you know" factor of authentication was used.
@ -17,7 +17,7 @@ func (r AuthenticationMethodsReferences) FactorKnowledge() bool {
// FactorPossession returns true if a "something you have" factor of authentication was used.
func (r AuthenticationMethodsReferences) FactorPossession() bool {
return r.TOTP || r.Webauthn || r.Duo
return r.TOTP || r.WebAuthn || r.Duo
}
// MultiFactorAuthentication returns true if multiple factors were used.
@ -27,7 +27,7 @@ func (r AuthenticationMethodsReferences) MultiFactorAuthentication() bool {
// ChannelBrowser returns true if a browser was used to authenticate.
func (r AuthenticationMethodsReferences) ChannelBrowser() bool {
return r.UsernameAndPassword || r.TOTP || r.Webauthn
return r.UsernameAndPassword || r.TOTP || r.WebAuthn
}
// ChannelService returns true if a non-browser service was used to authenticate.
@ -57,15 +57,15 @@ func (r AuthenticationMethodsReferences) MarshalRFC8176() []string {
amr = append(amr, AMRShortMessageService)
}
if r.Webauthn {
if r.WebAuthn {
amr = append(amr, AMRHardwareSecuredKey)
}
if r.WebauthnUserPresence {
if r.WebAuthnUserPresence {
amr = append(amr, AMRUserPresence)
}
if r.WebauthnUserVerified {
if r.WebAuthnUserVerified {
amr = append(amr, AMRPersonalIdentificationNumber)
}

View File

@ -49,9 +49,9 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
},
},
{
desc: "Webauthn",
desc: "WebAuthn",
is: AuthenticationMethodsReferences{Webauthn: true},
is: AuthenticationMethodsReferences{WebAuthn: true},
want: testAMRWant{
FactorKnowledge: false,
FactorPossession: true,
@ -63,9 +63,9 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
},
},
{
desc: "Webauthn User Presence",
desc: "WebAuthn User Presence",
is: AuthenticationMethodsReferences{WebauthnUserPresence: true},
is: AuthenticationMethodsReferences{WebAuthnUserPresence: true},
want: testAMRWant{
FactorKnowledge: false,
FactorPossession: false,
@ -77,9 +77,9 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
},
},
{
desc: "Webauthn User Verified",
desc: "WebAuthn User Verified",
is: AuthenticationMethodsReferences{WebauthnUserVerified: true},
is: AuthenticationMethodsReferences{WebAuthnUserVerified: true},
want: testAMRWant{
FactorKnowledge: false,
FactorPossession: false,
@ -91,9 +91,9 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
},
},
{
desc: "Webauthn with User Presence and Verified",
desc: "WebAuthn with User Presence and Verified",
is: AuthenticationMethodsReferences{Webauthn: true, WebauthnUserVerified: true, WebauthnUserPresence: true},
is: AuthenticationMethodsReferences{WebAuthn: true, WebAuthnUserVerified: true, WebAuthnUserPresence: true},
want: testAMRWant{
FactorKnowledge: false,
FactorPossession: true,
@ -119,9 +119,9 @@ func TestAuthenticationMethodsReferences(t *testing.T) {
},
},
{
desc: "Duo Webauthn TOTP",
desc: "Duo WebAuthn TOTP",
is: AuthenticationMethodsReferences{Duo: true, Webauthn: true, TOTP: true},
is: AuthenticationMethodsReferences{Duo: true, WebAuthn: true, TOTP: true},
want: testAMRWant{
FactorKnowledge: false,
FactorPossession: true,

View File

@ -195,7 +195,7 @@ const (
// a user presence test. Evidence that the end user is present and interacting with the device. This is sometimes
// also referred to as "test of user presence" as per W3C.WD-webauthn-20170216.
//
// Authelia utilizes this when a user has used Webauthn to authenticate and the user presence flag was set.
// Authelia utilizes this when a user has used WebAuthn to authenticate and the user presence flag was set.
// Factor: Meta, Channel: Meta.
//
// RFC8176: https://datatracker.ietf.org/doc/html/rfc8176
@ -208,7 +208,7 @@ const (
// containing only numbers) that a user enters to unlock a key on the device. This mechanism should have a way to
// deter an attacker from obtaining the PIN by trying repeated guesses.
//
// Authelia utilizes this when a user has used Webauthn to authenticate and the user verified flag was set.
// Authelia utilizes this when a user has used WebAuthn to authenticate and the user verified flag was set.
// Factor: Meta, Channel: Meta.
//
// RFC8176: https://datatracker.ietf.org/doc/html/rfc8176
@ -244,7 +244,7 @@ const (
// AMRHardwareSecuredKey is an RFC8176 Authentication Method Reference Value that
// represents authentication via a proof-of-Possession (PoP) of a hardware-secured key.
//
// Authelia utilizes this when a user has used Webauthn to authenticate. Factor: Have, Channel: Browser.
// Authelia utilizes this when a user has used WebAuthn to authenticate. Factor: Have, Channel: Browser.
//
// RFC8176: https://datatracker.ietf.org/doc/html/rfc8176
AMRHardwareSecuredKey = "hwk"

View File

@ -12,8 +12,8 @@ const (
// AuthTypeTOTP is the string representing an auth log for second-factor authentication via TOTP.
AuthTypeTOTP = "TOTP"
// AuthTypeWebauthn is the string representing an auth log for second-factor authentication via FIDO2/CTAP2/WebAuthn.
AuthTypeWebauthn = "Webauthn"
// AuthTypeWebAuthn is the string representing an auth log for second-factor authentication via FIDO2/CTAP2/WebAuthn.
AuthTypeWebAuthn = "WebAuthn"
// AuthTypeDuo is the string representing an auth log for second-factor authentication via DUO.
AuthTypeDuo = "Duo"

View File

@ -255,14 +255,14 @@ func handleRouter(config *schema.Configuration, providers middlewares.Providers)
r.POST("/api/secondfactor/totp", middleware1FA(handlers.TimeBasedOneTimePasswordPOST))
}
if !config.Webauthn.Disable {
// Webauthn Endpoints.
if !config.WebAuthn.Disable {
// WebAuthn Endpoints.
r.POST("/api/secondfactor/webauthn/identity/start", middleware1FA(handlers.WebauthnIdentityStart))
r.POST("/api/secondfactor/webauthn/identity/finish", middleware1FA(handlers.WebauthnIdentityFinish))
r.POST("/api/secondfactor/webauthn/attestation", middleware1FA(handlers.WebauthnAttestationPOST))
r.POST("/api/secondfactor/webauthn/attestation", middleware1FA(handlers.WebAuthnAttestationPOST))
r.GET("/api/secondfactor/webauthn/assertion", middleware1FA(handlers.WebauthnAssertionGET))
r.POST("/api/secondfactor/webauthn/assertion", middleware1FA(handlers.WebauthnAssertionPOST))
r.GET("/api/secondfactor/webauthn/assertion", middleware1FA(handlers.WebAuthnAssertionGET))
r.POST("/api/secondfactor/webauthn/assertion", middleware1FA(handlers.WebAuthnAssertionPOST))
}
// Configure DUO api endpoint only if configuration exists.

View File

@ -272,7 +272,7 @@ func NewTemplatedFileOptions(config *schema.Configuration) (opts *TemplatedFileO
Theme: config.Theme,
EndpointsPasswordReset: !(config.AuthenticationBackend.PasswordReset.Disable || config.AuthenticationBackend.PasswordReset.CustomURL.String() != ""),
EndpointsWebauthn: !config.Webauthn.Disable,
EndpointsWebAuthn: !config.WebAuthn.Disable,
EndpointsTOTP: !config.TOTP.Disable,
EndpointsDuo: !config.DuoAPI.Disable,
EndpointsOpenIDConnect: !(config.IdentityProviders.OIDC == nil),
@ -304,7 +304,7 @@ type TemplatedFileOptions struct {
Theme string
EndpointsPasswordReset bool
EndpointsWebauthn bool
EndpointsWebAuthn bool
EndpointsTOTP bool
EndpointsDuo bool
EndpointsOpenIDConnect bool
@ -362,7 +362,7 @@ func (options *TemplatedFileOptions) OpenAPIData(base, baseURL, domain, nonce st
Session: options.Session,
PasswordReset: options.EndpointsPasswordReset,
Webauthn: options.EndpointsWebauthn,
WebAuthn: options.EndpointsWebAuthn,
TOTP: options.EndpointsTOTP,
Duo: options.EndpointsDuo,
OpenIDConnect: options.EndpointsOpenIDConnect,
@ -395,7 +395,7 @@ type TemplatedFileOpenAPIData struct {
CSPNonce string
Session string
PasswordReset bool
Webauthn bool
WebAuthn bool
TOTP bool
Duo bool
OpenIDConnect bool

View File

@ -179,7 +179,7 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
AuthenticationMethodRefs: oidc.AuthenticationMethodsReferences{UsernameAndPassword: true},
}, session)
session.SetTwoFactorWebauthn(timeTwoFactor, false, false)
session.SetTwoFactorWebAuthn(timeTwoFactor, false, false)
err = provider.SaveSession(ctx, session)
assert.NoError(t, err)
@ -187,7 +187,7 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
session, err = provider.GetSession(ctx)
assert.NoError(t, err)
assert.Equal(t, oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true}, session.AuthenticationMethodRefs)
assert.Equal(t, oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, WebAuthn: true}, session.AuthenticationMethodRefs)
assert.True(t, session.AuthenticationMethodRefs.MultiFactorAuthentication())
authAt, err = session.AuthenticatedTime(authorization.OneFactor)
@ -202,7 +202,7 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
assert.EqualError(t, err, "invalid authorization level")
assert.Equal(t, timeZeroFactor, authAt)
session.SetTwoFactorWebauthn(timeTwoFactor, false, false)
session.SetTwoFactorWebAuthn(timeTwoFactor, false, false)
err = provider.SaveSession(ctx, session)
assert.NoError(t, err)
@ -211,10 +211,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true},
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, WebAuthn: true},
session.AuthenticationMethodRefs)
session.SetTwoFactorWebauthn(timeTwoFactor, false, false)
session.SetTwoFactorWebAuthn(timeTwoFactor, false, false)
err = provider.SaveSession(ctx, session)
assert.NoError(t, err)
@ -223,10 +223,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true},
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, WebAuthn: true},
session.AuthenticationMethodRefs)
session.SetTwoFactorWebauthn(timeTwoFactor, true, false)
session.SetTwoFactorWebAuthn(timeTwoFactor, true, false)
err = provider.SaveSession(ctx, session)
assert.NoError(t, err)
@ -235,10 +235,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true, WebauthnUserPresence: true},
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, WebAuthn: true, WebAuthnUserPresence: true},
session.AuthenticationMethodRefs)
session.SetTwoFactorWebauthn(timeTwoFactor, true, false)
session.SetTwoFactorWebAuthn(timeTwoFactor, true, false)
err = provider.SaveSession(ctx, session)
assert.NoError(t, err)
@ -247,10 +247,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true, WebauthnUserPresence: true},
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, WebAuthn: true, WebAuthnUserPresence: true},
session.AuthenticationMethodRefs)
session.SetTwoFactorWebauthn(timeTwoFactor, false, true)
session.SetTwoFactorWebAuthn(timeTwoFactor, false, true)
err = provider.SaveSession(ctx, session)
assert.NoError(t, err)
@ -259,10 +259,10 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true, WebauthnUserVerified: true},
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, WebAuthn: true, WebAuthnUserVerified: true},
session.AuthenticationMethodRefs)
session.SetTwoFactorWebauthn(timeTwoFactor, false, true)
session.SetTwoFactorWebAuthn(timeTwoFactor, false, true)
err = provider.SaveSession(ctx, session)
assert.NoError(t, err)
@ -271,7 +271,7 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, Webauthn: true, WebauthnUserVerified: true},
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, WebAuthn: true, WebAuthnUserVerified: true},
session.AuthenticationMethodRefs)
session.SetTwoFactorTOTP(timeTwoFactor)
@ -283,7 +283,7 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, TOTP: true, Webauthn: true, WebauthnUserVerified: true},
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, TOTP: true, WebAuthn: true, WebAuthnUserVerified: true},
session.AuthenticationMethodRefs)
session.SetTwoFactorTOTP(timeTwoFactor)
@ -295,7 +295,7 @@ func TestShouldSetSessionAuthenticationLevelsAMR(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t,
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, TOTP: true, Webauthn: true, WebauthnUserVerified: true},
oidc.AuthenticationMethodsReferences{UsernameAndPassword: true, TOTP: true, WebAuthn: true, WebAuthnUserVerified: true},
session.AuthenticationMethodRefs)
}

View File

@ -35,8 +35,8 @@ type UserSession struct {
AuthenticationMethodRefs oidc.AuthenticationMethodsReferences
// Webauthn holds the session registration data for this session.
Webauthn *webauthn.SessionData
// WebAuthn holds the session registration data for this session.
WebAuthn *webauthn.SessionData
// This boolean is set to true after identity verification and checked
// while doing the query actually updating the password.

View File

@ -56,13 +56,13 @@ func (s *UserSession) SetTwoFactorDuo(now time.Time) {
s.AuthenticationMethodRefs.Duo = true
}
// SetTwoFactorWebauthn sets the relevant Webauthn AMR's and sets the factor to 2FA.
func (s *UserSession) SetTwoFactorWebauthn(now time.Time, userPresence, userVerified bool) {
// SetTwoFactorWebAuthn sets the relevant WebAuthn AMR's and sets the factor to 2FA.
func (s *UserSession) SetTwoFactorWebAuthn(now time.Time, userPresence, userVerified bool) {
s.setTwoFactor(now)
s.AuthenticationMethodRefs.Webauthn = true
s.AuthenticationMethodRefs.WebauthnUserPresence, s.AuthenticationMethodRefs.WebauthnUserVerified = userPresence, userVerified
s.AuthenticationMethodRefs.WebAuthn = true
s.AuthenticationMethodRefs.WebAuthnUserPresence, s.AuthenticationMethodRefs.WebAuthnUserVerified = userPresence, userVerified
s.Webauthn = nil
s.WebAuthn = nil
}
// AuthenticatedTime returns the unix timestamp this session authenticated successfully at the given level.

View File

@ -11,7 +11,7 @@ const (
tableTOTPConfigurations = "totp_configurations"
tableUserOpaqueIdentifier = "user_opaque_identifier"
tableUserPreferences = "user_preferences"
tableWebauthnDevices = "webauthn_devices"
tableWebAuthnDevices = "webauthn_devices"
tableOAuth2BlacklistedJTI = "oauth2_blacklisted_jti"
tableOAuth2ConsentSession = "oauth2_consent_session"

View File

@ -11,8 +11,8 @@ var (
// ErrNoTOTPConfiguration error thrown when no TOTP configuration has been found in DB.
ErrNoTOTPConfiguration = errors.New("no TOTP configuration for user")
// ErrNoWebauthnDevice error thrown when no Webauthn device handle has been found in DB.
ErrNoWebauthnDevice = errors.New("no Webauthn device found")
// ErrNoWebAuthnDevice error thrown when no WebAuthn device handle has been found in DB.
ErrNoWebAuthnDevice = errors.New("no WebAuthn device found")
// ErrNoDuoDevice error thrown when no Duo device and method has been found in DB.
ErrNoDuoDevice = errors.New("no Duo device and method saved")

View File

@ -38,12 +38,12 @@ type Provider interface {
LoadTOTPConfiguration(ctx context.Context, username string) (config *model.TOTPConfiguration, err error)
LoadTOTPConfigurations(ctx context.Context, limit, page int) (configs []model.TOTPConfiguration, err error)
SaveWebauthnDevice(ctx context.Context, device model.WebAuthnDevice) (err error)
UpdateWebauthnDeviceSignIn(ctx context.Context, id int, rpid string, lastUsedAt sql.NullTime, signCount uint32, cloneWarning bool) (err error)
DeleteWebauthnDevice(ctx context.Context, kid string) (err error)
DeleteWebauthnDeviceByUsername(ctx context.Context, username, description string) (err error)
LoadWebauthnDevices(ctx context.Context, limit, page int) (devices []model.WebAuthnDevice, err error)
LoadWebauthnDevicesByUsername(ctx context.Context, username string) (devices []model.WebAuthnDevice, err error)
SaveWebAuthnDevice(ctx context.Context, device model.WebAuthnDevice) (err error)
UpdateWebAuthnDeviceSignIn(ctx context.Context, id int, rpid string, lastUsedAt sql.NullTime, signCount uint32, cloneWarning bool) (err error)
DeleteWebAuthnDevice(ctx context.Context, kid string) (err error)
DeleteWebAuthnDeviceByUsername(ctx context.Context, username, description string) (err error)
LoadWebAuthnDevices(ctx context.Context, limit, page int) (devices []model.WebAuthnDevice, err error)
LoadWebAuthnDevicesByUsername(ctx context.Context, username string) (devices []model.WebAuthnDevice, err error)
SavePreferredDuoDevice(ctx context.Context, device model.DuoDevice) (err error)
DeletePreferredDuoDevice(ctx context.Context, username string) (err error)

View File

@ -46,16 +46,16 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
sqlUpdateTOTPConfigRecordSignIn: fmt.Sprintf(queryFmtUpdateTOTPConfigRecordSignIn, tableTOTPConfigurations),
sqlUpdateTOTPConfigRecordSignInByUsername: fmt.Sprintf(queryFmtUpdateTOTPConfigRecordSignInByUsername, tableTOTPConfigurations),
sqlUpsertWebauthnDevice: fmt.Sprintf(queryFmtUpsertWebauthnDevice, tableWebauthnDevices),
sqlSelectWebauthnDevices: fmt.Sprintf(queryFmtSelectWebauthnDevices, tableWebauthnDevices),
sqlSelectWebauthnDevicesByUsername: fmt.Sprintf(queryFmtSelectWebauthnDevicesByUsername, tableWebauthnDevices),
sqlUpsertWebAuthnDevice: fmt.Sprintf(queryFmtUpsertWebAuthnDevice, tableWebAuthnDevices),
sqlSelectWebAuthnDevices: fmt.Sprintf(queryFmtSelectWebAuthnDevices, tableWebAuthnDevices),
sqlSelectWebAuthnDevicesByUsername: fmt.Sprintf(queryFmtSelectWebAuthnDevicesByUsername, tableWebAuthnDevices),
sqlUpdateWebauthnDeviceRecordSignIn: fmt.Sprintf(queryFmtUpdateWebauthnDeviceRecordSignIn, tableWebauthnDevices),
sqlUpdateWebauthnDeviceRecordSignInByUsername: fmt.Sprintf(queryFmtUpdateWebauthnDeviceRecordSignInByUsername, tableWebauthnDevices),
sqlUpdateWebAuthnDeviceRecordSignIn: fmt.Sprintf(queryFmtUpdateWebAuthnDeviceRecordSignIn, tableWebAuthnDevices),
sqlUpdateWebAuthnDeviceRecordSignInByUsername: fmt.Sprintf(queryFmtUpdateWebAuthnDeviceRecordSignInByUsername, tableWebAuthnDevices),
sqlDeleteWebauthnDevice: fmt.Sprintf(queryFmtDeleteWebauthnDevice, tableWebauthnDevices),
sqlDeleteWebauthnDeviceByUsername: fmt.Sprintf(queryFmtDeleteWebauthnDeviceByUsername, tableWebauthnDevices),
sqlDeleteWebauthnDeviceByUsernameAndDescription: fmt.Sprintf(queryFmtDeleteWebauthnDeviceByUsernameAndDescription, tableWebauthnDevices),
sqlDeleteWebAuthnDevice: fmt.Sprintf(queryFmtDeleteWebAuthnDevice, tableWebAuthnDevices),
sqlDeleteWebAuthnDeviceByUsername: fmt.Sprintf(queryFmtDeleteWebAuthnDeviceByUsername, tableWebAuthnDevices),
sqlDeleteWebAuthnDeviceByUsernameAndDescription: fmt.Sprintf(queryFmtDeleteWebAuthnDeviceByUsernameAndDescription, tableWebAuthnDevices),
sqlUpsertDuoDevice: fmt.Sprintf(queryFmtUpsertDuoDevice, tableDuoDevices),
sqlDeleteDuoDevice: fmt.Sprintf(queryFmtDeleteDuoDevice, tableDuoDevices),
@ -63,7 +63,7 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
sqlUpsertPreferred2FAMethod: fmt.Sprintf(queryFmtUpsertPreferred2FAMethod, tableUserPreferences),
sqlSelectPreferred2FAMethod: fmt.Sprintf(queryFmtSelectPreferred2FAMethod, tableUserPreferences),
sqlSelectUserInfo: fmt.Sprintf(queryFmtSelectUserInfo, tableTOTPConfigurations, tableWebauthnDevices, tableDuoDevices, tableUserPreferences),
sqlSelectUserInfo: fmt.Sprintf(queryFmtSelectUserInfo, tableTOTPConfigurations, tableWebAuthnDevices, tableDuoDevices, tableUserPreferences),
sqlInsertUserOpaqueIdentifier: fmt.Sprintf(queryFmtInsertUserOpaqueIdentifier, tableUserOpaqueIdentifier),
sqlSelectUserOpaqueIdentifier: fmt.Sprintf(queryFmtSelectUserOpaqueIdentifier, tableUserOpaqueIdentifier),
@ -165,16 +165,16 @@ type SQLProvider struct {
sqlUpdateTOTPConfigRecordSignInByUsername string
// Table: webauthn_devices.
sqlUpsertWebauthnDevice string
sqlSelectWebauthnDevices string
sqlSelectWebauthnDevicesByUsername string
sqlUpsertWebAuthnDevice string
sqlSelectWebAuthnDevices string
sqlSelectWebAuthnDevicesByUsername string
sqlUpdateWebauthnDeviceRecordSignIn string
sqlUpdateWebauthnDeviceRecordSignInByUsername string
sqlUpdateWebAuthnDeviceRecordSignIn string
sqlUpdateWebAuthnDeviceRecordSignInByUsername string
sqlDeleteWebauthnDevice string
sqlDeleteWebauthnDeviceByUsername string
sqlDeleteWebauthnDeviceByUsernameAndDescription string
sqlDeleteWebAuthnDevice string
sqlDeleteWebAuthnDeviceByUsername string
sqlDeleteWebAuthnDeviceByUsernameAndDescription string
// Table: duo_devices.
sqlUpsertDuoDevice string
@ -823,7 +823,7 @@ func (p *SQLProvider) SaveTOTPConfiguration(ctx context.Context, config model.TO
return nil
}
// UpdateTOTPConfigurationSignIn updates a registered Webauthn devices sign in information.
// UpdateTOTPConfigurationSignIn updates a registered WebAuthn devices sign in information.
func (p *SQLProvider) UpdateTOTPConfigurationSignIn(ctx context.Context, id int, lastUsedAt sql.NullTime) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlUpdateTOTPConfigRecordSignIn, lastUsedAt, id); err != nil {
return fmt.Errorf("error updating TOTP configuration id %d: %w", id, err)
@ -881,95 +881,95 @@ func (p *SQLProvider) LoadTOTPConfigurations(ctx context.Context, limit, page in
return configs, nil
}
// SaveWebauthnDevice saves a registered Webauthn device.
func (p *SQLProvider) SaveWebauthnDevice(ctx context.Context, device model.WebAuthnDevice) (err error) {
// SaveWebAuthnDevice saves a registered WebAuthn device.
func (p *SQLProvider) SaveWebAuthnDevice(ctx context.Context, device model.WebAuthnDevice) (err error) {
if device.PublicKey, err = p.encrypt(device.PublicKey); err != nil {
return fmt.Errorf("error encrypting Webauthn device public key for user '%s' kid '%x': %w", device.Username, device.KID, err)
return fmt.Errorf("error encrypting WebAuthn device public key for user '%s' kid '%x': %w", device.Username, device.KID, err)
}
if _, err = p.db.ExecContext(ctx, p.sqlUpsertWebauthnDevice,
if _, err = p.db.ExecContext(ctx, p.sqlUpsertWebAuthnDevice,
device.CreatedAt, device.LastUsedAt,
device.RPID, device.Username, device.Description,
device.KID, device.PublicKey,
device.AttestationType, device.Transport, device.AAGUID, device.SignCount, device.CloneWarning,
); err != nil {
return fmt.Errorf("error upserting Webauthn device for user '%s' kid '%x': %w", device.Username, device.KID, err)
return fmt.Errorf("error upserting WebAuthn device for user '%s' kid '%x': %w", device.Username, device.KID, err)
}
return nil
}
// UpdateWebauthnDeviceSignIn updates a registered Webauthn devices sign in information.
func (p *SQLProvider) UpdateWebauthnDeviceSignIn(ctx context.Context, id int, rpid string, lastUsedAt sql.NullTime, signCount uint32, cloneWarning bool) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlUpdateWebauthnDeviceRecordSignIn, rpid, lastUsedAt, signCount, cloneWarning, id); err != nil {
return fmt.Errorf("error updating Webauthn signin metadata for id '%x': %w", id, err)
// UpdateWebAuthnDeviceSignIn updates a registered WebAuthn devices sign in information.
func (p *SQLProvider) UpdateWebAuthnDeviceSignIn(ctx context.Context, id int, rpid string, lastUsedAt sql.NullTime, signCount uint32, cloneWarning bool) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlUpdateWebAuthnDeviceRecordSignIn, rpid, lastUsedAt, signCount, cloneWarning, id); err != nil {
return fmt.Errorf("error updating WebAuthn signin metadata for id '%x': %w", id, err)
}
return nil
}
// DeleteWebauthnDevice deletes a registered Webauthn device.
func (p *SQLProvider) DeleteWebauthnDevice(ctx context.Context, kid string) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlDeleteWebauthnDevice, kid); err != nil {
return fmt.Errorf("error deleting webauthn device with kid '%s': %w", kid, err)
// DeleteWebAuthnDevice deletes a registered WebAuthn device.
func (p *SQLProvider) DeleteWebAuthnDevice(ctx context.Context, kid string) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlDeleteWebAuthnDevice, kid); err != nil {
return fmt.Errorf("error deleting WebAuthn device with kid '%s': %w", kid, err)
}
return nil
}
// DeleteWebauthnDeviceByUsername deletes registered Webauthn devices by username or username and description.
func (p *SQLProvider) DeleteWebauthnDeviceByUsername(ctx context.Context, username, description string) (err error) {
// DeleteWebAuthnDeviceByUsername deletes registered WebAuthn devices by username or username and description.
func (p *SQLProvider) DeleteWebAuthnDeviceByUsername(ctx context.Context, username, description string) (err error) {
if len(username) == 0 {
return fmt.Errorf("error deleting webauthn device with username '%s' and description '%s': username must not be empty", username, description)
return fmt.Errorf("error deleting WebAuthn device with username '%s' and description '%s': username must not be empty", username, description)
}
if len(description) == 0 {
if _, err = p.db.ExecContext(ctx, p.sqlDeleteWebauthnDeviceByUsername, username); err != nil {
return fmt.Errorf("error deleting webauthn devices for username '%s': %w", username, err)
if _, err = p.db.ExecContext(ctx, p.sqlDeleteWebAuthnDeviceByUsername, username); err != nil {
return fmt.Errorf("error deleting WebAuthn devices for username '%s': %w", username, err)
}
} else {
if _, err = p.db.ExecContext(ctx, p.sqlDeleteWebauthnDeviceByUsernameAndDescription, username, description); err != nil {
return fmt.Errorf("error deleting webauthn device with username '%s' and description '%s': %w", username, description, err)
if _, err = p.db.ExecContext(ctx, p.sqlDeleteWebAuthnDeviceByUsernameAndDescription, username, description); err != nil {
return fmt.Errorf("error deleting WebAuthn device with username '%s' and description '%s': %w", username, description, err)
}
}
return nil
}
// LoadWebauthnDevices loads Webauthn device registrations.
func (p *SQLProvider) LoadWebauthnDevices(ctx context.Context, limit, page int) (devices []model.WebAuthnDevice, err error) {
// LoadWebAuthnDevices loads WebAuthn device registrations.
func (p *SQLProvider) LoadWebAuthnDevices(ctx context.Context, limit, page int) (devices []model.WebAuthnDevice, err error) {
devices = make([]model.WebAuthnDevice, 0, limit)
if err = p.db.SelectContext(ctx, &devices, p.sqlSelectWebauthnDevices, limit, limit*page); err != nil {
if err = p.db.SelectContext(ctx, &devices, p.sqlSelectWebAuthnDevices, limit, limit*page); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, fmt.Errorf("error selecting Webauthn devices: %w", err)
return nil, fmt.Errorf("error selecting WebAuthn devices: %w", err)
}
for i, device := range devices {
if devices[i].PublicKey, err = p.decrypt(device.PublicKey); err != nil {
return nil, fmt.Errorf("error decrypting Webauthn public key for user '%s': %w", device.Username, err)
return nil, fmt.Errorf("error decrypting WebAuthn public key for user '%s': %w", device.Username, err)
}
}
return devices, nil
}
// LoadWebauthnDevicesByUsername loads all webauthn devices registration for a given username.
func (p *SQLProvider) LoadWebauthnDevicesByUsername(ctx context.Context, username string) (devices []model.WebAuthnDevice, err error) {
if err = p.db.SelectContext(ctx, &devices, p.sqlSelectWebauthnDevicesByUsername, username); err != nil {
// LoadWebAuthnDevicesByUsername loads all WebAuthn devices registration for a given username.
func (p *SQLProvider) LoadWebAuthnDevicesByUsername(ctx context.Context, username string) (devices []model.WebAuthnDevice, err error) {
if err = p.db.SelectContext(ctx, &devices, p.sqlSelectWebAuthnDevicesByUsername, username); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrNoWebauthnDevice
return nil, ErrNoWebAuthnDevice
}
return nil, fmt.Errorf("error selecting Webauthn devices for user '%s': %w", username, err)
return nil, fmt.Errorf("error selecting WebAuthn devices for user '%s': %w", username, err)
}
for i, device := range devices {
if devices[i].PublicKey, err = p.decrypt(device.PublicKey); err != nil {
return nil, fmt.Errorf("error decrypting Webauthn public key for user '%s': %w", username, err)
return nil, fmt.Errorf("error decrypting WebAuthn public key for user '%s': %w", username, err)
}
}

View File

@ -31,7 +31,7 @@ func NewPostgreSQLProvider(config *schema.Configuration, caCertPool *x509.CertPo
// Specific alterations to this provider.
// PostgreSQL doesn't have a UPSERT statement but has an ON CONFLICT operation instead.
provider.sqlUpsertWebauthnDevice = fmt.Sprintf(queryFmtUpsertWebauthnDevicePostgreSQL, tableWebauthnDevices)
provider.sqlUpsertWebAuthnDevice = fmt.Sprintf(queryFmtUpsertWebAuthnDevicePostgreSQL, tableWebAuthnDevices)
provider.sqlUpsertDuoDevice = fmt.Sprintf(queryFmtUpsertDuoDevicePostgreSQL, tableDuoDevices)
provider.sqlUpsertTOTPConfig = fmt.Sprintf(queryFmtUpsertTOTPConfigurationPostgreSQL, tableTOTPConfigurations)
provider.sqlUpsertPreferred2FAMethod = fmt.Sprintf(queryFmtUpsertPreferred2FAMethodPostgreSQL, tableUserPreferences)
@ -59,13 +59,13 @@ func NewPostgreSQLProvider(config *schema.Configuration, caCertPool *x509.CertPo
provider.sqlDeleteTOTPConfig = provider.db.Rebind(provider.sqlDeleteTOTPConfig)
provider.sqlSelectTOTPConfigs = provider.db.Rebind(provider.sqlSelectTOTPConfigs)
provider.sqlSelectWebauthnDevices = provider.db.Rebind(provider.sqlSelectWebauthnDevices)
provider.sqlSelectWebauthnDevicesByUsername = provider.db.Rebind(provider.sqlSelectWebauthnDevicesByUsername)
provider.sqlUpdateWebauthnDeviceRecordSignIn = provider.db.Rebind(provider.sqlUpdateWebauthnDeviceRecordSignIn)
provider.sqlUpdateWebauthnDeviceRecordSignInByUsername = provider.db.Rebind(provider.sqlUpdateWebauthnDeviceRecordSignInByUsername)
provider.sqlDeleteWebauthnDevice = provider.db.Rebind(provider.sqlDeleteWebauthnDevice)
provider.sqlDeleteWebauthnDeviceByUsername = provider.db.Rebind(provider.sqlDeleteWebauthnDeviceByUsername)
provider.sqlDeleteWebauthnDeviceByUsernameAndDescription = provider.db.Rebind(provider.sqlDeleteWebauthnDeviceByUsernameAndDescription)
provider.sqlSelectWebAuthnDevices = provider.db.Rebind(provider.sqlSelectWebAuthnDevices)
provider.sqlSelectWebAuthnDevicesByUsername = provider.db.Rebind(provider.sqlSelectWebAuthnDevicesByUsername)
provider.sqlUpdateWebAuthnDeviceRecordSignIn = provider.db.Rebind(provider.sqlUpdateWebAuthnDeviceRecordSignIn)
provider.sqlUpdateWebAuthnDeviceRecordSignInByUsername = provider.db.Rebind(provider.sqlUpdateWebAuthnDeviceRecordSignInByUsername)
provider.sqlDeleteWebAuthnDevice = provider.db.Rebind(provider.sqlDeleteWebAuthnDevice)
provider.sqlDeleteWebAuthnDeviceByUsername = provider.db.Rebind(provider.sqlDeleteWebAuthnDeviceByUsername)
provider.sqlDeleteWebAuthnDeviceByUsernameAndDescription = provider.db.Rebind(provider.sqlDeleteWebAuthnDeviceByUsernameAndDescription)
provider.sqlSelectDuoDevice = provider.db.Rebind(provider.sqlSelectDuoDevice)
provider.sqlDeleteDuoDevice = provider.db.Rebind(provider.sqlDeleteDuoDevice)

View File

@ -34,7 +34,7 @@ func (p *SQLProvider) SchemaEncryptionChangeKey(ctx context.Context, key string)
encChangeFuncs := []EncryptionChangeKeyFunc{
schemaEncryptionChangeKeyTOTP,
schemaEncryptionChangeKeyWebauthn,
schemaEncryptionChangeKeyWebAuthn,
}
for i := 0; true; i++ {
@ -90,7 +90,7 @@ func (p *SQLProvider) SchemaEncryptionCheckKey(ctx context.Context, verbose bool
if verbose {
encCheckFuncs := []EncryptionCheckKeyFunc{
schemaEncryptionCheckKeyTOTP,
schemaEncryptionCheckKeyWebauthn,
schemaEncryptionCheckKeyWebAuthn,
}
for i := 0; true; i++ {
@ -153,10 +153,10 @@ func schemaEncryptionChangeKeyTOTP(ctx context.Context, provider *SQLProvider, t
return nil
}
func schemaEncryptionChangeKeyWebauthn(ctx context.Context, provider *SQLProvider, tx *sqlx.Tx, key [32]byte) (err error) {
func schemaEncryptionChangeKeyWebAuthn(ctx context.Context, provider *SQLProvider, tx *sqlx.Tx, key [32]byte) (err error) {
var count int
if err = tx.GetContext(ctx, &count, fmt.Sprintf(queryFmtSelectRowCount, tableWebauthnDevices)); err != nil {
if err = tx.GetContext(ctx, &count, fmt.Sprintf(queryFmtSelectRowCount, tableWebAuthnDevices)); err != nil {
return err
}
@ -164,29 +164,29 @@ func schemaEncryptionChangeKeyWebauthn(ctx context.Context, provider *SQLProvide
return nil
}
devices := make([]encWebauthnDevice, 0, count)
devices := make([]encWebAuthnDevice, 0, count)
if err = tx.SelectContext(ctx, &devices, fmt.Sprintf(queryFmtSelectWebauthnDevicesEncryptedData, tableWebauthnDevices)); err != nil {
if err = tx.SelectContext(ctx, &devices, fmt.Sprintf(queryFmtSelectWebAuthnDevicesEncryptedData, tableWebAuthnDevices)); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil
}
return fmt.Errorf("error selecting Webauthn devices: %w", err)
return fmt.Errorf("error selecting WebAuthn devices: %w", err)
}
query := provider.db.Rebind(fmt.Sprintf(queryFmtUpdateWebauthnDevicePublicKey, tableWebauthnDevices))
query := provider.db.Rebind(fmt.Sprintf(queryFmtUpdateWebAuthnDevicePublicKey, tableWebAuthnDevices))
for _, d := range devices {
if d.PublicKey, err = provider.decrypt(d.PublicKey); err != nil {
return fmt.Errorf("error decrypting Webauthn device public key with id '%d': %w", d.ID, err)
return fmt.Errorf("error decrypting WebAuthn device public key with id '%d': %w", d.ID, err)
}
if d.PublicKey, err = utils.Encrypt(d.PublicKey, &key); err != nil {
return fmt.Errorf("error encrypting Webauthn device public key with id '%d': %w", d.ID, err)
return fmt.Errorf("error encrypting WebAuthn device public key with id '%d': %w", d.ID, err)
}
if _, err = tx.ExecContext(ctx, query, d.PublicKey, d.ID); err != nil {
return fmt.Errorf("error updating Webauthn device public key with id '%d': %w", d.ID, err)
return fmt.Errorf("error updating WebAuthn device public key with id '%d': %w", d.ID, err)
}
}
@ -262,17 +262,17 @@ func schemaEncryptionCheckKeyTOTP(ctx context.Context, provider *SQLProvider) (t
return tableTOTPConfigurations, result
}
func schemaEncryptionCheckKeyWebauthn(ctx context.Context, provider *SQLProvider) (table string, result EncryptionValidationTableResult) {
func schemaEncryptionCheckKeyWebAuthn(ctx context.Context, provider *SQLProvider) (table string, result EncryptionValidationTableResult) {
var (
rows *sqlx.Rows
err error
)
if rows, err = provider.db.QueryxContext(ctx, fmt.Sprintf(queryFmtSelectWebauthnDevicesEncryptedData, tableWebauthnDevices)); err != nil {
return tableWebauthnDevices, EncryptionValidationTableResult{Error: fmt.Errorf("error selecting Webauthn devices: %w", err)}
if rows, err = provider.db.QueryxContext(ctx, fmt.Sprintf(queryFmtSelectWebAuthnDevicesEncryptedData, tableWebAuthnDevices)); err != nil {
return tableWebAuthnDevices, EncryptionValidationTableResult{Error: fmt.Errorf("error selecting WebAuthn devices: %w", err)}
}
var device encWebauthnDevice
var device encWebAuthnDevice
for rows.Next() {
result.Total++
@ -280,7 +280,7 @@ func schemaEncryptionCheckKeyWebauthn(ctx context.Context, provider *SQLProvider
if err = rows.StructScan(&device); err != nil {
_ = rows.Close()
return tableWebauthnDevices, EncryptionValidationTableResult{Error: fmt.Errorf("error scanning Webauthn device to struct: %w", err)}
return tableWebAuthnDevices, EncryptionValidationTableResult{Error: fmt.Errorf("error scanning WebAuthn device to struct: %w", err)}
}
if _, err = provider.decrypt(device.PublicKey); err != nil {
@ -290,7 +290,7 @@ func schemaEncryptionCheckKeyWebauthn(ctx context.Context, provider *SQLProvider
_ = rows.Close()
return tableWebauthnDevices, result
return tableWebAuthnDevices, result
}
func schemaEncryptionCheckKeyOpenIDConnect(typeOAuth2Session OAuth2SessionType) EncryptionCheckKeyFunc {

View File

@ -119,59 +119,59 @@ const (
)
const (
queryFmtSelectWebauthnDevices = `
queryFmtSelectWebAuthnDevices = `
SELECT id, created_at, last_used_at, rpid, username, description, kid, public_key, attestation_type, transport, aaguid, sign_count, clone_warning
FROM %s
LIMIT ?
OFFSET ?;`
queryFmtSelectWebauthnDevicesEncryptedData = `
queryFmtSelectWebAuthnDevicesEncryptedData = `
SELECT id, public_key
FROM %s;`
queryFmtSelectWebauthnDevicesByUsername = `
queryFmtSelectWebAuthnDevicesByUsername = `
SELECT id, created_at, last_used_at, rpid, username, description, kid, public_key, attestation_type, transport, aaguid, sign_count, clone_warning
FROM %s
WHERE username = ?;`
queryFmtUpdateWebauthnDevicePublicKey = `
queryFmtUpdateWebAuthnDevicePublicKey = `
UPDATE %s
SET public_key = ?
WHERE id = ?;`
queryFmtUpdateWebauthnDeviceRecordSignIn = `
queryFmtUpdateWebAuthnDeviceRecordSignIn = `
UPDATE %s
SET
rpid = ?, last_used_at = ?, sign_count = ?,
clone_warning = CASE clone_warning WHEN TRUE THEN TRUE ELSE ? END
WHERE id = ?;`
queryFmtUpdateWebauthnDeviceRecordSignInByUsername = `
queryFmtUpdateWebAuthnDeviceRecordSignInByUsername = `
UPDATE %s
SET
rpid = ?, last_used_at = ?, sign_count = ?,
clone_warning = CASE clone_warning WHEN TRUE THEN TRUE ELSE ? END
WHERE username = ? AND kid = ?;`
queryFmtUpsertWebauthnDevice = `
queryFmtUpsertWebAuthnDevice = `
REPLACE INTO %s (created_at, last_used_at, rpid, username, description, kid, public_key, attestation_type, transport, aaguid, sign_count, clone_warning)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`
queryFmtUpsertWebauthnDevicePostgreSQL = `
queryFmtUpsertWebAuthnDevicePostgreSQL = `
INSERT INTO %s (created_at, last_used_at, rpid, username, description, kid, public_key, attestation_type, transport, aaguid, sign_count, clone_warning)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
ON CONFLICT (username, description)
DO UPDATE SET created_at = $1, last_used_at = $2, rpid = $3, kid = $6, public_key = $7, attestation_type = $8, transport = $9, aaguid = $10, sign_count = $11, clone_warning = $12;`
queryFmtDeleteWebauthnDevice = `
queryFmtDeleteWebAuthnDevice = `
DELETE FROM %s
WHERE kid = ?;`
queryFmtDeleteWebauthnDeviceByUsername = `
queryFmtDeleteWebAuthnDeviceByUsername = `
DELETE FROM %s
WHERE username = ?;`
queryFmtDeleteWebauthnDeviceByUsernameAndDescription = `
queryFmtDeleteWebAuthnDeviceByUsernameAndDescription = `
DELETE FROM %s
WHERE username = ? AND description = ?;`
)

View File

@ -32,7 +32,7 @@ type encOAuth2Session struct {
Session []byte `db:"session_data"`
}
type encWebauthnDevice struct {
type encWebAuthnDevice struct {
ID int `db:"id"`
PublicKey []byte `db:"public_key"`
}

View File

@ -12,7 +12,7 @@ import {
IndexRoute,
LogoutRoute,
RegisterOneTimePasswordRoute,
RegisterWebauthnRoute,
RegisterWebAuthnRoute,
ResetPasswordStep1Route,
ResetPasswordStep2Route,
} from "@constants/Routes";
@ -28,7 +28,7 @@ import {
getTheme,
} from "@utils/Configuration";
import RegisterOneTimePassword from "@views/DeviceRegistration/RegisterOneTimePassword";
import RegisterWebauthn from "@views/DeviceRegistration/RegisterWebauthn";
import RegisterWebAuthn from "@views/DeviceRegistration/RegisterWebAuthn";
import BaseLoadingPage from "@views/LoadingPage/BaseLoadingPage";
import ConsentView from "@views/LoginPortal/ConsentView/ConsentView";
import LoginPortal from "@views/LoginPortal/LoginPortal";
@ -89,7 +89,7 @@ const App: React.FC<Props> = (props: Props) => {
<Routes>
<Route path={ResetPasswordStep1Route} element={<ResetPasswordStep1 />} />
<Route path={ResetPasswordStep2Route} element={<ResetPasswordStep2 />} />
<Route path={RegisterWebauthnRoute} element={<RegisterWebauthn />} />
<Route path={RegisterWebAuthnRoute} element={<RegisterWebAuthn />} />
<Route path={RegisterOneTimePasswordRoute} element={<RegisterOneTimePassword />} />
<Route path={LogoutRoute} element={<SignOut />} />
<Route path={ConsentRoute} element={<ConsentView />} />

View File

@ -3,12 +3,12 @@ export const AuthenticatedRoute: string = "/authenticated";
export const ConsentRoute: string = "/consent";
export const SecondFactorRoute: string = "/2fa/";
export const SecondFactorWebauthnSubRoute: string = "webauthn";
export const SecondFactorWebAuthnSubRoute: string = "webauthn";
export const SecondFactorTOTPSubRoute: string = "one-time-password";
export const SecondFactorPushSubRoute: string = "push-notification";
export const ResetPasswordStep1Route: string = "/reset-password/step1";
export const ResetPasswordStep2Route: string = "/reset-password/step2";
export const RegisterWebauthnRoute: string = "/webauthn/register";
export const RegisterWebAuthnRoute: string = "/webauthn/register";
export const RegisterOneTimePasswordRoute: string = "/one-time-password/register";
export const LogoutRoute: string = "/logout";

View File

@ -11,11 +11,11 @@ export const FirstFactorPath = basePath + "/api/firstfactor";
export const InitiateTOTPRegistrationPath = basePath + "/api/secondfactor/totp/identity/start";
export const CompleteTOTPRegistrationPath = basePath + "/api/secondfactor/totp/identity/finish";
export const WebauthnIdentityStartPath = basePath + "/api/secondfactor/webauthn/identity/start";
export const WebauthnIdentityFinishPath = basePath + "/api/secondfactor/webauthn/identity/finish";
export const WebauthnAttestationPath = basePath + "/api/secondfactor/webauthn/attestation";
export const WebAuthnIdentityStartPath = basePath + "/api/secondfactor/webauthn/identity/start";
export const WebAuthnIdentityFinishPath = basePath + "/api/secondfactor/webauthn/identity/finish";
export const WebAuthnAttestationPath = basePath + "/api/secondfactor/webauthn/attestation";
export const WebauthnAssertionPath = basePath + "/api/secondfactor/webauthn/assertion";
export const WebAuthnAssertionPath = basePath + "/api/secondfactor/webauthn/assertion";
export const InitiateDuoDeviceSelectionPath = basePath + "/api/secondfactor/duo_devices";
export const CompleteDuoDeviceSelectionPath = basePath + "/api/secondfactor/duo_device";

View File

@ -1,4 +1,4 @@
import { CompleteTOTPRegistrationPath, InitiateTOTPRegistrationPath, WebauthnIdentityStartPath } from "@services/Api";
import { CompleteTOTPRegistrationPath, InitiateTOTPRegistrationPath, WebAuthnIdentityStartPath } from "@services/Api";
import { Post, PostWithOptionalResponse } from "@services/Client";
export async function initiateTOTPRegistrationProcess() {
@ -14,6 +14,6 @@ export async function completeTOTPRegistrationProcess(processToken: string) {
return Post<CompleteTOTPRegistrationResponse>(CompleteTOTPRegistrationPath, { token: processToken });
}
export async function initiateWebauthnRegistrationProcess() {
return PostWithOptionalResponse(WebauthnIdentityStartPath);
export async function initiateWebAuthnRegistrationProcess() {
return PostWithOptionalResponse(WebAuthnIdentityStartPath);
}

View File

@ -20,14 +20,14 @@ import {
import {
OptionalDataServiceResponse,
ServiceResponse,
WebauthnAssertionPath,
WebauthnAttestationPath,
WebauthnIdentityFinishPath,
WebAuthnAssertionPath,
WebAuthnAttestationPath,
WebAuthnIdentityFinishPath,
} from "@services/Api";
import { SignInResponse } from "@services/SignIn";
import { getBase64WebEncodingFromBytes, getBytesFromBase64 } from "@utils/Base64";
export function isWebauthnSecure(): boolean {
export function isWebAuthnSecure(): boolean {
if (window.isSecureContext) {
return true;
}
@ -35,12 +35,12 @@ export function isWebauthnSecure(): boolean {
return window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1";
}
export function isWebauthnSupported(): boolean {
export function isWebAuthnSupported(): boolean {
return window?.PublicKeyCredential !== undefined && typeof window.PublicKeyCredential === "function";
}
export async function isWebauthnPlatformAuthenticatorAvailable(): Promise<boolean> {
if (!isWebauthnSupported()) {
export async function isWebAuthnPlatformAuthenticatorAvailable(): Promise<boolean> {
if (!isWebAuthnSupported()) {
return false;
}
@ -215,7 +215,7 @@ function getAssertionResultFromDOMException(
async function getAttestationCreationOptions(token: string): Promise<PublicKeyCredentialCreationOptionsStatus> {
let response: AxiosResponse<ServiceResponse<CredentialCreation>>;
response = await axios.post<ServiceResponse<CredentialCreation>>(WebauthnIdentityFinishPath, {
response = await axios.post<ServiceResponse<CredentialCreation>>(WebAuthnIdentityFinishPath, {
token: token,
});
@ -234,7 +234,7 @@ async function getAttestationCreationOptions(token: string): Promise<PublicKeyCr
export async function getAssertionRequestOptions(): Promise<PublicKeyCredentialRequestOptionsStatus> {
let response: AxiosResponse<ServiceResponse<CredentialRequest>>;
response = await axios.get<ServiceResponse<CredentialRequest>>(WebauthnAssertionPath);
response = await axios.get<ServiceResponse<CredentialRequest>>(WebAuthnAssertionPath);
if (response.data.status !== "OK" || response.data.data == null) {
return {
@ -317,7 +317,7 @@ async function postAttestationPublicKeyCredentialResult(
): Promise<AxiosResponse<OptionalDataServiceResponse<any>>> {
const credentialJSON = encodeAttestationPublicKeyCredential(credential);
return axios.post<OptionalDataServiceResponse<any>>(WebauthnAttestationPath, credentialJSON);
return axios.post<OptionalDataServiceResponse<any>>(WebAuthnAttestationPath, credentialJSON);
}
export async function postAssertionPublicKeyCredentialResult(
@ -328,7 +328,7 @@ export async function postAssertionPublicKeyCredentialResult(
): Promise<AxiosResponse<ServiceResponse<SignInResponse>>> {
const credentialJSON = encodeAssertionPublicKeyCredential(credential, targetURL, workflow, workflowID);
return axios.post<ServiceResponse<SignInResponse>>(WebauthnAssertionPath, credentialJSON);
return axios.post<ServiceResponse<SignInResponse>>(WebAuthnAssertionPath, credentialJSON);
}
export async function performAttestationCeremony(token: string): Promise<AttestationResult> {

View File

@ -13,7 +13,7 @@ import { AttestationResult } from "@models/Webauthn";
import { FirstFactorPath } from "@services/Api";
import { performAttestationCeremony } from "@services/Webauthn";
const RegisterWebauthn = function () {
const RegisterWebAuthn = function () {
const styles = useStyles();
const navigate = useNavigate();
const { createErrorNotification } = useNotifications();
@ -99,7 +99,7 @@ const RegisterWebauthn = function () {
);
};
export default RegisterWebauthn;
export default RegisterWebAuthn;
const useStyles = makeStyles((theme: Theme) => ({
icon: {

View File

@ -8,7 +8,7 @@ import {
SecondFactorPushSubRoute,
SecondFactorRoute,
SecondFactorTOTPSubRoute,
SecondFactorWebauthnSubRoute,
SecondFactorWebAuthnSubRoute,
} from "@constants/Routes";
import { RedirectionURL } from "@constants/SearchParams";
import { useConfiguration } from "@hooks/Configuration";
@ -144,7 +144,7 @@ const LoginPortal = function (props: Props) {
redirect(AuthenticatedRoute, false);
} else {
if (userInfo.method === SecondFactorMethod.Webauthn) {
redirect(`${SecondFactorRoute}${SecondFactorWebauthnSubRoute}`);
redirect(`${SecondFactorRoute}${SecondFactorWebAuthnSubRoute}`);
} else if (userInfo.method === SecondFactorMethod.MobilePush) {
redirect(`${SecondFactorRoute}${SecondFactorPushSubRoute}`);
} else {

View File

@ -8,7 +8,7 @@ import { Route, Routes, useNavigate } from "react-router-dom";
import {
SecondFactorPushSubRoute,
SecondFactorTOTPSubRoute,
SecondFactorWebauthnSubRoute,
SecondFactorWebAuthnSubRoute,
LogoutRoute as SignOutRoute,
} from "@constants/Routes";
import { useNotifications } from "@hooks/NotificationsContext";
@ -16,10 +16,10 @@ import LoginLayout from "@layouts/LoginLayout";
import { Configuration } from "@models/Configuration";
import { SecondFactorMethod } from "@models/Methods";
import { UserInfo } from "@models/UserInfo";
import { initiateTOTPRegistrationProcess, initiateWebauthnRegistrationProcess } from "@services/RegisterDevice";
import { initiateTOTPRegistrationProcess, initiateWebAuthnRegistrationProcess } from "@services/RegisterDevice";
import { AuthenticationLevel } from "@services/State";
import { setPreferred2FAMethod } from "@services/UserInfo";
import { isWebauthnSupported } from "@services/Webauthn";
import { isWebAuthnSupported } from "@services/Webauthn";
import MethodSelectionDialog from "@views/LoginPortal/SecondFactor/MethodSelectionDialog";
import OneTimePasswordMethod from "@views/LoginPortal/SecondFactor/OneTimePasswordMethod";
import PushNotificationMethod from "@views/LoginPortal/SecondFactor/PushNotificationMethod";
@ -45,7 +45,7 @@ const SecondFactorForm = function (props: Props) {
const { t: translate } = useTranslation();
useEffect(() => {
setWebauthnSupported(isWebauthnSupported());
setWebauthnSupported(isWebAuthnSupported());
}, [setWebauthnSupported]);
const initiateRegistration = (initiateRegistrationFunc: () => Promise<void>) => {
@ -124,14 +124,14 @@ const SecondFactorForm = function (props: Props) {
}
/>
<Route
path={SecondFactorWebauthnSubRoute}
path={SecondFactorWebAuthnSubRoute}
element={
<WebauthnMethod
id="webauthn-method"
authenticationLevel={props.authenticationLevel}
// Whether the user has a Webauthn device registered already
registered={props.userInfo.has_webauthn}
onRegisterClick={initiateRegistration(initiateWebauthnRegistrationProcess)}
onRegisterClick={initiateRegistration(initiateWebAuthnRegistrationProcess)}
onSignInError={(err) => createErrorNotification(err.message)}
onSignInSuccess={props.onAuthenticationSuccess}
/>