verifyEmailCode checks a submitted code against the stored row. Enforces expiry, attempt limit (5), and constant-time hash compare. On successful match, deletes the row (one-time use) and returns it so the caller can use its captured workspace context (e.g. for gate checks and workspace assignment o
(ctx context.Context, email string, purpose storepb.EmailVerificationCodePurpose, submittedCode string)
| 1784 | // caller can use its captured workspace context (e.g. for gate checks and |
| 1785 | // workspace assignment on the email-code signup path). |
| 1786 | func (s *AuthService) verifyEmailCode(ctx context.Context, email string, purpose storepb.EmailVerificationCodePurpose, submittedCode string) (*store.EmailVerificationCodeMessage, error) { |
| 1787 | row, err := s.store.GetEmailVerificationCode(ctx, email, purpose) |
| 1788 | if err != nil { |
| 1789 | return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to get email verification code")) |
| 1790 | } |
| 1791 | if row == nil { |
| 1792 | return nil, connect.NewError(connect.CodeUnauthenticated, errors.Errorf("invalid or expired code")) |
| 1793 | } |
| 1794 | if time.Now().After(row.ExpiresAt) { |
| 1795 | _ = s.store.DeleteEmailVerificationCodeIfMatch(ctx, email, purpose, row.CodeHash) |
| 1796 | return nil, connect.NewError(connect.CodeUnauthenticated, errors.Errorf("invalid or expired code")) |
| 1797 | } |
| 1798 | if row.Attempts >= emailCodeMaxAttempts { |
| 1799 | _ = s.store.DeleteEmailVerificationCodeIfMatch(ctx, email, purpose, row.CodeHash) |
| 1800 | return nil, connect.NewError(connect.CodeUnauthenticated, errors.Errorf("too many attempts")) |
| 1801 | } |
| 1802 | if subtle.ConstantTimeCompare([]byte(s.hashEmailCode(submittedCode)), []byte(row.CodeHash)) != 1 { |
| 1803 | _ = s.store.IncrementEmailVerificationCodeAttempts(ctx, email, purpose) |
| 1804 | return nil, connect.NewError(connect.CodeUnauthenticated, errors.Errorf("invalid or expired code")) |
| 1805 | } |
| 1806 | _ = s.store.DeleteEmailVerificationCodeIfMatch(ctx, email, purpose, row.CodeHash) |
| 1807 | return row, nil |
| 1808 | } |
| 1809 | |
| 1810 | // authenticateEmailCodeLogin handles the email + 6-digit code flow. |
| 1811 | // Existing users: verify code → return user (downstream pipeline handles workspace-level gates). |
no test coverage detected