summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Elliott <james-d-elliott@users.noreply.github.com>2023-10-30 05:43:25 +1100
committerJames Elliott <james-d-elliott@users.noreply.github.com>2024-03-04 20:29:11 +1100
commit85562a2465218273161cf9240ffebfe2ba6b187f (patch)
treea4d1ad1f62a5536056e2d169e9e896eedc071b35
parentb5f63fde3b1cef156de4ca968939c6dafd1f88fb (diff)
refactor: misc fixes
This implements misc fixes as part of one of our betas. Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>
-rw-r--r--api/openapi.yml58
-rw-r--r--cmd/authelia-gen/const.go8
-rw-r--r--docs/content/en/configuration/identity-providers/openid-connect/clients.md2
-rw-r--r--docs/content/en/configuration/identity-validation/_index.md2
-rw-r--r--docs/content/en/configuration/identity-validation/elevated-session.md2
-rw-r--r--docs/content/en/configuration/identity-validation/introduction.md2
-rw-r--r--docs/content/en/configuration/identity-validation/reset-password.md2
-rw-r--r--go.mod4
-rw-r--r--go.sum8
-rw-r--r--internal/configuration/provider_test.go2
-rw-r--r--internal/configuration/validator/identity_providers_test.go2
-rw-r--r--internal/handlers/const.go14
-rw-r--r--internal/handlers/handler_firstfactor.go14
-rw-r--r--internal/handlers/handler_firstfactor_test.go35
-rw-r--r--internal/handlers/handler_register_webauthn.go41
-rwxr-xr-xinternal/handlers/handler_session_elevation.go14
-rw-r--r--internal/handlers/handler_sign_duo.go6
-rw-r--r--internal/handlers/handler_sign_totp.go6
-rw-r--r--internal/handlers/handler_sign_webauthn.go24
-rw-r--r--internal/storage/migrations/mysql/V0012.WebAuthnMultiCookieDomain.down.sql2
-rw-r--r--internal/storage/migrations/mysql/V0012.WebAuthnMultiCookieDomain.up.sql2
-rw-r--r--internal/storage/migrations/postgres/V0012.WebAuthnMultiCookieDomain.down.sql2
-rw-r--r--internal/storage/migrations/postgres/V0012.WebAuthnMultiCookieDomain.up.sql2
-rw-r--r--web/pnpm-lock.yaml29
-rw-r--r--web/src/views/Settings/Common/IdentityVerificationDialog.tsx10
-rw-r--r--web/src/views/Settings/TwoFactorAuthentication/OneTimePasswordRegisterDialog.tsx1
-rw-r--r--web/src/views/Settings/TwoFactorAuthentication/TwoFactorAuthenticationOptionsPanel.tsx23
-rwxr-xr-xweb/src/views/Settings/TwoFactorAuthentication/TwoFactorAuthenticationView.tsx33
28 files changed, 177 insertions, 173 deletions
diff --git a/api/openapi.yml b/api/openapi.yml
index a721656b2..5a2c148dd 100644
--- a/api/openapi.yml
+++ b/api/openapi.yml
@@ -95,7 +95,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
/api/state:
get:
tags:
@@ -534,7 +534,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
security:
- authelia_auth: []
/api/reset-password/identity/finish:
@@ -560,7 +560,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
security:
- authelia_auth: []
/api/reset-password:
@@ -585,7 +585,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
security:
- authelia_auth: []
delete:
@@ -607,7 +607,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.API'
security:
- authelia_auth: []
{{- end }}
@@ -665,7 +665,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
"403":
description: Forbidden
security:
@@ -707,7 +707,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
"403":
description: Forbidden
security:
@@ -752,9 +752,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
- "403":
- description: Forbidden
+ $ref: '#/components/schemas/middlewares.Response.API'
security:
- authelia_auth: []
{{- if .TOTP }}
@@ -822,7 +820,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
"403":
description: Forbidden
security:
@@ -840,7 +838,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
"403":
description: Forbidden
security:
@@ -888,7 +886,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.ErrorResponse'
+ $ref: '#/components/schemas/middlewares.Response.KO'
security:
- authelia_auth: []
delete:
@@ -903,7 +901,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
"403":
description: Forbidden
security:
@@ -998,7 +996,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
"401":
description: Unauthorized
security:
@@ -1016,7 +1014,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
"403":
description: Forbidden
security:
@@ -1043,7 +1041,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
security:
- authelia_auth: []
delete:
@@ -1061,7 +1059,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
security:
- authelia_auth: []
{{- end }}
@@ -1124,7 +1122,7 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/middlewares.OkResponse'
+ $ref: '#/components/schemas/middlewares.Response.OK'
"401":
description: Unauthorized
security:
@@ -2151,14 +2149,32 @@ components:
token:
type: string
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDc5MjU1OTYsImlzcyI6IkF1dGhlbGlhIiwiYWN0aW9uIjoiUmVzZXRQYXNzd29yZCIsInVzZXJuYW1lIjoiQW1pciJ9.636yqRrUCGCe4jsMCsonleX5CYWHncYqZum-YYb6VaY
- middlewares.OkResponse:
+ middlewares.Response.API:
+ oneOf:
+ - $ref: '#/components/schemas/middlewares.Response.OK'
+ - $ref: '#/components/schemas/middlewares.Response.KO'
+ middlewares.Response.OK:
type: object
properties:
status:
+ enum:
+ - 'OK'
type: string
- example: OK
+ example: 'OK'
data:
type: object
+ description: 'The data content for the response.'
+ middlewares.Response.KO:
+ type: object
+ properties:
+ status:
+ enum:
+ - 'KO'
+ type: string
+ example: 'KO'
+ message:
+ type: string
+ example: 'Operation Failed.'
handlers.UserInfo:
type: object
properties:
diff --git a/cmd/authelia-gen/const.go b/cmd/authelia-gen/const.go
index afbc0b2bc..202709c3a 100644
--- a/cmd/authelia-gen/const.go
+++ b/cmd/authelia-gen/const.go
@@ -61,14 +61,6 @@ const (
)
const (
- extJSON = ".json"
-)
-
-const (
- pathJSONSchema = "json-schema"
-)
-
-const (
pkgConfigSchema = "schema"
pkgScriptsGen = "cmd"
)
diff --git a/docs/content/en/configuration/identity-providers/openid-connect/clients.md b/docs/content/en/configuration/identity-providers/openid-connect/clients.md
index 5590e930e..55ec1e74b 100644
--- a/docs/content/en/configuration/identity-providers/openid-connect/clients.md
+++ b/docs/content/en/configuration/identity-providers/openid-connect/clients.md
@@ -523,7 +523,7 @@ calculated in the [issuer_private_keys].
### request_object_signing_alg
-{{< confkey type="string" default="RSA256" required="no" >}}
+{{< confkey type="string" default="RS256" required="no" >}}
The JWT signing algorithm accepted for request objects.
diff --git a/docs/content/en/configuration/identity-validation/_index.md b/docs/content/en/configuration/identity-validation/_index.md
index 52534e408..67ccb9430 100644
--- a/docs/content/en/configuration/identity-validation/_index.md
+++ b/docs/content/en/configuration/identity-validation/_index.md
@@ -2,7 +2,7 @@
title: "Identity Validation"
description: "Identity Validation Configuration"
lead: ""
-date: 2023-10-28T18:57:18+11:00
+date: 2023-11-20T21:28:38+11:00
draft: false
images: []
weight: 105000
diff --git a/docs/content/en/configuration/identity-validation/elevated-session.md b/docs/content/en/configuration/identity-validation/elevated-session.md
index e77c1ff1a..cc00805f8 100644
--- a/docs/content/en/configuration/identity-validation/elevated-session.md
+++ b/docs/content/en/configuration/identity-validation/elevated-session.md
@@ -2,7 +2,7 @@
title: "Elevated Session"
description: "Elevated Session Identity Validation Configuration"
lead: "Authelia uses multiple methods to verify the identity of users to prevent a malicious user from performing actions on behalf of them. This section describes the Elevated Session method."
-date: 2023-10-28T18:57:18+11:00
+date: 2023-11-20T21:28:38+11:00
draft: false
images: []
menu:
diff --git a/docs/content/en/configuration/identity-validation/introduction.md b/docs/content/en/configuration/identity-validation/introduction.md
index 8298366ac..d2d73a780 100644
--- a/docs/content/en/configuration/identity-validation/introduction.md
+++ b/docs/content/en/configuration/identity-validation/introduction.md
@@ -2,7 +2,7 @@
title: "Identity Validation"
description: "Identity Validation Configuration"
lead: "Authelia uses multiple methods to verify the identity of users to prevent a malicious user from performing actions on behalf of them. This section describes these methods."
-date: 2023-10-28T18:57:18+11:00
+date: 2023-11-20T21:28:38+11:00
draft: false
images: []
menu:
diff --git a/docs/content/en/configuration/identity-validation/reset-password.md b/docs/content/en/configuration/identity-validation/reset-password.md
index d20b91e66..a689289f1 100644
--- a/docs/content/en/configuration/identity-validation/reset-password.md
+++ b/docs/content/en/configuration/identity-validation/reset-password.md
@@ -2,7 +2,7 @@
title: "Reset Password"
description: "Reset Password Identity Validation Configuration"
lead: "Authelia uses multiple methods to verify the identity of users to prevent a malicious user from performing actions on behalf of them. This section describes Reset Password method."
-date: 2023-10-28T18:57:18+11:00
+date: 2023-11-20T21:28:38+11:00
draft: false
images: []
menu:
diff --git a/go.mod b/go.mod
index 3da384918..c61f302c0 100644
--- a/go.mod
+++ b/go.mod
@@ -16,7 +16,7 @@ require (
github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-rod/rod v0.114.7
github.com/go-sql-driver/mysql v1.7.1
- github.com/go-webauthn/webauthn v0.8.6
+ github.com/go-webauthn/webauthn v0.9.1
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/golang/mock v1.6.0
github.com/google/uuid v1.6.0
@@ -70,7 +70,7 @@ require (
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/ecordell/optgen v0.0.6 // indirect
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
- github.com/fxamacker/cbor/v2 v2.4.0 // indirect
+ github.com/fxamacker/cbor/v2 v2.5.0 // indirect
github.com/go-crypt/x v0.2.12 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-webauthn/x v0.1.4 // indirect
diff --git a/go.sum b/go.sum
index 49b64902b..0936e0578 100644
--- a/go.sum
+++ b/go.sum
@@ -113,8 +113,8 @@ github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
-github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
-github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
+github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
+github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-crypt/crypt v0.2.18 h1:j3YYrcHTpF4lLCwRW8//d91Wi7XSFqgsOYw0SHxljEg=
@@ -133,8 +133,8 @@ github.com/go-rod/rod v0.114.7/go.mod h1:aiedSEFg5DwG/fnNbUOTPMTTWX3MRj6vIs/a684
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
-github.com/go-webauthn/webauthn v0.8.6 h1:bKMtL1qzd2WTFkf1mFTVbreYrwn7dsYmEPjTq6QN90E=
-github.com/go-webauthn/webauthn v0.8.6/go.mod h1:emwVLMCI5yx9evTTvr0r+aOZCdWJqMfbRhF0MufyUog=
+github.com/go-webauthn/webauthn v0.9.1 h1:KuZjvUX9JTuFjB2n7kZhM6n76BClLUFbFM8SLKnrXpo=
+github.com/go-webauthn/webauthn v0.9.1/go.mod h1:m315kRGbUljOytw8b9FGWG9QzErjI5v02pNFCF3lwpI=
github.com/go-webauthn/x v0.1.4 h1:sGmIFhcY70l6k7JIDfnjVBiAAFEssga5lXIUXe0GtAs=
github.com/go-webauthn/x v0.1.4/go.mod h1:75Ug0oK6KYpANh5hDOanfDI+dvPWHk788naJVG/37H8=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
diff --git a/internal/configuration/provider_test.go b/internal/configuration/provider_test.go
index f35cc2dbb..864eb55e4 100644
--- a/internal/configuration/provider_test.go
+++ b/internal/configuration/provider_test.go
@@ -89,7 +89,7 @@ func TestShouldHaveNotifier(t *testing.T) {
func TestShouldConfigureRefreshIntervalDisable(t *testing.T) {
testSetEnv(t, "SESSION_SECRET", "abc")
testSetEnv(t, "STORAGE_MYSQL_PASSWORD", "abc")
- testSetEnv(t, "JWT_SECRET", "abc")
+ testSetEnv(t, "IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET", "abc")
testSetEnv(t, "AUTHENTICATION_BACKEND_LDAP_PASSWORD", "abc")
val := schema.NewStructValidator()
diff --git a/internal/configuration/validator/identity_providers_test.go b/internal/configuration/validator/identity_providers_test.go
index 1e5c5a805..b3e276488 100644
--- a/internal/configuration/validator/identity_providers_test.go
+++ b/internal/configuration/validator/identity_providers_test.go
@@ -2934,7 +2934,7 @@ func TestValidateOIDCClientJWKS(t *testing.T) {
nil,
},
{
- "ShouldOnlyAllowRequetsObjectSigningAlgsThatTheClientHasKeysFor",
+ "ShouldOnlyAllowRequestObjectSigningAlgsThatTheClientHasKeysFor",
nil,
[]schema.JWK{
{KeyID: "test", Use: "", Algorithm: "", Key: keyECDSAP521.Public()},
diff --git a/internal/handlers/const.go b/internal/handlers/const.go
index ad12c1781..efc4675c9 100644
--- a/internal/handlers/const.go
+++ b/internal/handlers/const.go
@@ -74,13 +74,13 @@ const (
)
const (
- logFmtErrParseRequestBody = "Failed to parse %s request body: %+v"
- logFmtErrWriteResponseBody = "Failed to write %s response body for user '%s': %+v"
- logFmtErrRegulationFail = "Failed to perform %s authentication regulation for user '%s': %+v"
- logFmtErrSessionRegenerate = "Could not regenerate session during %s authentication for user '%s': %+v"
- logFmtErrSessionReset = "Could not reset session during %s authentication for user '%s': %+v"
- logFmtErrSessionSave = "Could not save session with the %s during %s authentication for user '%s': %+v"
- logFmtErrObtainProfileDetails = "Could not obtain profile details during %s authentication for user '%s': %+v"
+ logFmtErrParseRequestBody = "Failed to parse %s request body"
+ logFmtErrWriteResponseBody = "Failed to write %s response body for user '%s'"
+ logFmtErrRegulationFail = "Failed to perform %s authentication regulation for user '%s'"
+ logFmtErrSessionRegenerate = "Could not regenerate session during %s authentication for user '%s'"
+ logFmtErrSessionReset = "Could not reset session during %s authentication for user '%s'"
+ logFmtErrSessionSave = "Could not save session with the %s during %s authentication for user '%s'"
+ logFmtErrObtainProfileDetails = "Could not obtain profile details during %s authentication for user '%s'"
logFmtTraceProfileDetails = "Profile details for user '%s' => groups: %s, emails %s"
)
diff --git a/internal/handlers/handler_firstfactor.go b/internal/handlers/handler_firstfactor.go
index d90a1172f..5bcf2dd21 100644
--- a/internal/handlers/handler_firstfactor.go
+++ b/internal/handlers/handler_firstfactor.go
@@ -24,7 +24,7 @@ func FirstFactorPOST(delayFunc middlewares.TimingAttackDelayFunc) middlewares.Re
bodyJSON := bodyFirstFactorRequest{}
if err := ctx.ParseBody(&bodyJSON); err != nil {
- ctx.Logger.Errorf(logFmtErrParseRequestBody, regulation.AuthType1FA, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrParseRequestBody, regulation.AuthType1FA)
respondUnauthorized(ctx, messageAuthenticationFailed)
@@ -40,7 +40,7 @@ func FirstFactorPOST(delayFunc middlewares.TimingAttackDelayFunc) middlewares.Re
return
}
- ctx.Logger.Errorf(logFmtErrRegulationFail, regulation.AuthType1FA, bodyJSON.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrRegulationFail, regulation.AuthType1FA, bodyJSON.Username)
respondUnauthorized(ctx, messageAuthenticationFailed)
@@ -92,7 +92,7 @@ func FirstFactorPOST(delayFunc middlewares.TimingAttackDelayFunc) middlewares.Re
// Reset all values from previous session except OIDC workflow before regenerating the cookie.
if err = ctx.SaveSession(newSession); err != nil {
- ctx.Logger.Errorf(logFmtErrSessionReset, regulation.AuthType1FA, bodyJSON.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionReset, regulation.AuthType1FA, bodyJSON.Username)
respondUnauthorized(ctx, messageAuthenticationFailed)
@@ -100,7 +100,7 @@ func FirstFactorPOST(delayFunc middlewares.TimingAttackDelayFunc) middlewares.Re
}
if err = ctx.RegenerateSession(); err != nil {
- ctx.Logger.Errorf(logFmtErrSessionRegenerate, regulation.AuthType1FA, bodyJSON.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionRegenerate, regulation.AuthType1FA, bodyJSON.Username)
respondUnauthorized(ctx, messageAuthenticationFailed)
@@ -114,7 +114,7 @@ func FirstFactorPOST(delayFunc middlewares.TimingAttackDelayFunc) middlewares.Re
if keepMeLoggedIn {
err = provider.UpdateExpiration(ctx.RequestCtx, provider.Config.RememberMe)
if err != nil {
- ctx.Logger.Errorf(logFmtErrSessionSave, "updated expiration", regulation.AuthType1FA, bodyJSON.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionSave, "updated expiration", regulation.AuthType1FA, bodyJSON.Username)
respondUnauthorized(ctx, messageAuthenticationFailed)
@@ -125,7 +125,7 @@ func FirstFactorPOST(delayFunc middlewares.TimingAttackDelayFunc) middlewares.Re
// Get the details of the given user from the user provider.
userDetails, err := ctx.Providers.UserProvider.GetDetails(bodyJSON.Username)
if err != nil {
- ctx.Logger.Errorf(logFmtErrObtainProfileDetails, regulation.AuthType1FA, bodyJSON.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrObtainProfileDetails, regulation.AuthType1FA, bodyJSON.Username)
respondUnauthorized(ctx, messageAuthenticationFailed)
@@ -141,7 +141,7 @@ func FirstFactorPOST(delayFunc middlewares.TimingAttackDelayFunc) middlewares.Re
}
if err = ctx.SaveSession(userSession); err != nil {
- ctx.Logger.Errorf(logFmtErrSessionSave, "updated profile", regulation.AuthType1FA, bodyJSON.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionSave, "updated profile", regulation.AuthType1FA, bodyJSON.Username)
respondUnauthorized(ctx, messageAuthenticationFailed)
diff --git a/internal/handlers/handler_firstfactor_test.go b/internal/handlers/handler_firstfactor_test.go
index fb00a51df..d7a2a1a14 100644
--- a/internal/handlers/handler_firstfactor_test.go
+++ b/internal/handlers/handler_firstfactor_test.go
@@ -6,7 +6,9 @@ import (
"testing"
"github.com/golang/mock/gomock"
+ "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/valyala/fasthttp"
@@ -36,7 +38,7 @@ func (s *FirstFactorSuite) TestShouldFailIfBodyIsNil() {
FirstFactorPOST(nil)(s.mock.Ctx)
// No body.
- assert.Equal(s.T(), "Failed to parse 1FA request body: unable to parse body: unexpected end of JSON input", s.mock.Hook.LastEntry().Message)
+ AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), "Failed to parse 1FA request body", "unable to parse body: unexpected end of JSON input")
s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
}
@@ -47,7 +49,7 @@ func (s *FirstFactorSuite) TestShouldFailIfBodyIsInBadFormat() {
}`)
FirstFactorPOST(nil)(s.mock.Ctx)
- assert.Equal(s.T(), "Failed to parse 1FA request body: unable to validate body: password: non zero value required", s.mock.Hook.LastEntry().Message)
+ AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), "Failed to parse 1FA request body", "unable to validate body: password: non zero value required")
s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
}
@@ -75,8 +77,8 @@ func (s *FirstFactorSuite) TestShouldFailIfUserProviderCheckPasswordFail() {
}`)
FirstFactorPOST(nil)(s.mock.Ctx)
- assert.Equal(s.T(), "Unsuccessful 1FA authentication attempt by user 'test'", s.mock.Hook.LastEntry().Message)
- assert.EqualError(s.T(), s.mock.Hook.LastEntry().Data["error"].(error), "failed")
+ AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), "Unsuccessful 1FA authentication attempt by user 'test'", "failed")
+
s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
}
@@ -155,7 +157,7 @@ func (s *FirstFactorSuite) TestShouldFailIfUserProviderGetDetailsFail() {
}`)
FirstFactorPOST(nil)(s.mock.Ctx)
- assert.Equal(s.T(), "Could not obtain profile details during 1FA authentication for user 'test': failed", s.mock.Hook.LastEntry().Message)
+ AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), "Could not obtain profile details during 1FA authentication for user 'test'", "failed")
s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
}
@@ -177,8 +179,7 @@ func (s *FirstFactorSuite) TestShouldFailIfAuthenticationMarkFail() {
}`)
FirstFactorPOST(nil)(s.mock.Ctx)
- assert.Equal(s.T(), "Unable to mark 1FA authentication attempt by user 'test'", s.mock.Hook.LastEntry().Message)
- assert.EqualError(s.T(), s.mock.Hook.LastEntry().Data["error"].(error), "failed")
+ AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), "Unable to mark 1FA authentication attempt by user 'test'", "failed")
s.mock.Assert401KO(s.T(), "Authentication failed. Check your credentials.")
}
@@ -463,3 +464,23 @@ func TestFirstFactorSuite(t *testing.T) {
suite.Run(t, new(FirstFactorSuite))
suite.Run(t, new(FirstFactorRedirectionSuite))
}
+
+func AssertLogEntryMessageAndError(t *testing.T, entry *logrus.Entry, message, err string) {
+ assert.Equal(t, message, entry.Message)
+
+ v, ok := entry.Data["error"]
+
+ if err == "" {
+ assert.False(t, ok)
+ assert.Nil(t, v)
+ } else {
+ assert.True(t, ok)
+ require.NotNil(t, v)
+
+ theErr, ok := v.(error)
+ assert.True(t, ok)
+ require.NotNil(t, theErr)
+
+ assert.EqualError(t, theErr, err)
+ }
+}
diff --git a/internal/handlers/handler_register_webauthn.go b/internal/handlers/handler_register_webauthn.go
index dcd534c29..8c4d08f9b 100644
--- a/internal/handlers/handler_register_webauthn.go
+++ b/internal/handlers/handler_register_webauthn.go
@@ -3,6 +3,7 @@ package handlers
import (
"bytes"
"encoding/json"
+ "errors"
"strings"
"github.com/go-webauthn/webauthn/protocol"
@@ -35,7 +36,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {
}
if w, err = newWebAuthn(ctx); err != nil {
- ctx.Logger.Errorf("Unable to create provider to generate %s registration challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to create provider to generate %s registration challenge for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@@ -43,7 +44,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {
}
if err = json.Unmarshal(ctx.PostBody(), &bodyJSON); err != nil {
- ctx.Logger.Errorf("Unable to parse %s registration request PUT data for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to parse %s registration request PUT data for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@@ -60,7 +61,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {
devices, err := ctx.Providers.StorageProvider.LoadWebAuthnCredentialsByUsername(ctx, w.Config.RPID, userSession.Username)
if err != nil && err != storage.ErrNoWebAuthnCredential {
- ctx.Logger.Errorf("Unable to load existing %s devices for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to load existing %s devices for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@@ -79,7 +80,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {
}
if user, err = getWebAuthnUserByRPID(ctx, userSession.Username, userSession.DisplayName, w.Config.RPID); err != nil {
- ctx.Logger.Errorf("Unable to load %s devices for registration challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to load %s devices for registration challenge for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@@ -101,7 +102,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {
}
if creation, data.SessionData, err = w.BeginRegistration(user, opts...); err != nil {
- ctx.Logger.Errorf("Unable to create %s registration challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to create %s registration challenge for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@@ -111,7 +112,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {
userSession.WebAuthn = &data
if err = ctx.SaveSession(userSession); err != nil {
- ctx.Logger.Errorf(logFmtErrSessionSave, "registration challenge", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionSave, "registration challenge", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@@ -119,7 +120,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {
}
if err = ctx.SetJSONBody(creation); err != nil {
- ctx.Logger.Errorf(logFmtErrWriteResponseBody, regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrWriteResponseBody, regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@@ -158,7 +159,7 @@ func WebAuthnRegistrationPOST(ctx *middlewares.AutheliaCtx) {
}
if w, err = newWebAuthn(ctx); err != nil {
- ctx.Logger.Errorf("Unable to configure %s during registration for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to configure %s during registration for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@@ -166,11 +167,13 @@ func WebAuthnRegistrationPOST(ctx *middlewares.AutheliaCtx) {
}
if response, err = protocol.ParseCredentialCreationResponseBody(bytes.NewReader(ctx.PostBody())); err != nil {
- switch e := err.(type) {
- case *protocol.Error:
- ctx.Logger.Errorf("Unable to parse %s registration for user '%s': %+v (%s)", regulation.AuthTypeWebAuthn, userSession.Username, err, e.DevInfo)
+ var e *protocol.Error
+
+ switch {
+ case errors.As(err, &e):
+ ctx.Logger.WithError(e).Errorf("Unable to parse %s registration for user '%s': %+v (%s)", regulation.AuthTypeWebAuthn, userSession.Username, err, e.DevInfo)
default:
- ctx.Logger.Errorf("Unable to parse %s registration for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to parse %s registration for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
}
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@@ -187,11 +190,13 @@ func WebAuthnRegistrationPOST(ctx *middlewares.AutheliaCtx) {
}
if credential, err = w.CreateCredential(user, *userSession.WebAuthn.SessionData, response); err != nil {
- switch e := err.(type) {
- case *protocol.Error:
- ctx.Logger.Errorf("Unable to create %s credential for user '%s': %+v (%s)", regulation.AuthTypeWebAuthn, userSession.Username, err, e.DevInfo)
+ var e *protocol.Error
+
+ switch {
+ case errors.As(err, &e):
+ ctx.Logger.WithError(e).Errorf("Unable to create %s credential for user '%s': %s", regulation.AuthTypeWebAuthn, userSession.Username, e.DevInfo)
default:
- ctx.Logger.Errorf("Unable to create %s credential for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to create %s credential for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
}
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@@ -204,7 +209,7 @@ func WebAuthnRegistrationPOST(ctx *middlewares.AutheliaCtx) {
device.Discoverable = webauthnCredentialCreationIsDiscoverable(ctx, response)
if err = ctx.Providers.StorageProvider.SaveWebAuthnCredential(ctx, device); err != nil {
- ctx.Logger.Errorf("Unable to save %s device registration for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to save %s device registration for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageUnableToRegisterSecurityKey)
@@ -214,7 +219,7 @@ func WebAuthnRegistrationPOST(ctx *middlewares.AutheliaCtx) {
userSession.WebAuthn = nil
if err = ctx.SaveSession(userSession); err != nil {
- ctx.Logger.Errorf(logFmtErrSessionSave, "removal of the registration challenge", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionSave, "removal of the registration challenge", regulation.AuthTypeWebAuthn, userSession.Username)
}
ctx.ReplyOK()
diff --git a/internal/handlers/handler_session_elevation.go b/internal/handlers/handler_session_elevation.go
index 7c74cc97a..712cd8206 100755
--- a/internal/handlers/handler_session_elevation.go
+++ b/internal/handlers/handler_session_elevation.go
@@ -315,8 +315,6 @@ func UserSessionElevateDELETE(ctx *middlewares.AutheliaCtx) {
if _, err = base64.RawURLEncoding.Decode(decoded, []byte(value)); err != nil {
ctx.Logger.WithError(err).Error("Failed to base64 decode elevation identifier during elevation revocation.")
-
- ctx.SetStatusCode(fasthttp.StatusForbidden)
ctx.SetJSONError(messageOperationFailed)
return
@@ -324,8 +322,6 @@ func UserSessionElevateDELETE(ctx *middlewares.AutheliaCtx) {
if id, err = uuid.FromBytes(decoded); err != nil {
ctx.Logger.WithError(err).Error("Failed to parse decoded elevation identifier during elevation revocation.")
-
- ctx.SetStatusCode(fasthttp.StatusForbidden)
ctx.SetJSONError(messageOperationFailed)
return
@@ -333,8 +329,6 @@ func UserSessionElevateDELETE(ctx *middlewares.AutheliaCtx) {
if otp, err = ctx.Providers.StorageProvider.LoadOneTimeCodeByPublicID(ctx, id); err != nil {
ctx.Logger.WithError(err).Error("Failed to load the elevation One-Time Code row from the database during elevation revocation.")
-
- ctx.SetStatusCode(fasthttp.StatusForbidden)
ctx.SetJSONError(messageOperationFailed)
return
@@ -342,8 +336,6 @@ func UserSessionElevateDELETE(ctx *middlewares.AutheliaCtx) {
if otp.RevokedAt.Valid {
ctx.Logger.Error("Failed to revoke the One-Time Code during elevation revocation as it's already revoked.")
-
- ctx.SetStatusCode(fasthttp.StatusForbidden)
ctx.SetJSONError(messageOperationFailed)
return
@@ -351,8 +343,6 @@ func UserSessionElevateDELETE(ctx *middlewares.AutheliaCtx) {
if otp.ConsumedAt.Valid {
ctx.Logger.Error("Failed to revoke the One-Time Code during elevation revocation as it's consumed.")
-
- ctx.SetStatusCode(fasthttp.StatusForbidden)
ctx.SetJSONError(messageOperationFailed)
return
@@ -360,8 +350,6 @@ func UserSessionElevateDELETE(ctx *middlewares.AutheliaCtx) {
if otp.Intent != model.OTCIntentUserSessionElevation {
ctx.Logger.Error("Failed to revoke the One-Time Code during elevation revocation as it doesn't have the expected intent.")
-
- ctx.SetStatusCode(fasthttp.StatusForbidden)
ctx.SetJSONError(messageOperationFailed)
return
@@ -369,8 +357,6 @@ func UserSessionElevateDELETE(ctx *middlewares.AutheliaCtx) {
if err = ctx.Providers.StorageProvider.RevokeOneTimeCode(ctx, id, model.NewIP(ctx.RemoteIP())); err != nil {
ctx.Logger.WithError(err).Error("Failed to save the revocation information to the database during elevation revocation.")
-
- ctx.SetStatusCode(fasthttp.StatusForbidden)
ctx.SetJSONError(messageOperationFailed)
return
diff --git a/internal/handlers/handler_sign_duo.go b/internal/handlers/handler_sign_duo.go
index b4cf072ee..d4b10d26a 100644
--- a/internal/handlers/handler_sign_duo.go
+++ b/internal/handlers/handler_sign_duo.go
@@ -24,7 +24,7 @@ func DuoPOST(duoAPI duo.API) middlewares.RequestHandler {
)
if err = ctx.ParseBody(bodyJSON); err != nil {
- ctx.Logger.Errorf(logFmtErrParseRequestBody, regulation.AuthTypeDuo, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrParseRequestBody, regulation.AuthTypeDuo)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -256,7 +256,7 @@ func HandleAllow(ctx *middlewares.AutheliaCtx, userSession *session.UserSession,
)
if err = ctx.RegenerateSession(); err != nil {
- ctx.Logger.Errorf(logFmtErrSessionRegenerate, regulation.AuthTypeDuo, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionRegenerate, regulation.AuthTypeDuo, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -266,7 +266,7 @@ func HandleAllow(ctx *middlewares.AutheliaCtx, userSession *session.UserSession,
userSession.SetTwoFactorDuo(ctx.Clock.Now())
if err = ctx.SaveSession(*userSession); err != nil {
- ctx.Logger.Errorf(logFmtErrSessionSave, "authentication time", regulation.AuthTypeTOTP, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionSave, "authentication time", regulation.AuthTypeTOTP, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
diff --git a/internal/handlers/handler_sign_totp.go b/internal/handlers/handler_sign_totp.go
index 38b98bd0a..ee8f275b1 100644
--- a/internal/handlers/handler_sign_totp.go
+++ b/internal/handlers/handler_sign_totp.go
@@ -60,7 +60,7 @@ func TimeBasedOneTimePasswordPOST(ctx *middlewares.AutheliaCtx) {
)
if err = ctx.ParseBody(&bodyJSON); err != nil {
- ctx.Logger.Errorf(logFmtErrParseRequestBody, regulation.AuthTypeTOTP, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrParseRequestBody, regulation.AuthTypeTOTP)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -107,7 +107,7 @@ func TimeBasedOneTimePasswordPOST(ctx *middlewares.AutheliaCtx) {
}
if err = ctx.RegenerateSession(); err != nil {
- ctx.Logger.Errorf(logFmtErrSessionRegenerate, regulation.AuthTypeTOTP, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionRegenerate, regulation.AuthTypeTOTP, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -127,7 +127,7 @@ func TimeBasedOneTimePasswordPOST(ctx *middlewares.AutheliaCtx) {
userSession.SetTwoFactorTOTP(ctx.Clock.Now())
if err = ctx.SaveSession(userSession); err != nil {
- ctx.Logger.Errorf(logFmtErrSessionSave, "authentication time", regulation.AuthTypeTOTP, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionSave, "authentication time", regulation.AuthTypeTOTP, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
diff --git a/internal/handlers/handler_sign_webauthn.go b/internal/handlers/handler_sign_webauthn.go
index 33fcf5b8b..fa6b512e9 100644
--- a/internal/handlers/handler_sign_webauthn.go
+++ b/internal/handlers/handler_sign_webauthn.go
@@ -30,7 +30,7 @@ func WebAuthnAssertionGET(ctx *middlewares.AutheliaCtx) {
}
if w, err = newWebAuthn(ctx); err != nil {
- ctx.Logger.Errorf("Unable to configure %s during authentication challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to configure %s during authentication challenge for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -38,7 +38,7 @@ func WebAuthnAssertionGET(ctx *middlewares.AutheliaCtx) {
}
if user, err = getWebAuthnUserByRPID(ctx, userSession.Username, userSession.DisplayName, w.Config.RPID); err != nil {
- ctx.Logger.Errorf("Unable to load %s user details during authentication challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to load %s user details during authentication challenge for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -65,7 +65,7 @@ func WebAuthnAssertionGET(ctx *middlewares.AutheliaCtx) {
)
if assertion, data.SessionData, err = w.BeginLogin(user, opts...); err != nil {
- ctx.Logger.Errorf("Unable to create %s authentication challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to create %s authentication challenge for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -75,7 +75,7 @@ func WebAuthnAssertionGET(ctx *middlewares.AutheliaCtx) {
userSession.WebAuthn = &data
if err = ctx.SaveSession(userSession); err != nil {
- ctx.Logger.Errorf(logFmtErrSessionSave, "assertion challenge", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionSave, "assertion challenge", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -83,7 +83,7 @@ func WebAuthnAssertionGET(ctx *middlewares.AutheliaCtx) {
}
if err = ctx.SetJSONBody(assertion); err != nil {
- ctx.Logger.Errorf(logFmtErrWriteResponseBody, regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrWriteResponseBody, regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -105,7 +105,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
)
if err = ctx.ParseBody(&bodyJSON); err != nil {
- ctx.Logger.Errorf(logFmtErrParseRequestBody, regulation.AuthTypeWebAuthn, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrParseRequestBody, regulation.AuthTypeWebAuthn)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -129,7 +129,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
}
if w, err = newWebAuthn(ctx); err != nil {
- ctx.Logger.Errorf("Unable to configure %s during authentication challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to configure %s during authentication challenge for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -143,7 +143,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
)
if assertionResponse, err = protocol.ParseCredentialRequestResponseBody(bytes.NewReader(bodyJSON.Response)); err != nil {
- ctx.Logger.Errorf("Unable to parse %s authentication challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to parse %s authentication challenge for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -151,7 +151,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
}
if user, err = getWebAuthnUserByRPID(ctx, userSession.Username, userSession.DisplayName, w.Config.RPID); err != nil {
- ctx.Logger.Errorf("Unable to load %s credentials for authentication challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to load %s credentials for authentication challenge for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -175,7 +175,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
found = true
if err = ctx.Providers.StorageProvider.UpdateWebAuthnCredentialSignIn(ctx, device); err != nil {
- ctx.Logger.Errorf("Unable to save %s device signin count for authentication challenge for user '%s': %+v", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf("Unable to save %s device signin count for authentication challenge for user '%s'", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -195,7 +195,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
}
if err = ctx.RegenerateSession(); err != nil {
- ctx.Logger.Errorf(logFmtErrSessionRegenerate, regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionRegenerate, regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
@@ -213,7 +213,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {
assertionResponse.Response.AuthenticatorData.Flags.HasUserVerified())
if err = ctx.SaveSession(userSession); err != nil {
- ctx.Logger.Errorf(logFmtErrSessionSave, "removal of the authentiation challenge and authentication time", regulation.AuthTypeWebAuthn, userSession.Username, err)
+ ctx.Logger.WithError(err).Errorf(logFmtErrSessionSave, "removal of the authentiation challenge and authentication time", regulation.AuthTypeWebAuthn, userSession.Username)
respondUnauthorized(ctx, messageMFAValidationFailed)
diff --git a/internal/storage/migrations/mysql/V0012.WebAuthnMultiCookieDomain.down.sql b/internal/storage/migrations/mysql/V0012.WebAuthnMultiCookieDomain.down.sql
index ebb0651c3..8e67ff5b9 100644
--- a/internal/storage/migrations/mysql/V0012.WebAuthnMultiCookieDomain.down.sql
+++ b/internal/storage/migrations/mysql/V0012.WebAuthnMultiCookieDomain.down.sql
@@ -9,7 +9,7 @@ CREATE TABLE IF NOT EXISTS webauthn_devices (
public_key BLOB NOT NULL,
attestation_type VARCHAR(32),
transport VARCHAR(64) DEFAULT '',
- aaguid CHAR(36) NOT NULL,
+ aaguid CHAR(36) NULL,
sign_count INTEGER DEFAULT 0,
clone_warning BOOLEAN NOT NULL DEFAULT FALSE,
UNIQUE KEY (username, description),
diff --git a/internal/storage/migrations/mysql/V0012.WebAuthnMultiCookieDomain.up.sql b/internal/storage/migrations/mysql/V0012.WebAuthnMultiCookieDomain.up.sql
index 0a0ea7317..05b846464 100644
--- a/internal/storage/migrations/mysql/V0012.WebAuthnMultiCookieDomain.up.sql
+++ b/internal/storage/migrations/mysql/V0012.WebAuthnMultiCookieDomain.up.sql
@@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS webauthn_credentials (
username VARCHAR(100) NOT NULL,
description VARCHAR(30) NOT NULL,
kid VARCHAR(512) NOT NULL,
- aaguid CHAR(36) NOT NULL,
+ aaguid CHAR(36) NULL,
attestation_type VARCHAR(32),
attachment VARCHAR(64) NOT NULL,
transport VARCHAR(64) DEFAULT '',
diff --git a/internal/storage/migrations/postgres/V0012.WebAuthnMultiCookieDomain.down.sql b/internal/storage/migrations/postgres/V0012.WebAuthnMultiCookieDomain.down.sql
index ab9264727..4c4a35611 100644
--- a/internal/storage/migrations/postgres/V0012.WebAuthnMultiCookieDomain.down.sql
+++ b/internal/storage/migrations/postgres/V0012.WebAuthnMultiCookieDomain.down.sql
@@ -9,7 +9,7 @@ CREATE TABLE IF NOT EXISTS webauthn_devices (
public_key BYTEA NOT NULL,
attestation_type VARCHAR(32),
transport VARCHAR(64) DEFAULT '',
- aaguid CHAR(36) NOT NULL,
+ aaguid CHAR(36) NULL,
sign_count INTEGER DEFAULT 0,
clone_warning BOOLEAN NOT NULL DEFAULT FALSE
);
diff --git a/internal/storage/migrations/postgres/V0012.WebAuthnMultiCookieDomain.up.sql b/internal/storage/migrations/postgres/V0012.WebAuthnMultiCookieDomain.up.sql
index 5d9fc7402..78249eece 100644
--- a/internal/storage/migrations/postgres/V0012.WebAuthnMultiCookieDomain.up.sql
+++ b/internal/storage/migrations/postgres/V0012.WebAuthnMultiCookieDomain.up.sql
@@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS webauthn_credentials (
username VARCHAR(100) NOT NULL,
description VARCHAR(64) NOT NULL,
kid VARCHAR(512) NOT NULL,
- aaguid CHAR(36) NOT NULL,
+ aaguid CHAR(36) NULL,
attestation_type VARCHAR(32),
attachment VARCHAR(64) NOT NULL,
transport VARCHAR(64) DEFAULT '',
diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml
index ed6f2050e..ee385ea5e 100644
--- a/web/pnpm-lock.yaml
+++ b/web/pnpm-lock.yaml
@@ -591,7 +591,6 @@ packages:
/@babel/parser@7.23.3:
resolution: {integrity: sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==}
engines: {node: '>=6.0.0'}
- hasBin: true
dependencies:
'@babel/types': 7.23.6
dev: true
@@ -599,7 +598,6 @@ packages:
/@babel/parser@7.23.5:
resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==}
engines: {node: '>=6.0.0'}
- hasBin: true
dependencies:
'@babel/types': 7.23.6
dev: true
@@ -1690,7 +1688,6 @@ packages:
/@commitlint/cli@19.0.3(@types/node@20.11.24)(typescript@5.3.3):
resolution: {integrity: sha512-mGhh/aYPib4Vy4h+AGRloMY+CqkmtdeKPV9poMcZeImF5e3knQ5VYaSeAM0mEzps1dbKsHvABwaDpafLUuM96g==}
engines: {node: '>=v18'}
- hasBin: true
dependencies:
'@commitlint/format': 19.0.3
'@commitlint/lint': 19.0.3
@@ -3537,7 +3534,6 @@ packages:
/JSONStream@1.3.5:
resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
- hasBin: true
dependencies:
jsonparse: 1.3.1
through: 2.3.8
@@ -3567,7 +3563,6 @@ packages:
/acorn@8.11.2:
resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==}
engines: {node: '>=0.4.0'}
- hasBin: true
dev: true
/ajv@6.12.6:
@@ -3958,7 +3953,6 @@ packages:
/browserslist@4.21.10:
resolution: {integrity: sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
- hasBin: true
dependencies:
caniuse-lite: 1.0.30001518
electron-to-chromium: 1.4.478
@@ -4175,7 +4169,6 @@ packages:
/conventional-commits-parser@5.0.0:
resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==}
engines: {node: '>=16'}
- hasBin: true
dependencies:
JSONStream: 1.3.5
is-text-path: 2.0.0
@@ -4865,7 +4858,6 @@ packages:
/esbuild@0.15.18:
resolution: {integrity: sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==}
engines: {node: '>=12'}
- hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.15.18
@@ -4926,7 +4918,6 @@ packages:
/esbuild@0.20.1:
resolution: {integrity: sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==}
engines: {node: '>=12'}
- hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/aix-ppc64': 0.20.1
@@ -4973,7 +4964,6 @@ packages:
/eslint-config-prettier@9.1.0(eslint@8.57.0):
resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==}
- hasBin: true
peerDependencies:
eslint: '>=7.0.0'
dependencies:
@@ -6123,7 +6113,6 @@ packages:
/is-docker@2.2.1:
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
engines: {node: '>=8'}
- hasBin: true
dev: true
/is-docker@3.0.0:
@@ -6387,7 +6376,6 @@ packages:
/jiti@1.20.0:
resolution: {integrity: sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==}
- hasBin: true
dev: true
/js-tokens@4.0.0:
@@ -6414,13 +6402,11 @@ packages:
/jsesc@0.5.0:
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
- hasBin: true
dev: true
/jsesc@2.5.2:
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
engines: {node: '>=4'}
- hasBin: true
dev: true
/json-parse-even-better-errors@2.3.1:
@@ -6448,7 +6434,6 @@ packages:
/json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
- hasBin: true
dev: true
/jsonc-parser@3.2.0:
@@ -6637,7 +6622,6 @@ packages:
/loose-envify@1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
- hasBin: true
dependencies:
js-tokens: 4.0.0
@@ -6793,7 +6777,6 @@ packages:
/nanoid@3.3.7:
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
- hasBin: true
dev: true
/natural-compare@1.4.0:
@@ -7183,7 +7166,6 @@ packages:
/prettier@3.2.5:
resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
engines: {node: '>=14'}
- hasBin: true
dev: true
/pretty-format@27.5.1:
@@ -7269,6 +7251,7 @@ packages:
dependencies:
loose-envify: 1.4.0
react: 18.2.0
+ scheduler: 0.23.0
/react-i18next@14.0.5(i18next@23.10.0)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-5+bQSeEtgJrMBABBL5lO7jPdSNAbeAZ+MlFWDw//7FnVacuVu3l9EeWFzBQvZsKy+cihkbThWOAThEdH8YjGEw==}
@@ -7528,7 +7511,6 @@ packages:
/rollup@2.79.1:
resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==}
engines: {node: '>=10.0.0'}
- hasBin: true
optionalDependencies:
fsevents: 2.3.3
dev: true
@@ -7536,7 +7518,6 @@ packages:
/rollup@4.4.1:
resolution: {integrity: sha512-idZzrUpWSblPJX66i+GzrpjKE3vbYrlWirUHteoAbjKReZwa0cohAErOYA5efoMmNCdvG9yrJS+w9Kl6csaH4w==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
- hasBin: true
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.4.1
'@rollup/rollup-android-arm64': 4.4.1
@@ -8149,7 +8130,6 @@ packages:
/typescript@5.3.3:
resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
engines: {node: '>=14.17'}
- hasBin: true
dev: true
/ufo@1.3.2:
@@ -8218,7 +8198,6 @@ packages:
/update-browserslist-db@1.0.11(browserslist@4.21.10):
resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==}
- hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
dependencies:
@@ -8246,7 +8225,6 @@ packages:
/vite-node@1.3.1(@types/node@20.11.24):
resolution: {integrity: sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==}
engines: {node: ^18.0.0 || >=20.0.0}
- hasBin: true
dependencies:
cac: 6.7.14
debug: 4.3.4
@@ -8365,7 +8343,6 @@ packages:
/vite@3.2.5(@types/node@18.16.5):
resolution: {integrity: sha512-4mVEpXpSOgrssFZAOmGIr85wPHKvaDAcXqxVxVRZhljkJOMZi1ibLibzjLHzJvcok8BMguLc7g1W6W/GqZbLdQ==}
engines: {node: ^14.18.0 || >=16.0.0}
- hasBin: true
peerDependencies:
'@types/node': '>= 14'
less: '*'
@@ -8399,7 +8376,6 @@ packages:
/vite@5.1.4(@types/node@20.11.24):
resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==}
engines: {node: ^18.0.0 || >=20.0.0}
- hasBin: true
peerDependencies:
'@types/node': ^18.0.0 || >=20.0.0
less: '*'
@@ -8452,7 +8428,6 @@ packages:
/vitest@1.3.1(@types/node@20.11.24)(happy-dom@13.6.2):
resolution: {integrity: sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==}
engines: {node: ^18.0.0 || >=20.0.0}
- hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@types/node': ^18.0.0 || >=20.0.0
@@ -8634,7 +8609,6 @@ packages:
/which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
- hasBin: true
dependencies:
isexe: 2.0.0
dev: true
@@ -8642,7 +8616,6 @@ packages:
/why-is-node-running@2.2.2:
resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==}
engines: {node: '>=8'}
- hasBin: true
dependencies:
siginfo: 2.0.0
stackback: 0.0.2
diff --git a/web/src/views/Settings/Common/IdentityVerificationDialog.tsx b/web/src/views/Settings/Common/IdentityVerificationDialog.tsx
index 5aebe7feb..298901888 100644
--- a/web/src/views/Settings/Common/IdentityVerificationDialog.tsx
+++ b/web/src/views/Settings/Common/IdentityVerificationDialog.tsx
@@ -105,15 +105,17 @@ const IdentityVerificationDialog = function (props: Props) {
return;
}
+ if (open) {
+ return;
+ }
+
const attempt = await generateUserSessionElevation();
if (!attempt) throw new Error("Failed to load the data.");
setCodeDelete(attempt.delete_id);
- if (!open) {
- props.handleOpened();
- setOpen(true);
- }
+ props.handleOpened();
+ setOpen(true);
}, [handleClose, open, props]);
const handleSubmit = useCallback(async () => {
diff --git a/web/src/views/Settings/TwoFactorAuthentication/OneTimePasswordRegisterDialog.tsx b/web/src/views/Settings/TwoFactorAuthentication/OneTimePasswordRegisterDialog.tsx
index 48a5c1cde..79cbb408b 100644
--- a/web/src/views/Settings/TwoFactorAuthentication/OneTimePasswordRegisterDialog.tsx
+++ b/web/src/views/Settings/TwoFactorAuthentication/OneTimePasswordRegisterDialog.tsx
@@ -385,7 +385,6 @@ const OneTimePasswordRegisterDialog = function (props: Props) {
<Fragment>
<Grid xs={12} my={2}>
<FormControlLabel
- disabled={disableAdvanced}
control={
<Switch
checked={showQRCode}
diff --git a/web/src/views/Settings/TwoFactorAuthentication/TwoFactorAuthenticationOptionsPanel.tsx b/web/src/views/Settings/TwoFactorAuthentication/TwoFactorAuthenticationOptionsPanel.tsx
index f8a95e83c..1248781e2 100644
--- a/web/src/views/Settings/TwoFactorAuthentication/TwoFactorAuthenticationOptionsPanel.tsx
+++ b/web/src/views/Settings/TwoFactorAuthentication/TwoFactorAuthenticationOptionsPanel.tsx
@@ -12,8 +12,8 @@ import { Method2FA, isMethod2FA, setPreferred2FAMethod, toMethod2FA, toSecondFac
interface Props {
refresh: () => void;
- config?: Configuration;
- info?: UserInfo;
+ config: Configuration;
+ info: UserInfo;
}
const TwoFactorAuthenticationOptionsPanel = function (props: Props) {
@@ -24,8 +24,7 @@ const TwoFactorAuthenticationOptionsPanel = function (props: Props) {
const [method, setMethod] = useState<string>();
const [methods, setMethods] = useState<string[]>([]);
- const hasMethods =
- props.info !== undefined && (props.info.has_totp || props.info.has_webauthn || props.info.has_duo);
+ const hasMethods = props.info.has_totp || props.info.has_webauthn || props.info.has_duo;
useEffect(() => {
if (props.info === undefined) return;
@@ -34,7 +33,7 @@ const TwoFactorAuthenticationOptionsPanel = function (props: Props) {
}, [props.info]);
useEffect(() => {
- if (!props.config || !hasMethods) return;
+ if (!hasMethods) return;
let valuesFinal: string[] = [];
const values = Array.from(props.config.available_methods);
@@ -45,20 +44,26 @@ const TwoFactorAuthenticationOptionsPanel = function (props: Props) {
if (!valuesFinal.includes(v)) {
switch (value) {
case SecondFactorMethod.WebAuthn:
- valuesFinal.push(v);
+ if (props.info.has_webauthn) {
+ valuesFinal.push(v);
+ }
break;
case SecondFactorMethod.TOTP:
- valuesFinal.push(v);
+ if (props.info.has_totp) {
+ valuesFinal.push(v);
+ }
break;
case SecondFactorMethod.MobilePush:
- valuesFinal.push(v);
+ if (props.info.has_duo) {
+ valuesFinal.push(v);
+ }
break;
}
}
});
setMethods(valuesFinal);
- }, [props.config, hasMethods]);
+ }, [props.config, hasMethods, props.info.has_webauthn, props.info.has_totp, props.info.has_duo]);
const handleMethodChanged = (event: ChangeEvent<HTMLInputElement>) => {
if (isMethod2FA(event.target.value)) {
diff --git a/web/src/views/Settings/TwoFactorAuthentication/TwoFactorAuthenticationView.tsx b/web/src/views/Settings/TwoFactorAuthentication/TwoFactorAuthenticationView.tsx
index 0888c1e43..787ed2b94 100755
--- a/web/src/views/Settings/TwoFactorAuthentication/TwoFactorAuthenticationView.tsx
+++ b/web/src/views/Settings/TwoFactorAuthentication/TwoFactorAuthenticationView.tsx
@@ -8,6 +8,7 @@ import { useNotifications } from "@hooks/NotificationsContext";
import { useUserInfoPOST } from "@hooks/UserInfo";
import { useUserInfoTOTPConfigurationOptional } from "@hooks/UserInfoTOTPConfiguration";
import { useUserWebAuthnCredentials } from "@hooks/WebAuthnCredentials";
+import { SecondFactorMethod } from "@models/Methods.ts";
import OneTimePasswordPanel from "@views/Settings/TwoFactorAuthentication/OneTimePasswordPanel";
import TwoFactorAuthenticationOptionsPanel from "@views/Settings/TwoFactorAuthentication/TwoFactorAuthenticationOptionsPanel";
import WebAuthnCredentialsPanel from "@views/Settings/TwoFactorAuthentication/WebAuthnCredentialsPanel";
@@ -118,20 +119,24 @@ const TwoFactorAuthenticationView = function (props: Props) {
return (
<Fragment>
<Grid container spacing={2}>
- <Grid xs={12}>
- <OneTimePasswordPanel
- info={userInfo}
- config={userTOTPConfig}
- handleRefreshState={handleRefreshTOTPState}
- />
- </Grid>
- <Grid xs={12}>
- <WebAuthnCredentialsPanel
- info={userInfo}
- credentials={userWebAuthnCredentials}
- handleRefreshState={handleRefreshWebAuthnState}
- />
- </Grid>
+ {configuration?.available_methods.has(SecondFactorMethod.TOTP) ? (
+ <Grid xs={12}>
+ <OneTimePasswordPanel
+ info={userInfo}
+ config={userTOTPConfig}
+ handleRefreshState={handleRefreshTOTPState}
+ />
+ </Grid>
+ ) : null}
+ {configuration?.available_methods.has(SecondFactorMethod.WebAuthn) ? (
+ <Grid xs={12}>
+ <WebAuthnCredentialsPanel
+ info={userInfo}
+ credentials={userWebAuthnCredentials}
+ handleRefreshState={handleRefreshWebAuthnState}
+ />
+ </Grid>
+ ) : null}
{configuration && userInfo ? (
<Grid xs={12}>
<TwoFactorAuthenticationOptionsPanel