Email-based One Time Passwords (OTP)
Summary
Section titled “Summary”Email-based OTP is a mandatory authentication feature for GitLab.com users signing in with passwords. Users receive a code via email during login and must enter it to proceed. This runbook helps SREs triage and mitigate Email OTP-related incidents.
It’s similar to Two-factor authentication but with key differences:
- Mandatory rollout: All GitLab.com users signing in with passwords will be required to use Email OTP1
- Exceptions: Email OTP is unavailable when:
- Group policy requires other two-factor authentication methods
- Account uses an external identity provider
- Account is scheduled for automatic enablement at a future date2
See also https://docs.gitlab.com/development/email_one_time_passwords.
Escalation
Section titled “Escalation”Depending on the urgency required for technical assistance, the escalation path is:
- @ mentioning
@gitlab-com/gl-security/product-security/product-security-engineering - Ask in
#mfa_default_planning - Ask in
#g_sscs_authentication - Page the Authentication team using Pager Duty. (Product Security Engineering is not in Pager Duty).
Known & Documented Impacts
Section titled “Known & Documented Impacts”- Password-based API authentication blocked:
git cloneanddocker loginwill fail withAccess denied3 when a password is used. - Mandatory code entry: Once enforcement begins, users must enter the emailed code to sign in.4
Support has an existing 2FA removal workflow to help users who cannot access their email.
Troubleshooting
Section titled “Troubleshooting”Symptom: Users cannot sign in
Section titled “Symptom: Users cannot sign in”Cause A: Code error in authentication flow
Section titled “Cause A: Code error in authentication flow”Look for errors in:
VerifiesWithEmail(app/controllers/concerns/verifies_with_email.rb),Users::EmailVerification::GenerateTokenService, orUsers::EmailVerification::ValidateTokenService
Solution:
- Confirm the issue is Email OTP-related
- Follow Global temporary hold
- Notify
#mfa_default_planningfor developer investigation
Symptom: Email OTP emails not being delivered
Section titled “Symptom: Email OTP emails not being delivered”Cause A: Mailgun outage
Section titled “Cause A: Mailgun outage”Email OTP depends on Mailgun for email delivery.
Solution:
- Check the Mailgun Service runbooks
- If Mailgun cannot fix it, follow Global temporary hold
Cause B: User error or spam filters
Section titled “Cause B: User error or spam filters”The email was delivered but the user cannot find it.
Solution:
- Direct user to GitLab Support. Support has an existing 2FA removal workflow to help users who cannot access their email.
Symptom: Users cannot be created or updated
Section titled “Symptom: Users cannot be created or updated”Cause A: Code error in user enrollment
Section titled “Cause A: Code error in user enrollment”Look for errors in:
Users::BuildService(new user enrollment)Users::UpdateService, orUsers::EmailOtpEnrollment
Solution:
- Confirm the issue is Email OTP-related
- If it affects existing and new users, follow Global temporary hold
- If it only affects new users, follow Disable for new users
- Notify
#mfa_default_planningfor developer investigation
Mitigation Steps
Section titled “Mitigation Steps”Delaying or disabling for specific users
Section titled “Delaying or disabling for specific users”What: Un-enroll user(s) from Email OTP. They won’t see the prompt and password-based APIs will work again.
When:
- User is locked out (no access to email inboxes)
- Customer impacted by blocked password-based APIs
Approval: Incident Manager On Call (IMOC)
Who can execute: SREs with Rails Console Write privileges
How:
- Gather affected user ID(s)
- Engage an SRE to execute:
# To delay enrollmentsome_future_date = Time.parse('YYYY-MM-DDTHH:MM:SSZ')user.update(email_otp_required_after: some_future_date)# or for multiple usersusers.update_all(email_otp_required_after: some_future_date)- Document the action on the incident/support ticket
- Notify
#mfa_default_planningfor awareness
Knock-on impacts:
- If delayed: No further action needed
- If disabled with
require_minimum_email_based_otp_for_users_with_passwordsenabled: User(s) re-enrolled automatically on next sign-in - If disabled with flag disabled: User(s) need to be added to a later cohort
FAQ:
Q: Why can’t we disable the feature flag per user?
Phase 1 sets email_based_mfa to true for all users. GitLab feature flags don’t support “except user X” exceptions5. User-specific flags also aren’t designed for hundreds/thousands of actors.
Q: Can users self-service this?
Only if require_minimum_email_based_otp_for_users_with_passwords is disabled. Otherwise, they must enable App-based TOTP or WebAuthn first.
Global temporary hold
Section titled “Global temporary hold”What: Disable Email OTP for all users temporarily. Users won’t see the prompt, password-based APIs revert to being unblocked, and the Email OTP preference disappears from UI. (New users will still be automatically enrolled behind the scenes, but they won’t see the feature).
When: High/critical impact affecting many/most users
Approval: Incident Manager On Call (IMOC)
Who can execute: Anyone with ChatOps privileges (most developers, all SREs)
How:
- In Slack, go to
#production - (Optional) Verify current state:
/chatops run feature get email_based_mfa - Disable:
/chatops run feature set email_based_mfa false - Notify
#mfa_default_planning
Knock-on impacts:
- Must decide when/if to re-enable
- Credential stuffing attacks possible while disabled
Disable for new users
Section titled “Disable for new users”What: Disable Email OTP enrollment for new users users temporarily. New users could still find their way to the User Preference and enable Email-based OTP manually.
When: New users can’t sign up
Approval: Incident Manager On Call (IMOC)
Who can execute: Anyone with ChatOps privileges (most developers, all SREs)
How:
- Validate that
Gitlab::CurrentSettings.require_minimum_email_based_otp_for_users_with_passwordsisfalse. Otherwise, follow “Make Email OTP optional”. - In Slack, go to
#production - (Optional) Verify current state:
/chatops run feature get enrol_new_users_in_email_otp - Disable:
/chatops run feature set enrol_new_users_in_email_otp false - Notify
#mfa_default_planning
Knock-on impacts:
- Must decide when/if to re-enable
- Must decide when/if to enforce Email OTP for users who signed up
while
enrol_new_users_in_email_otpwas disabled.
Make Email OTP optional
Section titled “Make Email OTP optional”What: Remove the mandatory enforcement of Email OTP, without changing existing enrollment in Email OTP.
When: The feature works, but we want to allow users to be able to opt-out. (See also “Disable for new users”).
Approval:
- If part of enacting “Disable for new users”, then Incident Manager On Call (IMOC).
- Otherwise, VP of Product Security.
How:
- Set
Gitlab::CurrentSettings.require_minimum_email_based_otp_for_users_with_passwordstofalse - Notify
#mfa_default_planning
Full rollback
Section titled “Full rollback”What: Remove the feature entirely from the codebase.
When: Feature doesn’t work and rollout must be cancelled
Approval: E-Group
Who can execute: Release Manager + developer
How:
- Execute Global temporary hold
- Developers create revert MRs to:
- Remove Email OTP database columns
- Revert feature introduction MRs
Knock-on impacts: Removes feature from self-managed installations (was behind feature flag anyway)
Footnotes
Section titled “Footnotes”-
https://docs.gitlab.com/user/profile/account/two_factor_authentication/#enable-email-otp ↩
-
See Two Factor Authentication Troubleshooting docs. Users must switch to using a Personal Access Token (1, 2) ↩
-
https://docs.gitlab.com/user/profile/account/two_factor_authentication/#sign-in-with-email-otp ↩
-
https://docs.gitlab.com/development/feature_flags/controls/#selectively-disable-by-actor ↩