Refactor + bug fixes

This commit is contained in:
Neeraj Gupta
2024-03-07 18:14:04 +05:30
committed by Neeraj Gupta
parent 980ab6c49c
commit 7f66714d96
7 changed files with 27 additions and 31 deletions

View File

@@ -434,7 +434,7 @@ func main() {
publicAPI.POST("/users/two-factor/passkeys/begin", userHandler.BeginPasskeyAuthenticationCeremony) publicAPI.POST("/users/two-factor/passkeys/begin", userHandler.BeginPasskeyAuthenticationCeremony)
publicAPI.POST("/users/two-factor/passkeys/finish", userHandler.FinishPasskeyAuthenticationCeremony) publicAPI.POST("/users/two-factor/passkeys/finish", userHandler.FinishPasskeyAuthenticationCeremony)
privateAPI.GET("/users/two-factor/recovery-status", userHandler.GetTwoFactorRecoveryStatus) privateAPI.GET("/users/two-factor/recovery-status", userHandler.GetTwoFactorRecoveryStatus)
privateAPI.POST("/users/two-factor/passkeys/set-skip-challenge", userHandler.ConfigurePassKeySkipChallenge) privateAPI.POST("/users/two-factor/passkeys/configure-reset", userHandler.ConfigurePassKeySkipChallenge)
privateAPI.GET("/users/two-factor/status", userHandler.GetTwoFactorStatus) privateAPI.GET("/users/two-factor/status", userHandler.GetTwoFactorStatus)
privateAPI.POST("/users/two-factor/setup", userHandler.SetupTwoFactor) privateAPI.POST("/users/two-factor/setup", userHandler.SetupTwoFactor)
privateAPI.POST("/users/two-factor/enable", userHandler.EnableTwoFactor) privateAPI.POST("/users/two-factor/enable", userHandler.EnableTwoFactor)

View File

@@ -13,8 +13,8 @@ type Passkey struct {
var MaxPasskeys = 10 var MaxPasskeys = 10
type ConfigurePassKeyRecoveryRequest struct { type SetPassKeyRecoveryRequest struct {
SkipSecret string `json:"resetSecret" binding:"required"` Secret uuid.UUID `json:"secret" binding:"required"`
// The UserSecretCipher has SkipSecret encrypted with the user's recoveryKey // The UserSecretCipher has SkipSecret encrypted with the user's recoveryKey
// If the user sends the correct UserSecretCipher, we can be sure that the user has the recoveryKey, // If the user sends the correct UserSecretCipher, we can be sure that the user has the recoveryKey,
// and we can allow the user to recover their MFA. // and we can allow the user to recover their MFA.
@@ -25,11 +25,6 @@ type ConfigurePassKeyRecoveryRequest struct {
type TwoFactorRecoveryStatus struct { type TwoFactorRecoveryStatus struct {
// AllowAdminReset is a boolean that determines if the admin can reset the user's MFA. // AllowAdminReset is a boolean that determines if the admin can reset the user's MFA.
// If true, in the event that the user loses their MFA device, the admin can reset the user's MFA. // If true, in the event that the user loses their MFA device, the admin can reset the user's MFA.
AllowAdminReset bool `json:"allowAdminReset" binding:"required"` AllowAdminReset bool `json:"allowAdminReset" binding:"required"`
IsPassKeySkipEnabled bool `json:"isPassKeyResetEnabled" binding:"required"` IsPassKeyRecoveryEnabled bool `json:"isPassKeyRecoveryEnabled" binding:"required"`
}
type SkipPassKeyRequest struct {
SessionID string `json:"sessionID" binding:"required"`
SkipSecret string `json:"resetSecret" binding:"required"`
} }

View File

@@ -1,5 +1,5 @@
CREATE TABLE IF NOT EXISTS two_factor_recovery ( CREATE TABLE IF NOT EXISTS two_factor_recovery (
user_id bigint NOT NULL, user_id bigint NOT NULL PRIMARY KEY,
-- if false, the support team team will not be able to reset the MFA for the user -- if false, the support team team will not be able to reset the MFA for the user
enable_admin_mfa_reset boolean NOT NULL DEFAULT true, enable_admin_mfa_reset boolean NOT NULL DEFAULT true,
server_passkey_secret_data bytea, server_passkey_secret_data bytea,

View File

@@ -256,7 +256,7 @@ func (h *UserHandler) GetTwoFactorRecoveryStatus(c *gin.Context) {
// ConfigurePassKeySkipChallenge configures the passkey skip challenge for a user. In case the user does not // ConfigurePassKeySkipChallenge configures the passkey skip challenge for a user. In case the user does not
// have access to passkey, the user can bypass the passkey by providing the recovery key // have access to passkey, the user can bypass the passkey by providing the recovery key
func (h *UserHandler) ConfigurePassKeySkipChallenge(c *gin.Context) { func (h *UserHandler) ConfigurePassKeySkipChallenge(c *gin.Context) {
var request ente.ConfigurePassKeyRecoveryRequest var request ente.SetPassKeyRecoveryRequest
if err := c.ShouldBindJSON(&request); err != nil { if err := c.ShouldBindJSON(&request); err != nil {
handler.Error(c, stacktrace.Propagate(err, "")) handler.Error(c, stacktrace.Propagate(err, ""))
return return
@@ -402,10 +402,10 @@ func (h *UserHandler) DisableTwoFactor(c *gin.Context) {
// recoveryKeyEncryptedTwoFactorSecret for the user to decrypt it and make twoFactor removal api call // recoveryKeyEncryptedTwoFactorSecret for the user to decrypt it and make twoFactor removal api call
func (h *UserHandler) RecoverTwoFactor(c *gin.Context) { func (h *UserHandler) RecoverTwoFactor(c *gin.Context) {
sessionID := c.Query("sessionID") sessionID := c.Query("sessionID")
twoFactorType := c.Query("type") twoFactorType := c.Query("twoFactorType")
var response *ente.TwoFactorRecoveryResponse var response *ente.TwoFactorRecoveryResponse
var err error var err error
if twoFactorType == "passkey" { if twoFactorType == "passKey" {
response, err = h.UserController.GetPasskeyRecoveryResponse(c, sessionID) response, err = h.UserController.GetPasskeyRecoveryResponse(c, sessionID)
} else { } else {
response, err = h.UserController.RecoverTwoFactor(sessionID) response, err = h.UserController.RecoverTwoFactor(sessionID)
@@ -427,10 +427,10 @@ func (h *UserHandler) RemoveTwoFactor(c *gin.Context) {
} }
var response *ente.TwoFactorAuthorizationResponse var response *ente.TwoFactorAuthorizationResponse
var err error var err error
if request.TwoFactorType == "passkey" { if request.TwoFactorType == "passKey" {
response, err = h.UserController.SkipPassKey(c, &request) response, err = h.UserController.SkipPasskeyVerification(c, &request)
} else { } else {
response, err = h.UserController.RemoveTwoFactor(c, request.SessionID, request.Secret) response, err = h.UserController.RemoveTOTPTwoFactor(c, request.SessionID, request.Secret)
} }
if err != nil { if err != nil {
handler.Error(c, stacktrace.Propagate(err, "")) handler.Error(c, stacktrace.Propagate(err, ""))

View File

@@ -13,9 +13,9 @@ func (c *UserController) GetTwoFactorRecoveryStatus(ctx *gin.Context) (*ente.Two
return c.TwoFactorRecoveryRepo.GetStatus(userID) return c.TwoFactorRecoveryRepo.GetStatus(userID)
} }
func (c *UserController) ConfigurePassKeySkip(ctx *gin.Context, req *ente.ConfigurePassKeyRecoveryRequest) error { func (c *UserController) ConfigurePassKeySkip(ctx *gin.Context, req *ente.SetPassKeyRecoveryRequest) error {
userID := auth.GetUserID(ctx.Request.Header) userID := auth.GetUserID(ctx.Request.Header)
return c.TwoFactorRecoveryRepo.ConfigurePassKeySkipChallenge(ctx, userID, req) return c.TwoFactorRecoveryRepo.SetPassKeyRecovery(ctx, userID, req)
} }
func (c *UserController) GetPasskeyRecoveryResponse(ctx *gin.Context, passKeySessionID string) (*ente.TwoFactorRecoveryResponse, error) { func (c *UserController) GetPasskeyRecoveryResponse(ctx *gin.Context, passKeySessionID string) (*ente.TwoFactorRecoveryResponse, error) {
@@ -27,7 +27,7 @@ func (c *UserController) GetPasskeyRecoveryResponse(ctx *gin.Context, passKeySes
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !recoveryStatus.IsPassKeySkipEnabled { if !recoveryStatus.IsPassKeyRecoveryEnabled {
return nil, ente.NewBadRequestWithMessage("Passkey reset is not configured") return nil, ente.NewBadRequestWithMessage("Passkey reset is not configured")
} }
@@ -41,7 +41,7 @@ func (c *UserController) GetPasskeyRecoveryResponse(ctx *gin.Context, passKeySes
return result, nil return result, nil
} }
func (c *UserController) SkipPassKey(context *gin.Context, req *ente.TwoFactorRemovalRequest) (*ente.TwoFactorAuthorizationResponse, error) { func (c *UserController) SkipPasskeyVerification(context *gin.Context, req *ente.TwoFactorRemovalRequest) (*ente.TwoFactorAuthorizationResponse, error) {
userID, err := c.PasskeyRepo.GetUserIDWithPasskeyTwoFactorSession(req.SessionID) userID, err := c.PasskeyRepo.GetUserIDWithPasskeyTwoFactorSession(req.SessionID)
if err != nil { if err != nil {
return nil, stacktrace.Propagate(err, "") return nil, stacktrace.Propagate(err, "")

View File

@@ -143,9 +143,9 @@ func (c *UserController) RecoverTwoFactor(sessionID string) (*ente.TwoFactorReco
return &response, nil return &response, nil
} }
// RemoveTwoFactor handles two factor deactivation request if user lost his device // RemoveTOTPTwoFactor handles two factor deactivation request if user lost his device
// by authenticating him using his twoFactorsessionToken and twoFactor secret // by authenticating him using his twoFactorsessionToken and twoFactor secret
func (c *UserController) RemoveTwoFactor(context *gin.Context, sessionID string, secret string) (*ente.TwoFactorAuthorizationResponse, error) { func (c *UserController) RemoveTOTPTwoFactor(context *gin.Context, sessionID string, secret string) (*ente.TwoFactorAuthorizationResponse, error) {
userID, err := c.TwoFactorRepo.GetUserIDWithTwoFactorSession(sessionID) userID, err := c.TwoFactorRepo.GetUserIDWithTwoFactorSession(sessionID)
if err != nil { if err != nil {
return nil, stacktrace.Propagate(err, "") return nil, stacktrace.Propagate(err, "")

View File

@@ -25,24 +25,25 @@ func (r *Repository) GetStatus(userID int64) (*ente.TwoFactorRecoveryStatus, err
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
// by default, admin // by default, admin
return &ente.TwoFactorRecoveryStatus{ return &ente.TwoFactorRecoveryStatus{
AllowAdminReset: true, AllowAdminReset: true,
IsPassKeySkipEnabled: false, IsPassKeyRecoveryEnabled: false,
}, nil }, nil
} }
return nil, err return nil, err
} }
return &ente.TwoFactorRecoveryStatus{AllowAdminReset: isAdminResetEnabled, IsPassKeySkipEnabled: resetKey.Valid}, nil return &ente.TwoFactorRecoveryStatus{AllowAdminReset: isAdminResetEnabled, IsPassKeyRecoveryEnabled: resetKey.Valid}, nil
} }
func (r *Repository) ConfigurePassKeySkipChallenge(ctx context.Context, userID int64, req *ente.ConfigurePassKeyRecoveryRequest) error { func (r *Repository) SetPassKeyRecovery(ctx context.Context, userID int64, req *ente.SetPassKeyRecoveryRequest) error {
serveEncPassKey, encRrr := crypto.Encrypt(req.SkipSecret, r.SecretEncryptionKey) serveEncPassKey, encRrr := crypto.Encrypt(req.Secret.String(), r.SecretEncryptionKey)
if encRrr != nil { if encRrr != nil {
return stacktrace.Propagate(encRrr, "failed to encrypt passkey secret") return stacktrace.Propagate(encRrr, "failed to encrypt passkey secret")
} }
_, err := r.Db.ExecContext(ctx, `INSERT INTO two_factor_recovery _, err := r.Db.ExecContext(ctx, `INSERT INTO two_factor_recovery
(user_id, server_passkey_secret_data, server_passkey_secret_nonce, user_passkey_secret_data, user_passkey_secret_nonce)) (user_id, server_passkey_secret_data, server_passkey_secret_nonce, user_passkey_secret_data, user_passkey_secret_nonce)
VALUES ($1, $2,$3,$4,$5) ON CONFLICT (user_id) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (user_id)
DO UPDATE SET server_passkey_secret_data = $2, server_passkey_secret_nonce = $3, user_passkey_secret_data=$4,user_passkey_secret_nonce=$5`, DO UPDATE SET server_passkey_secret_data = $2, server_passkey_secret_nonce = $3, user_passkey_secret_data = $4, user_passkey_secret_nonce = $5
WHERE two_factor_recovery.user_passkey_secret_data IS NULL AND two_factor_recovery.server_passkey_secret_data IS NULL`,
userID, serveEncPassKey.Cipher, serveEncPassKey.Nonce, req.UserSecretCipher, req.UserSecretNonce) userID, serveEncPassKey.Cipher, serveEncPassKey.Nonce, req.UserSecretCipher, req.UserSecretNonce)
return err return err
} }