From 7d4c6412972e4c88be0af326c1488108e7060869 Mon Sep 17 00:00:00 2001 From: lemonScaletech Date: Fri, 24 Nov 2023 13:52:17 +0530 Subject: [PATCH 1/3] fix: * removed fmt.Println --- server/env/env.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/env/env.go b/server/env/env.go index 2aa08b3b7..bd713c336 100644 --- a/server/env/env.go +++ b/server/env/env.go @@ -848,7 +848,6 @@ func InitAllEnv() error { envData[constants.EnvKeyDisableTOTPLogin] = boolValue } } - fmt.Println("=> final value", envData[constants.EnvKeyDisableTOTPLogin]) if _, ok := envData[constants.EnvKeyDisableMailOTPLogin]; !ok { envData[constants.EnvKeyDisableMailOTPLogin] = osDisableMailOTPLogin == "true" From 7f0bb360c57dc51be869eb9841c030729dbda645 Mon Sep 17 00:00:00 2001 From: scaletech-milan Date: Thu, 11 Jan 2024 19:37:02 +0530 Subject: [PATCH 2/3] Feat: - Introduce TOTP reset logic and redirect to scanner image - Function for Update TOTP info into db - Add string parser helper function --- server/authenticators/providers/providers.go | 8 ++- server/authenticators/providers/totp/totp.go | 60 ++++++++++++++++ server/resolvers/verify_otp.go | 74 ++++++++++++++++++++ server/utils/parser.go | 17 +++++ 4 files changed, 158 insertions(+), 1 deletion(-) diff --git a/server/authenticators/providers/providers.go b/server/authenticators/providers/providers.go index 7f43ef5c7..7c33ca01e 100644 --- a/server/authenticators/providers/providers.go +++ b/server/authenticators/providers/providers.go @@ -1,6 +1,10 @@ package providers -import "context" +import ( + "context" + + "github.com/authorizerdev/authorizer/server/graph/model" +) // AuthenticatorConfig defines authenticator config type AuthenticatorConfig struct { @@ -22,4 +26,6 @@ type Provider interface { Validate(ctx context.Context, passcode string, userID string) (bool, error) // ValidateRecoveryCode totp: allows user to validate using recovery code incase if they lost their device ValidateRecoveryCode(ctx context.Context, recoveryCode, userID string) (bool, error) + // UpdateTotpInfo: to update secret and recovery codes into db and returns base64 of QR code image + UpdateTotpInfo(ctx context.Context, id string) (*model.AuthResponse, error) } diff --git a/server/authenticators/providers/totp/totp.go b/server/authenticators/providers/totp/totp.go index b02fe293e..00baa3cdb 100644 --- a/server/authenticators/providers/totp/totp.go +++ b/server/authenticators/providers/totp/totp.go @@ -10,6 +10,7 @@ import ( "github.com/google/uuid" "github.com/pquerna/otp/totp" + log "github.com/sirupsen/logrus" "github.com/authorizerdev/authorizer/server/authenticators/providers" @@ -17,7 +18,9 @@ import ( "github.com/authorizerdev/authorizer/server/crypto" "github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db/models" + "github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/refs" + "github.com/authorizerdev/authorizer/server/utils" ) // Generate generates a Time-Based One-Time Password (TOTP) for a user and returns the base64-encoded QR code for frontend display. @@ -149,3 +152,60 @@ func (p *provider) ValidateRecoveryCode(ctx context.Context, recoveryCode, userI } return true, nil } + +// UpdateTotpInfo generates a Time-Based One-Time Password (TOTP) for a user, +// updates the user's authenticator details, and returns the base64-encoded QR code for frontend display. +func (p *provider) UpdateTotpInfo(ctx context.Context, id string) (*model.AuthResponse, error) { + // Buffer to store the base64-encoded QR code image + var buf bytes.Buffer + + // Retrieve user details from the database + user, err := db.Provider.GetUserByID(ctx, id) + if err != nil { + return nil, err + } + // Generate TOTP, Authenticators hash is valid for 30 seconds + key, err := totp.Generate(totp.GenerateOpts{ + Issuer: "authorizer", + AccountName: refs.StringValue(user.Email), + }) + if err != nil { + return nil, err + } + + // Generate image for the TOTP key and encode it to base64 for frontend display + img, err := key.Image(200, 200) + if err != nil { + return nil, err + } + + // Encode the QR code image to base64 + png.Encode(&buf, img) + encodedText := crypto.EncryptB64(buf.String()) + + // Update the authenticator record with the new TOTP secret + secret := key.Secret() + + // Retrieve an authenticator details for the user + authenticator, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, user.ID, constants.EnvKeyTOTPAuthenticator) + if err != nil { + log.Debug("Failed to get authenticator details by user id, creating new record: ", err) + return nil, err + } + + // Update the authenticator record with the new TOTP secret + authenticator.Secret = secret + + // Update the authenticator record in the database + _, err = db.Provider.UpdateAuthenticator(ctx, authenticator) + if err != nil { + return nil, err + } + + // Return the response with base64-encoded QR code, TOTP secret, and recovery codes + return &model.AuthResponse{ + AuthenticatorScannerImage: &encodedText, + AuthenticatorSecret: &secret, + AuthenticatorRecoveryCodes: utils.ParseStringArray(*authenticator.RecoveryCodes), + }, nil +} diff --git a/server/resolvers/verify_otp.go b/server/resolvers/verify_otp.go index 16a10c707..0bdc99aab 100644 --- a/server/resolvers/verify_otp.go +++ b/server/resolvers/verify_otp.go @@ -80,6 +80,80 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod log.Debug("Failed to verify otp request: Incorrect value") return res, fmt.Errorf(`invalid otp`) } + + // Redirect to TOTP scanner image screen when the user validates through a recovery code + { + // Update totp info into db + { + // Get TOTP details for the user + totpModel, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, user.ID, constants.EnvKeyTOTPAuthenticator) + if err != nil { + return nil, err + } + + // Clear TOTP secret from the TOTP model + totpModel.Secret = "" + + // Reset recovery code and TOTP secret in the database + _, err = db.Provider.UpdateAuthenticator(ctx, totpModel) + if err != nil { + return nil, err + } + } + + // Redirect to TOTP scanner image screen by resetting TOTP secret and updating a recovery codes + { + // Function to set OTP MFA session + setOTPMFaSession := func(expiresAt int64) error { + // Generate a new MFA session ID + mfaSession := uuid.NewString() + + // Store the MFA session in the memory store + err = memorystore.Provider.SetMfaSession(user.ID, mfaSession, expiresAt) + if err != nil { + log.Debug("Failed to add mfasession: ", err) + return err + } + + // Set the MFA session ID in a cookie + cookie.SetMfaSession(gc, mfaSession) + return nil + } + + // Calculate the expiration time for the TOTP information + expiresAt := time.Now().Add(3 * time.Minute).Unix() + + // Set the OTP MFA session + if err := setOTPMFaSession(expiresAt); err != nil { + log.Debug("Failed to set mfa session: ", err) + return nil, err + } + + // Retrieve TOTP details again after updating the session + authenticator, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, user.ID, constants.EnvKeyTOTPAuthenticator) + + // Check for an error or an empty TOTP secret in the authenticator details + if err != nil || authenticator.Secret == "" { + // If there's an error or the TOTP secret is empty, initiate TOTP information update + authConfig, err := authenticators.Provider.UpdateTotpInfo(ctx, user.ID) + if err != nil { + log.Debug("error while generating base64 url: ", err) + return nil, err + } + + // Response for the case when the user validate through TOTP recovery codes + res = &model.AuthResponse{ + Message: `Proceed to totp verification screen`, + ShouldShowTotpScreen: refs.NewBoolRef(true), + AuthenticatorScannerImage: authConfig.AuthenticatorScannerImage, + AuthenticatorSecret: authConfig.AuthenticatorSecret, + AuthenticatorRecoveryCodes: authConfig.AuthenticatorRecoveryCodes, + } + + return res, nil + } + } + } } } else { var otp *models.OTP diff --git a/server/utils/parser.go b/server/utils/parser.go index 1b037c097..ba0031372 100644 --- a/server/utils/parser.go +++ b/server/utils/parser.go @@ -2,6 +2,7 @@ package utils import ( "errors" + "strings" "time" ) @@ -19,3 +20,19 @@ func ParseDurationInSeconds(s string) (time.Duration, error) { return d, nil } + +// Helper function to parse string array values +func ParseStringArray(value string) []*string { + if value == "" { + return nil + } + splitValues := strings.Split(value, "|") + + var result []*string + for _, s := range splitValues { + temp := s + result = append(result, &temp) + } + + return result +} From 06091f7630d063abdd6824d6384588f11dcd7706 Mon Sep 17 00:00:00 2001 From: scaletech-milan Date: Fri, 12 Jan 2024 16:07:32 +0530 Subject: [PATCH 3/3] Feat: - Add function for parse refrence string array - Change the response of update totp info function --- server/authenticators/providers/providers.go | 4 +-- server/authenticators/providers/totp/totp.go | 11 ++++---- server/resolvers/verify_otp.go | 11 ++++++-- server/utils/parser.go | 29 ++++++++++++++++++++ 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/server/authenticators/providers/providers.go b/server/authenticators/providers/providers.go index 7c33ca01e..9c11d49c6 100644 --- a/server/authenticators/providers/providers.go +++ b/server/authenticators/providers/providers.go @@ -2,8 +2,6 @@ package providers import ( "context" - - "github.com/authorizerdev/authorizer/server/graph/model" ) // AuthenticatorConfig defines authenticator config @@ -27,5 +25,5 @@ type Provider interface { // ValidateRecoveryCode totp: allows user to validate using recovery code incase if they lost their device ValidateRecoveryCode(ctx context.Context, recoveryCode, userID string) (bool, error) // UpdateTotpInfo: to update secret and recovery codes into db and returns base64 of QR code image - UpdateTotpInfo(ctx context.Context, id string) (*model.AuthResponse, error) + UpdateTotpInfo(ctx context.Context, id string) (*AuthenticatorConfig, error) } diff --git a/server/authenticators/providers/totp/totp.go b/server/authenticators/providers/totp/totp.go index 00baa3cdb..2d2ce99d8 100644 --- a/server/authenticators/providers/totp/totp.go +++ b/server/authenticators/providers/totp/totp.go @@ -18,7 +18,6 @@ import ( "github.com/authorizerdev/authorizer/server/crypto" "github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db/models" - "github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/utils" ) @@ -155,7 +154,7 @@ func (p *provider) ValidateRecoveryCode(ctx context.Context, recoveryCode, userI // UpdateTotpInfo generates a Time-Based One-Time Password (TOTP) for a user, // updates the user's authenticator details, and returns the base64-encoded QR code for frontend display. -func (p *provider) UpdateTotpInfo(ctx context.Context, id string) (*model.AuthResponse, error) { +func (p *provider) UpdateTotpInfo(ctx context.Context, id string) (*providers.AuthenticatorConfig, error) { // Buffer to store the base64-encoded QR code image var buf bytes.Buffer @@ -203,9 +202,9 @@ func (p *provider) UpdateTotpInfo(ctx context.Context, id string) (*model.AuthRe } // Return the response with base64-encoded QR code, TOTP secret, and recovery codes - return &model.AuthResponse{ - AuthenticatorScannerImage: &encodedText, - AuthenticatorSecret: &secret, - AuthenticatorRecoveryCodes: utils.ParseStringArray(*authenticator.RecoveryCodes), + return &providers.AuthenticatorConfig{ + ScannerImage: encodedText, + Secret: secret, + RecoveryCodes: utils.ParseReferenceStringArray(authenticator.RecoveryCodes), }, nil } diff --git a/server/resolvers/verify_otp.go b/server/resolvers/verify_otp.go index 0bdc99aab..66f3b09e2 100644 --- a/server/resolvers/verify_otp.go +++ b/server/resolvers/verify_otp.go @@ -141,13 +141,18 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod return nil, err } + recoveryCodes := []*string{} + for _, code := range authConfig.RecoveryCodes { + recoveryCodes = append(recoveryCodes, refs.NewStringRef(code)) + } + // Response for the case when the user validate through TOTP recovery codes res = &model.AuthResponse{ Message: `Proceed to totp verification screen`, ShouldShowTotpScreen: refs.NewBoolRef(true), - AuthenticatorScannerImage: authConfig.AuthenticatorScannerImage, - AuthenticatorSecret: authConfig.AuthenticatorSecret, - AuthenticatorRecoveryCodes: authConfig.AuthenticatorRecoveryCodes, + AuthenticatorScannerImage: &authConfig.ScannerImage, + AuthenticatorSecret: &authConfig.Secret, + AuthenticatorRecoveryCodes: recoveryCodes, } return res, nil diff --git a/server/utils/parser.go b/server/utils/parser.go index ba0031372..a7dbd5043 100644 --- a/server/utils/parser.go +++ b/server/utils/parser.go @@ -36,3 +36,32 @@ func ParseStringArray(value string) []*string { return result } + +// Helper function to parse reference string array values +func ParseReferenceStringArray(value *string) []string { + if value == nil { + return nil + } + + // Dereference the pointer to get the string value + strValue := *value + + // Remove JSON brackets + strValue = strings.Trim(strValue, "{}") + + splitValues := strings.Split(strValue, ",") + + var result []string + for _, s := range splitValues { + // Split each key-value pair by colon ':' + parts := strings.SplitN(s, ":", 2) + if len(parts) > 0 { + unquoted := strings.Trim(strings.TrimSpace(parts[0]), `"`) + + // Extract and append only the key (UUID) + result = append(result, unquoted) + } + } + + return result +}