Skip to content

Commit

Permalink
fix(web): show appropriate default and available methods (#2999)
Browse files Browse the repository at this point in the history
This ensures that; the method set when a user does not have a preference is a method that is available, that if a user has a preferred method that is not available it is changed to an enabled method with preference put on methods the user has configured, that the frontend does not show the method selection option when only one method is available.
  • Loading branch information
james-d-elliott committed Mar 28, 2022
1 parent 2d8978c commit 70ab8aa
Show file tree
Hide file tree
Showing 16 changed files with 632 additions and 66 deletions.
18 changes: 18 additions & 0 deletions api/openapi.yml
Expand Up @@ -317,6 +317,24 @@ paths:
description: Forbidden
security:
- authelia_auth: []
post:
tags:
- User Information
summary: User Configuration
description: >
The user info endpoint provides detailed information including a users display name, preferred and registered
second factor method(s). The POST method also ensures the preferred method is configured correctly.
responses:
"200":
description: Successful Operation
content:
application/json:
schema:
$ref: '#/components/schemas/handlers.UserInfo'
"403":
description: Forbidden
security:
- authelia_auth: []
/api/user/info/totp:
get:
tags:
Expand Down
12 changes: 0 additions & 12 deletions internal/authentication/const.go
Expand Up @@ -16,15 +16,6 @@ const (
TwoFactor Level = iota
)

const (
// TOTP Method using Time-Based One-Time Password applications like Google Authenticator.
TOTP = "totp"
// Webauthn Method using Webauthn devices like YubiKeys.
Webauthn = "webauthn"
// Push Method using Duo application to receive push notifications.
Push = "mobile_push"
)

const (
ldapSupportedExtensionAttribute = "supportedExtension"
ldapOIDPasswdModifyExtension = "1.3.6.1.4.1.4203.1.11.1" // http://oidref.com/1.3.6.1.4.1.4203.1.11.1
Expand All @@ -36,9 +27,6 @@ const (
ldapPlaceholderUsername = "{username}"
)

// PossibleMethods is the set of all possible 2FA methods.
var PossibleMethods = []string{TOTP, Webauthn, Push}

// CryptAlgo the crypt representation of an algorithm used in the prefix of the hash.
type CryptAlgo string

Expand Down
13 changes: 1 addition & 12 deletions internal/handlers/handler_configuration.go
@@ -1,7 +1,6 @@
package handlers

import (
"github.com/authelia/authelia/v4/internal/authentication"
"github.com/authelia/authelia/v4/internal/middlewares"
)

Expand All @@ -12,17 +11,7 @@ func ConfigurationGet(ctx *middlewares.AutheliaCtx) {
}

if ctx.Providers.Authorizer.IsSecondFactorEnabled() {
if !ctx.Configuration.TOTP.Disable {
body.AvailableMethods = append(body.AvailableMethods, authentication.TOTP)
}

if !ctx.Configuration.Webauthn.Disable {
body.AvailableMethods = append(body.AvailableMethods, authentication.Webauthn)
}

if ctx.Configuration.DuoAPI != nil {
body.AvailableMethods = append(body.AvailableMethods, authentication.Push)
}
body.AvailableMethods = ctx.AvailableSecondFactorMethods()
}

ctx.Logger.Tracef("Available methods are %s", body.AvailableMethods)
Expand Down
55 changes: 50 additions & 5 deletions internal/handlers/handler_user_info.go
@@ -1,16 +1,61 @@
package handlers

import (
"database/sql"
"errors"
"fmt"
"strings"

"github.com/authelia/authelia/v4/internal/authentication"
"github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/model"
"github.com/authelia/authelia/v4/internal/utils"
)

// UserInfoGet get the info related to the user identified by the session.
func UserInfoGet(ctx *middlewares.AutheliaCtx) {
// UserInfoPOST handles setting up info for users if necessary when they login.
func UserInfoPOST(ctx *middlewares.AutheliaCtx) {
userSession := ctx.GetSession()

var (
userInfo model.UserInfo
err error
)

if _, err = ctx.Providers.StorageProvider.LoadPreferred2FAMethod(ctx, userSession.Username); err != nil {
if errors.Is(err, sql.ErrNoRows) {
if err = ctx.Providers.StorageProvider.SavePreferred2FAMethod(ctx, userSession.Username, ""); err != nil {
ctx.Error(fmt.Errorf("unable to load user information: %v", err), messageOperationFailed)
}
} else {
ctx.Error(fmt.Errorf("unable to load user information: %v", err), messageOperationFailed)
}
}

if userInfo, err = ctx.Providers.StorageProvider.LoadUserInfo(ctx, userSession.Username); err != nil {
ctx.Error(fmt.Errorf("unable to load user information: %v", err), messageOperationFailed)
return
}

var (
changed bool
)

if changed = userInfo.SetDefaultPreferred2FAMethod(ctx.AvailableSecondFactorMethods()); changed {
if err = ctx.Providers.StorageProvider.SavePreferred2FAMethod(ctx, userSession.Username, userInfo.Method); err != nil {
ctx.Error(fmt.Errorf("unable to save user two factor method: %v", err), messageOperationFailed)
return
}
}

userInfo.DisplayName = userSession.DisplayName

err = ctx.SetJSONBody(userInfo)
if err != nil {
ctx.Logger.Errorf("Unable to set user info response in body: %s", err)
}
}

// UserInfoGET get the info related to the user identified by the session.
func UserInfoGET(ctx *middlewares.AutheliaCtx) {
userSession := ctx.GetSession()

userInfo, err := ctx.Providers.StorageProvider.LoadUserInfo(ctx, userSession.Username)
Expand All @@ -37,8 +82,8 @@ func MethodPreferencePost(ctx *middlewares.AutheliaCtx) {
return
}

if !utils.IsStringInSlice(bodyJSON.Method, authentication.PossibleMethods) {
ctx.Error(fmt.Errorf("unknown method '%s', it should be one of %s", bodyJSON.Method, strings.Join(authentication.PossibleMethods, ", ")), messageOperationFailed)
if !utils.IsStringInSlice(bodyJSON.Method, ctx.AvailableSecondFactorMethods()) {
ctx.Error(fmt.Errorf("unknown or unavailable method '%s', it should be one of %s", bodyJSON.Method, strings.Join(ctx.AvailableSecondFactorMethods(), ", ")), messageOperationFailed)
return
}

Expand Down

0 comments on commit 70ab8aa

Please sign in to comment.