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

G

Google Sign-in

OAuth 2.0 via Google

Quick Start

  1. 1

    Create a developer account

    Go to https://trustotp.rocketinternet.in/get-started and register with email or Google.

  2. 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. 3

    Copy your Client ID

    Your client_id is shown on the app detail page. You will pass this with every API call.

  4. 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:

http
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
}
ParameterTypeRequiredDescription
emailstringYesUser's email address
client_idstringYesYour app's Client ID from the dashboard
redirect_uristringYesMust match an allowed URI in your app settings
statestringNoRandom value for CSRF protection — returned unchanged in the callback
json
// 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:

http
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
}
json
// 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)

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 email

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

typescript
// 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

typescript
// 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 name

Token Verification

All access_tokens are RS256-signed JWTs. Always verify them server-side using TrustOTP's public JWKS.

JWKS endpoint

http
GET https://trustotp.rocketinternet.in/api/jwks

Returns the public keys used to sign all tokens. Cache this for performance.

Verify with jose (Node.js)

typescript
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)

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 email

OpenID Connect discovery

TrustOTP exposes a standard OIDC discovery document:

http
GET https://trustotp.rocketinternet.in/api/.well-known/openid-configuration

API Reference

POST/api/auth/email-otp/send

Send a 6-digit OTP to the user's email.

ParameterTypeRequiredDescription
emailstringYesUser email
client_idstringYesApp Client ID
redirect_uristringYesAllowed callback URL
statestringNoCSRF token (echoed back)

POST/api/auth/email-otp/verify

Verify the OTP and receive an access token.

ParameterTypeRequiredDescription
emailstringYesSame email used in send
otpstringYes6-digit code
client_idstringYesApp Client ID
redirect_uristringYesSame URI used in send

POST/api/auth/magic-link/send

Send a one-click login link to the user's email.

ParameterTypeRequiredDescription
emailstringYesUser email
client_idstringYesApp Client ID
redirect_uristringYesAllowed callback URL
statestringNoCSRF 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.

StatusErrorMeaning
400Invalid requestMissing or malformed fields in the request body
401Unknown clientclient_id does not exist or app is inactive
401Invalid or expired OTPOTP is wrong, already used, or older than 10 minutes
401Invalid or expired linkMagic link token is invalid or expired (15 min TTL)
401Invalid redirect_uriredirect_uri not in your app's allowed list
403Email OTP not enabledEnable Email OTP in your app settings
403Magic Link not enabledEnable Magic Link in your app settings
500Internal server errorSomething went wrong on our end — try again

Ready to integrate?

Create your free account →