Password-less Authentication

Allow user to login using email + verification code

2025.07

This is a sample design document for our technical design consulting service.

Requirements Summary

UI Components

Account Component

Account Component to be placed on the right most side of the topbar.

If the user is logged in, the account component shows the user profile image and his name. When clicked, navigates to the user profile page.

If the user is not logged in, the account component is a "Login" button which navigates to the login page when clicked.

Before navigation to the login form, the current url is stored in sessionStorage under key login_redirect_url so it can be restored after successful login.

Profile page

The profile page should show the user's name, email, and profile picture.

The profile page includes a logout button.

Login page

The login page shows a login form which can be in two states:

Email Input

Code Input

Post-Login Redirection

After logging in, the login_redirect_url key from sessionStorage is inspected. If a value is present there, the key is cleared and the page is redirected to the given route. If it's empty, the page is redirected to the home page.


Database tables

For efficient storage on the database, we will internally use a numeric user_id, but it will not be exposed to the UI side.

login_codesemailstringPKcodestringfail_countintcreated_atdatetimesessionstokenstringPKuser_idintcreated_atdatetimeuser_accountsemailstringPKuser_idintFKcreated_atdatetimeuser_profilesuser_idintPKemailstringnamestringpicture_urlstringmodified_atdatetimecreated_atdatetime

Session

The backend generates a session token by filling a 16 byte buffer with cryptographically secure random data, then encodes it in base64.

For the client side, the session token is stored in local storage using key "session_token".

Every API request expects the session token to be included in the header "X-Session-Token".

The front end has a globally accessible instance of UserProfile

type UserProfile = {
    email: string
    name: string
    picture_url: string
}

Processes

There are types of events we care about:

As such, the following processes will be explained:


Login Code Request

Occurs when the user enters their email into the login form and clicks the "Request login code" button.

API:

POST /api/request_login_code

Request:
{
    email: string
}

OK Response (status 200)
{
}

Error Response (status 400)
{
}

Frontend flow:

Backend flow:

Below we demonstrate the basic case with a sequence diagram.

Login Code RequestUser's Email ServerUserFront EndBack EndDatabase click button"Request Login Code"request_login_code{email}insert login_codes entry send emailok responsedisplay login code input form Invalid Email InputUser's Email ServerUserFront EndBack EndDatabase click button"Request Login Code"validation error messageNotices emails is invalid

Login Code Verification

Occurs when the user enters the login code into the login form

API:

POST /api/verify_login_code

Request:
{
    email: string
    code: string
}

OK Response (status 200)
{
    session_token: string
    user_profile: UserProfile
}

Error Response (status 400)
{
}

Frontend flow:

Backend flow:

Below we demonstrate various cases with sequence diagrams.

Verify Login Code | Client Side | ValidUser's Email ServerUserFront EndlocalStorageBack End Find email that contains login codeShow email contentEnter code and click action buttonverify_login_code{email, code}ok response{session_token, user_profile}store session_tokenset global user_profile instanceget("login_redirect_url")resultshow page redirected to Verify Login Code | Server Side | ValidFront EndBack EndDatabase verify_login_code{email, code}query login_codes tableresultverifies code anddecides to accept itdelete login_codes rowquery for user profileresultcreate session entryok response{session_token, user_profile} Verify Login Code | Server Side | Valid + New AccountFront EndBack EndDatabase verify_login_code{email, code}query login_codes tableresultverifies code anddecides to accept itdelete login_codes rowquery for user profileno resultcreate user_accounts and user_profiles entriescreate session entryok response{session_token, user_profile} Verify Login Code | Server Side | Invalid CodeFront EndBack EndDatabase verify_login_code{email, code}query login_codes tableresultcode does not matchincrement failed_counterror response Verify Login Code | Server Side | Expired CodeFront EndBack EndDatabase verify_login_code{email, code}query login_codes tableresultcreated_at > 10 minutes agodelete login_codes entryerror response

Session Token Validation

Occurs when the page is first loaded during the initialization process

API:

POST /api/verify_session_token

Request:
{
    session_token: string
}

OK Response (status 200)
{
    session_token: string
    user_profile: UserProfile
}

Error Response (status 400)
{
}

Frontend flow:

Backend flow:

Below we demonstrate various cases with sequence diagrams.

Validate Session Token | Client Side | Valid TokenLocal StorageFront EndBack End get("session_token")tokenvalidate_session_token{session_token}ok responseset `user_profile`set("session_token") Validate Session Token | Server Side | Valid TokenFront EndBack EndDatabase validate_session_token{session_token}query `sessions` tablesessionquery `user_profiles` tableuser_profileok response Validate Session Token | Server Side | Valid Token with RenewFront EndBack EndDatabase validate_session_token{session_token}query `sessions` tablesessioncreate new session tokendelete current sessionstore new sessionquery `user_profiles` tableuser_profileok response Validate Session Token | Server Side | Invalid TokenFront EndBack EndDatabase validate_session_token{session_token}query `sessions` tableno resultserror response

Session Token Invalidation

Triggered when user hits logout button. The browser deletes the session token from local storage, and tells the backend to delete it from the database as well.

API:

POST /api/delete_session_token

Request:
{
    session_token: string
}

OK Response (status 200)
{
}

Frontend flow:

Backend flow:

There's no need for any error response. The browser does not even check the response.

Invalidate Session TokenLocal StorageFront EndBack EndDatabase invalidate_sessiondelete("session_token")delete entry from sessions tableempty responseredirect to home page and full reloaddoes not wait for responseDoes not wait for response

Conclusion

In order to support the implementation of password less implementation, we have outlined the following: