Overview
TrustOTP is a drop-in authentication service for web apps. It handles Email OTP, Magic Links, and Google Sign-in — all returning OIDC-compatible, RS256-signed JWTs you verify with a public JWKS endpoint.
Email OTP
6-digit code sent to user's inbox
Magic Link
One-click passwordless login
Google Sign-in
OAuth 2.0 via Google
Quick Start
- 1
Create a developer account
Go to https://trustotp.rocketinternet.in/get-started and register with email or Google.
- 2
Create an app
In the dashboard, click New App. Give it a name and set your
redirect_uri— this is where TrustOTP sends the user after login. - 3
Copy your Client ID
Your
client_idis shown on the app detail page. You will pass this with every API call. - 4
Call the API
Choose Email OTP, Magic Link, or Google Sign-in below and follow the integration steps.
Email OTP
A 6-digit one-time password is emailed to the user. Your app calls send, prompts the user to enter the code, then calls verify to exchange it for a JWT.
1. Send OTP
POST the user's email and your app credentials:
POST https://trustotp.rocketinternet.in/api/auth/email-otp/send
Content-Type: application/json
{
"email": "user@example.com",
"client_id": "YOUR_CLIENT_ID",
"redirect_uri": "https://yourapp.com/callback",
"state": "random-csrf-token" // optional
}| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Yes | User's email address | |
| client_id | string | Yes | Your app's Client ID from the dashboard |
| redirect_uri | string | Yes | Must match an allowed URI in your app settings |
| state | string | No | Random value for CSRF protection — returned unchanged in the callback |
// Success
{ "success": true, "message": "OTP sent" }
// Error
{ "error": "Email OTP not enabled for this app" }2. Verify OTP
After the user enters the code, verify it:
POST https://trustotp.rocketinternet.in/api/auth/email-otp/verify
Content-Type: application/json
{
"email": "user@example.com",
"otp": "123456",
"client_id": "YOUR_CLIENT_ID",
"redirect_uri": "https://yourapp.com/callback",
"state": "random-csrf-token" // optional — echoed back
}// Success — redirect the user to redirect_uri?access_token=...
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 900,
"sub": "user-id",
"redirect_uri": "https://yourapp.com/callback"
}
// Error
{ "error": "Invalid or expired OTP" }The OTP expires in 10 minutes. Redirect the user to your redirect_uri with the access_token as a query param, then verify the token server-side.
Full example (TypeScript)
// ① Send OTP
const sendRes = await fetch("https://trustotp.rocketinternet.in/api/auth/email-otp/send", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: userEmail,
client_id: "YOUR_CLIENT_ID",
redirect_uri: "https://yourapp.com/callback",
state: crypto.randomUUID(),
}),
});
// ② After user enters the code, verify it
const verifyRes = await fetch("https://trustotp.rocketinternet.in/api/auth/email-otp/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: userEmail,
otp: userEnteredCode,
client_id: "YOUR_CLIENT_ID",
redirect_uri: "https://yourapp.com/callback",
}),
});
const { access_token } = await verifyRes.json();
// ③ Verify the JWT server-side (see Token Verification)
import { createRemoteJWKSet, jwtVerify } from "jose";
const JWKS = createRemoteJWKSet(new URL("https://trustotp.rocketinternet.in/api/jwks"));
const { payload } = await jwtVerify(access_token, JWKS, {
issuer: "https://trustotp.rocketinternet.in",
audience: "YOUR_CLIENT_ID",
});
console.log(payload.sub); // stable user ID scoped to your app
console.log(payload.email); // verified emailMagic Link
A one-click login link is emailed to the user. When they click it, TrustOTP verifies the token and redirects to your redirect_uri with an access_token.
1. Send Magic Link
POST https://trustotp.rocketinternet.in/api/auth/magic-link/send
Content-Type: application/json
{
"email": "user@example.com",
"client_id": "YOUR_CLIENT_ID",
"redirect_uri": "https://yourapp.com/callback",
"state": "random-csrf-token" // optional
}{ "success": true, "message": "Magic link sent" }2. Handle the callback
When the user clicks the link in their email, TrustOTP redirects them to your redirect_uri:
GET https://yourapp.com/callback
?access_token=eyJ...
&token_type=Bearer
&expires_in=900
&sub=user-id
&state=random-csrf-token // echoed back if provided// On your callback page
const params = new URLSearchParams(window.location.search);
const token = params.get("access_token");
const state = params.get("state");
// Verify state matches what you stored before sending the link
// Then verify the JWT server-side (see Token Verification)Google Sign-in
Redirect users to the TrustOTP Google OAuth flow. They sign in with Google, and TrustOTP redirects back to your redirect_uri with an access_token.
1. Redirect the user
// Build the login URL and redirect
const loginUrl = new URL("https://trustotp.rocketinternet.in/login");
loginUrl.searchParams.set("client_id", "YOUR_CLIENT_ID");
loginUrl.searchParams.set("redirect_uri", "https://yourapp.com/callback");
loginUrl.searchParams.set("state", crypto.randomUUID());
window.location.href = loginUrl.href;2. Handle the callback
// GET https://yourapp.com/callback?access_token=eyJ...&sub=...
const params = new URLSearchParams(window.location.search);
const token = params.get("access_token");
// Verify the JWT server-side
import { createRemoteJWKSet, jwtVerify } from "jose";
const JWKS = createRemoteJWKSet(new URL("https://trustotp.rocketinternet.in/api/jwks"));
const { payload } = await jwtVerify(token, JWKS, {
issuer: "https://trustotp.rocketinternet.in",
audience: "YOUR_CLIENT_ID",
});
// payload.sub → stable user ID
// payload.email → verified Google email
// payload.name → Google display nameToken Verification
All access_tokens are RS256-signed JWTs. Always verify them server-side using TrustOTP's public JWKS.
JWKS endpoint
GET https://trustotp.rocketinternet.in/api/jwksReturns the public keys used to sign all tokens. Cache this for performance.
Verify with jose (Node.js)
import { createRemoteJWKSet, jwtVerify } from "jose";
const JWKS = createRemoteJWKSet(
new URL("https://trustotp.rocketinternet.in/api/jwks")
);
const { payload } = await jwtVerify(accessToken, JWKS, {
issuer: "https://trustotp.rocketinternet.in",
audience: "YOUR_CLIENT_ID", // your app's client_id
});
// Token claims:
// payload.sub → user ID (stable, scoped to your app)
// payload.email → verified email address
// payload.name → display name (if available)
// payload.iat → issued at (Unix timestamp)
// payload.exp → expires at (Unix timestamp)Verify with PyJWT (Python)
import jwt, requests
# Fetch JWKS
jwks = requests.get("https://trustotp.rocketinternet.in/api/jwks").json()
# Decode and verify
payload = jwt.decode(
access_token,
jwt.algorithms.RSAAlgorithm.from_jwk(jwks["keys"][0]),
algorithms=["RS256"],
audience="YOUR_CLIENT_ID",
issuer="https://trustotp.rocketinternet.in",
)
print(payload["sub"]) # user ID
print(payload["email"]) # verified emailOpenID Connect discovery
TrustOTP exposes a standard OIDC discovery document:
GET https://trustotp.rocketinternet.in/api/.well-known/openid-configurationAPI Reference
POST/api/auth/email-otp/send
Send a 6-digit OTP to the user's email.
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Yes | User email | |
| client_id | string | Yes | App Client ID |
| redirect_uri | string | Yes | Allowed callback URL |
| state | string | No | CSRF token (echoed back) |
POST/api/auth/email-otp/verify
Verify the OTP and receive an access token.
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Yes | Same email used in send | |
| otp | string | Yes | 6-digit code |
| client_id | string | Yes | App Client ID |
| redirect_uri | string | Yes | Same URI used in send |
POST/api/auth/magic-link/send
Send a one-click login link to the user's email.
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | Yes | User email | |
| client_id | string | Yes | App Client ID |
| redirect_uri | string | Yes | Allowed callback URL |
| state | string | No | CSRF token (echoed back) |
GET/api/auth/magic-link/verify
Called automatically when the user clicks the link in their email. Redirects to your redirect_uri with an access_token.
GET/api/jwks
Returns the RS256 public keys used to sign all tokens (JWKS format). No authentication required.
GET/.well-known/openid-configuration
Standard OIDC discovery document listing all endpoints and supported features.
Error Codes
All errors return JSON with an error field and an appropriate HTTP status code.
| Status | Error | Meaning |
|---|---|---|
| 400 | Invalid request | Missing or malformed fields in the request body |
| 401 | Unknown client | client_id does not exist or app is inactive |
| 401 | Invalid or expired OTP | OTP is wrong, already used, or older than 10 minutes |
| 401 | Invalid or expired link | Magic link token is invalid or expired (15 min TTL) |
| 401 | Invalid redirect_uri | redirect_uri not in your app's allowed list |
| 403 | Email OTP not enabled | Enable Email OTP in your app settings |
| 403 | Magic Link not enabled | Enable Magic Link in your app settings |
| 500 | Internal server error | Something went wrong on our end — try again |
Ready to integrate?
Create your free account →