Register Flow
This document explains how the email-based registration pipeline in the auth module is implemented and what a frontend client must do to integrate with it. The flow is deliberately split into two RPCs: one to issue a magic link by email and another to finalize the account creation. The sections below describe the data contracts, validation rules, and expected UX behavior at each step so that UI engineers can wire the screens without re-reading the Rust implementation.
Step 1. Request a registration email
Use the UserAuth.SendRegisterEmail RPC with the prospective user’s email address and optional referral code.
| Field | Notes |
|---|---|
email | Raw email string entered by the user. |
referral_code | Optional referral/invitation code. Will be passed to Step 2 via URL query. |
Backend behavior
- The service first validates the domain against the configurable whitelist/blacklist. Requests to disallowed domains return
INVALID_EMAILimmediately:
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EmailDomainConfig {
pub enable_white_list: bool,
pub white_list: Box<[CompactString]>,
pub enable_black_list: bool,
pub black_list: Box<[CompactString]>,
}
impl EmailDomainConfig {
pub fn check_addr(&self, addr: impl AsRef<str>) -> bool {
// ...
}
}
- If the email already maps to an existing login, the call returns
EMAIL_EXISTSso the UI can direct the user to the login or password reset flow. - When rate limiting is triggered (same address requested again before
resend_intervalelapses), the server still returnsSENTbut quietly suppresses a duplicate email. Surface a neutral “email sent” toast to avoid leaking account existence. The defaultresend_intervalis 30 seconds. - For a fresh request, the service creates a magic-link record containing a 32-character
auth_key, queues an email via RabbitMQ, and responds withSENT:
#[derive(Debug, Clone, PartialEq, Eq, sqlx::FromRow)]
pub struct EmailVerifyLink {
pub id: i64,
pub email: String,
pub auth_key: String,
pub send_at: PrimitiveDateTime,
pub reason: EmailVerifyReason,
pub user_id: Option<Uuid>,
pub is_unused: bool,
}
const AUTH_KEY_LENGTH: usize = 32;
pub fn generate_auth_key() -> String {
// ...
}
Frontend expectations
- Present an email field, an optional referral code field, and an action button. On submission, call
SendRegisterEmailand branch on the enum:SENT: Show success UI (“Check your inbox for a magic link”) and inform the user to click the link in their email. The magic link will include theauth_keyand thereferral_code(if provided) as URL query parameters, automatically directing them to Step 2.INVALID_EMAIL: Highlight the field with a validation error.EMAIL_EXISTS: Offer links to sign in or reset password.
- Because the backend may skip resending, add a visible countdown using the configured
resend_interval(default 30 seconds) before enabling a “Resend” button. - The email template contains a clickable magic link that embeds the
auth_keyin the URL. When clicked, it should route the user directly to the registration completion page (Step 2) with the token and referral code automatically populated from URL query parameters.
Step 2. Complete registration via magic link
When the user clicks the magic link from the email, they are directed to the registration completion page. The URL will contain:
auth_key– extracted from the URL query parameter, automatically validates the user’s emailreferral_code– passed through from Step 1 via URL query parameter (if provided)
The magic link is time-limited. Backend enforcement uses link_expire_after (default 5 minutes). After that, registration attempts fail with INVALID_LINK.
On the frontend, the registration completion page should:
- Extract the
auth_keyfrom the URL query parameters (this proves the user has access to the email). - Extract the
referral_codefrom the URL query parameters (if present, this was provided in Step 1). - Display a form to collect:
- A password that satisfies the platform’s policy (policy enforcement happens upstream in the password module).
- Display the referral code if present (read-only or hidden field).
- A checkbox “Keep me signed in” that toggles auto-login.
Step 3. Finalize registration
Call UserAuth.RegisterUser with the collected data:
pub struct RegisterUser {
pub auth_key: String,
pub password: String,
pub referral_code: Option<String>,
pub auto_login: bool,
pub ip: Option<IpAddr>,
pub user_agent: Option<String>,
}
| Field | Required | Notes |
|---|---|---|
auth_key | ✓ | Magic link token from URL query parameter. Each token is single-use and tied to the email. |
password | ✓ | Plain password; hashing happens server-side. |
referral_code | Optional marketing code from URL query parameter (passed through from Step 1), forwarded unchanged. | |
auto_login | ✓ | When true, the backend issues access & refresh tokens on success. |
ip | Optional. If the frontend can detect the client’s public IP (e.g., API gateway), pass it for session metadata. | |
user_agent | Optional string captured from the browser; used for session/device history. |
Response handling
RegisterUserReply can return four shapes based on the service result:
pub enum RegisterUserResult {
Registered(Uuid),
RegisteredWithSession(Uuid, AccessToken, RefreshToken),
EmailAlreadyExists,
InvalidLink,
}
REGISTERED_WITH_SESSION: Registration succeeded and a new session was created. The reply contains theuser_idplusaccess_tokenandrefresh_token. Store them immediately using the same rules as a login response, then route to the signed-in area.REGISTERED: Registration succeeded but no session was created (becauseauto_loginwas false). Route to the login screen and preload the email for convenience.- gRPC error
ALREADY_EXISTS: The email was registered after the magic link was issued. Show an “already registered” message and link to sign-in. INVALID_LINK: Magic link was unknown, already used, or expired. Inform the user that the link is invalid/expired and offer to send a new registration email.
When auto-login is enabled, the backend records the session with the supplied IP and user agent before returning tokens, so capturing accurate metadata is important for the session list and security analytics.
Retrying after failures
If the magic link expires or has already been used, require the user to restart from Step 1 and request a new registration email. Once a link has been consumed, it cannot be retried.
UI checklist
- Provide separate screens for Step 1 (email + referral code submission) and Step 2 (password entry after clicking magic link).
- In Step 1, include both an email field and an optional referral code field.
- Show a visible countdown for magic link expiration and resend availability (based on config defaults; make them configurable via environment/UI constants).
- In Step 2, extract both
auth_keyandreferral_codefrom URL query parameters automatically—no manual token entry is needed. - Display the referral code (if present) on the Step 2 page for user confirmation, either as a read-only field or hidden input.
- Ensure all success paths funnel to analytics hooks alongside the
user_idreturned from the RPC for downstream tracking. - When auto-login is disabled, clearly direct the user to the login screen after successful registration.
This flow mirrors the backend implementation and should keep the frontend in sync with the server-side invariants without re-reading the Rust code every time changes are made.