MCPcopy Create free account
hub / github.com/bytebase/bytebase / authenticateEmailCodeLogin

Method authenticateEmailCodeLogin

backend/api/v1/auth_service.go:1813–1898  ·  view source on GitHub ↗

authenticateEmailCodeLogin handles the email + 6-digit code flow. Existing users: verify code → return user (downstream pipeline handles workspace-level gates). Unknown emails: verify code → gate checks on pre-invited workspace → create user + provision workspace.

(ctx context.Context, request *v1pb.LoginRequest)

Source from the content-addressed store, hash-verified

1811// Existing users: verify code → return user (downstream pipeline handles workspace-level gates).
1812// Unknown emails: verify code → gate checks on pre-invited workspace → create user + provision workspace.
1813func (s *AuthService) authenticateEmailCodeLogin(ctx context.Context, request *v1pb.LoginRequest) (*store.UserMessage, error) {
1814 if request.Password != "" || request.GetIdpName() != "" {
1815 return nil, connect.NewError(connect.CodeInvalidArgument, errors.Errorf("email_code is mutually exclusive with password and idp_name"))
1816 }
1817 email := strings.ToLower(strings.TrimSpace(request.Email))
1818 if email == "" {
1819 return nil, connect.NewError(connect.CodeInvalidArgument, errors.Errorf("email is required"))
1820 }
1821
1822 codeRow, err := s.verifyEmailCode(ctx, email, storepb.EmailVerificationCodePurpose_LOGIN, *request.EmailCode)
1823 if err != nil {
1824 return nil, err
1825 }
1826
1827 // Existing user → return. allow_email_code_signin is checked later in validateLoginPermissions
1828 // against the actually-resolved login workspace (which may not match the send-time workspace
1829 // for multi-workspace users — resolveWorkspaceForLogin prefers LastLoginWorkspace).
1830 user, err := s.store.GetUserByEmail(ctx, email)
1831 if err != nil {
1832 return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to find user"))
1833 }
1834 if user != nil {
1835 return user, nil
1836 }
1837
1838 // Unknown email → signup path. Validate email format to prevent reserved-namespace collisions.
1839 if err := validateEndUserEmail(email); err != nil {
1840 return nil, err
1841 }
1842
1843 // Gate checks run BEFORE user creation to prevent orphan accounts.
1844 // We only consult AllowEmailCodeSignin here: DisallowSignup governs password self-service
1845 // signup (the Signup RPC), not email-code onboarding — the two paths are independent.
1846 // Admins who want to block new users via email-code set AllowEmailCodeSignin=false.
1847 restriction, err := getAccountRestriction(ctx, s.store, s.licenseService, s.profile.SaaS, codeRow.Workspace)
1848 if err != nil {
1849 return nil, err
1850 }
1851 if !restriction.AllowEmailCodeSignin {
1852 return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Errorf("email code login is not enabled for this workspace"))
1853 }
1854 // Signup is always allowed for SaaS
1855 if !s.profile.SaaS {
1856 if restriction.DisallowSignup {
1857 return nil, connect.NewError(connect.CodePermissionDenied, errors.Errorf("sign up is disallowed for this workspace"))
1858 }
1859 }
1860
1861 // Provision workspace BEFORE creating the user so retries are self-healing: if user
1862 // creation fails, the next attempt's FindWorkspace(email) finds the already-provisioned
1863 // workspace via its IAM binding and returns it. The reverse order would leave a user
1864 // without a workspace, and subsequent retries would early-return via GetUserByEmail and
1865 // never re-run provisioning — permanently stuck. Matches the Signup RPC's ordering.
1866 if _, err := s.provisionWorkspaceForNewUser(ctx, email); err != nil {
1867 return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to provision workspace"))
1868 }
1869
1870 // Create principal with random bcrypt password.

Callers 1

authenticateLoginMethod · 0.95

Calls 8

verifyEmailCodeMethod · 0.95
validateEndUserEmailFunction · 0.85
getAccountRestrictionFunction · 0.85
GetIdpNameMethod · 0.80
ErrorfMethod · 0.80
GetUserByEmailMethod · 0.80
CreateUserMethod · 0.65

Tested by

no test coverage detected