The One-Time Password (OTP) identity provider offers a low-assurance, passwordless authentication method. It verifies end-users by sending a temporary code to their email address and validating it against the code entered by the user.
Client Application -> Authenticates via Keycloak Keycloak (Broker) -> Delegates authentication to Email OTP Identity Provider OTP Provider -> Authenticates user via email-based OTP and returns identity assertion
- User accesses the client application, which redirects to Keycloak for authentication.
- Keycloak presents the Email OTP provider as a login option (configured as an external identity provider).
- User selects the OTP provider, and Keycloak redirects the user to the provider’s login page.
- User enters their email address, and the provider sends an OTP to that address.
- User enters the OTP, and the provider validates it.
- Upon success, the provider redirects back to Keycloak with an identity token (OIDC or SAML).
- Keycloak maps the external identity to a local user (via mappers or first-login flow).
- Keycloak issues its own tokens (access, ID, refresh) to the client application.
-
Install
asdfaccording to theasdfinstallation guide. -
Install
asdftools defined in .tool-verions.cat .tool-versions | cut -f 1 -d ' ' | xargs -n 1 asdf plugin-add || true asdf plugin-update --all asdf install asdf reshim
-
Install
pipwith python.curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py python get-pip.py
-
Install Python packages with
pip.pip install -r requirements.txt
-
Setup git pre-commit hooks.
pre-commit install gitlint install-hook
- Create
.envfrom.env.exampleand update all the values - Run
yarnto install all the dependencies - Run
yarn devto start a local server - Run
yarn buildto create a javascript bundle for production deployment - Run
yarn startto run the javascript bundle - Run
yarn tailwindto compile the css (will hot reload)
- Create a tenant
otp - Create an email identity under the tenant and complete verification
- Create a domain identity under the tenant and request right person from
Digital Workplace and Collaboration Services Branchto get the DKIM, DMARC and custom MAIL FROM CNAME records in NNR - Ensure all the records are successfully verified and then request production access. The production access requires below details:
- Mail Type:
TRANSACTIONAL - Website URL:
*/.well-known/openid-configuration - Usecase Description: One time passcode identity provider
- Mail Type:
- Enable
Virtual Deliverability Managerwith click tracking disabled. The click tracking adds 1 X 1 pixel image to every email sent to users and most of the email providers display a warning on top of the email so we had to disable it - (Optional) Create a configuration set to override any settings pertaining to reputation metrics, suppression list, auto validation, archiving options. Assign it to the tenant
- The OTP provider running as an ECS Task requires appropriate IAM permissions added to its Task Role for it to be able to send emails. Permissions include
sendEmailandsendRawEmail
The app runs locally using tsup to compile the server and client files into the build directory. To recompile the css on the fly, run yarn tailwind in another terminal.
-
Given the application and database are up and database migration is successfully complete, run below SQL to add a client for testing purposes.
--confidential client INSERT INTO "ClientConfig" ("clientId", "clientSecret", "grantTypes", "redirectUris", "scope", "responseTypes", "clientUri", "allowedCorsOrigins", "postLogoutRedirectUris", "tokenEndpointAuthMethod") VALUES('conf-client', 's3cr3t', '{authorization_code, refresh_token}', '{http://localhost:3001}', 'openid email', '{code}', 'http://localhost:3001', '{http://localhost:3001}', '{http://localhost:3001}', 'client_secret_post'); --public client INSERT INTO public."ClientConfig" ("clientId", "grantTypes", "redirectUris", "scope", "responseTypes", "clientUri", "allowedCorsOrigins", "postLogoutRedirectUris", "tokenEndpointAuthMethod") VALUES('pub-client', '{authorization_code, refresh_token}', '{http://localhost:3001}', 'openid email', '{code}', 'http://localhost:3001', '{http://localhost:3001}', '{http://localhost:3001}', 'none');
End to end testing is done with playwright. As prerequisite the end-to-end tests need a seeded db. Run the folowing from this directory:
psql -c 'create database otp_test';yarn build && DB_NAME=otp_test node build/migrate.jspsql -d otp_test -f e2e/seed.sql
This is only needed the first time to initialize the db. To run the tests run yarn test:e2e
For debugging, you can run yarn playwright test --debug. This is useful alongside adding test.only on the test to debug.
For auto-generating tests, you can run yarn playwright codegen to click through the app and generate a test.
The test-version of the server has a few settings, using the env vars NODE_ENV=test OTP_RESEND_INTERVAL_MINUTES=[2,3,3,4]. When NODE_ENV is set to test, code resend intervals will be in seconds instead of minutes, useful for testing lockout functionality. You can adjust the OTP_RESEND_INTERVAL_MINUTES array to desired intervals in seconds then. It will also skip the CHES email callouts while in test mode.