(w http.ResponseWriter, r *http.Request, params *VerifyFactorParams)
| 592 | } |
| 593 | |
| 594 | func (a *API) verifyTOTPFactor(w http.ResponseWriter, r *http.Request, params *VerifyFactorParams) error { |
| 595 | var err error |
| 596 | ctx := r.Context() |
| 597 | user := getUser(ctx) |
| 598 | factor := getFactor(ctx) |
| 599 | config := a.config |
| 600 | db := a.db.WithContext(ctx) |
| 601 | |
| 602 | challenge, err := a.validateChallenge(r, db, factor, params.ChallengeID) |
| 603 | if err != nil { |
| 604 | return err |
| 605 | } |
| 606 | |
| 607 | secret, shouldReEncrypt, err := factor.GetSecret(config.Security.DBEncryption.DecryptionKeys, config.Security.DBEncryption.Encrypt, config.Security.DBEncryption.EncryptionKeyID) |
| 608 | if err != nil { |
| 609 | return apierrors.NewInternalServerError("Database error verifying MFA TOTP secret").WithInternalError(err) |
| 610 | } |
| 611 | |
| 612 | valid, verr := totp.ValidateCustom(params.Code, secret, time.Now().UTC(), totp.ValidateOpts{ |
| 613 | Period: 30, |
| 614 | Skew: 1, |
| 615 | Digits: otp.DigitsSix, |
| 616 | Algorithm: otp.AlgorithmSHA1, |
| 617 | }) |
| 618 | |
| 619 | if config.Hook.MFAVerificationAttempt.Enabled { |
| 620 | input := v0hooks.NewMFAVerificationAttemptInput( |
| 621 | r, |
| 622 | user.ID, |
| 623 | factor.ID, |
| 624 | factor.FactorType, |
| 625 | valid, |
| 626 | ) |
| 627 | |
| 628 | output := v0hooks.MFAVerificationAttemptOutput{} |
| 629 | err := a.hooksMgr.InvokeHook(nil, r, input, &output) |
| 630 | if err != nil { |
| 631 | return err |
| 632 | } |
| 633 | |
| 634 | if output.Decision == v0hooks.HookRejection { |
| 635 | if err := models.Logout(db, user.ID); err != nil { |
| 636 | return err |
| 637 | } |
| 638 | |
| 639 | if output.Message == "" { |
| 640 | output.Message = v0hooks.DefaultMFAHookRejectionMessage |
| 641 | } |
| 642 | |
| 643 | return apierrors.NewForbiddenError(apierrors.ErrorCodeMFAVerificationRejected, "%s", output.Message) |
| 644 | } |
| 645 | } |
| 646 | if !valid { |
| 647 | if shouldReEncrypt && config.Security.DBEncryption.Encrypt { |
| 648 | if err := factor.SetSecret(secret, true, config.Security.DBEncryption.EncryptionKeyID, config.Security.DBEncryption.EncryptionKey); err != nil { |
| 649 | return err |
| 650 | } |
| 651 |
no test coverage detected