refactor: webauthn naming (#5243)
Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>pull/5244/head
parent
37a49b21af
commit
2733fc040c
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
@ -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'",
|
||||
|
|
|
@ -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"}
|
||||
)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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"})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 = ?;`
|
||||
)
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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 />} />
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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: {
|
|
@ -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 {
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
Loading…
Reference in New Issue