(ctx context.Context, user *store.UserMessage, recoveryCode string)
| 856 | } |
| 857 | |
| 858 | func (s *AuthService) challengeRecoveryCode(ctx context.Context, user *store.UserMessage, recoveryCode string) error { |
| 859 | for i, code := range user.MFAConfig.RecoveryCodes { |
| 860 | if subtle.ConstantTimeCompare([]byte(code), []byte(recoveryCode)) == 1 { |
| 861 | // If the recovery code is valid, delete it from the user's recovery code list. |
| 862 | user.MFAConfig.RecoveryCodes = slices.Delete(user.MFAConfig.RecoveryCodes, i, i+1) |
| 863 | _, err := s.store.UpdateUser(ctx, user, &store.UpdateUserMessage{ |
| 864 | MFAConfig: &storepb.MFAConfig{ |
| 865 | OtpSecret: user.MFAConfig.OtpSecret, |
| 866 | RecoveryCodes: user.MFAConfig.RecoveryCodes, |
| 867 | }, |
| 868 | }) |
| 869 | if err != nil { |
| 870 | return connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to update user")) |
| 871 | } |
| 872 | return nil |
| 873 | } |
| 874 | } |
| 875 | return connect.NewError(connect.CodeUnauthenticated, errors.Errorf(errMsgInvalidRecoveryCode)) |
| 876 | } |
| 877 | |
| 878 | // validateWithCodeAndSecret validates the given code against the given secret. |
| 879 | func validateWithCodeAndSecret(code, secret string) bool { |
no test coverage detected