Getting Started
Add 'Sign in with Passji' to your app in under 5 minutes. Standard OAuth 2.0 / OIDC flow.
Prerequisites
- An application that can handle OAuth 2.0 / OIDC callbacks
- A registered app in the Passji Developer Portal
- Any OIDC client library (optional - raw HTTP works fine)
Step 1Register Your App
Go to the Developer Portal and register your application. You'll need:
- App name - Displayed to users during consent
- Redirect URI(s) - Where users return after auth
- App type - Web, SPA, mobile, or machine-to-machine
After registration, you'll receive your client_id and client_secret.
// 1. Go to the Developer Portal
// https://passji.com/developer
// 2. Register your app and get credentials:
// - client_id: "your_client_id"
// - client_secret: "your_client_secret"
// - redirect_uri: "https://yourapp.com/callback"Step 2Redirect to Authorization
When a user clicks "Sign in with Passji", redirect them to the authorization endpoint. The user will pick their emoji, authenticate with their passkey, and grant consent.
// Step 1: Build the authorization URL
const authUrl = new URL("https://passji.com/authorize");
authUrl.searchParams.set("client_id", "your_client_id");
authUrl.searchParams.set("redirect_uri", "https://yourapp.com/callback");
authUrl.searchParams.set("response_type", "code");
authUrl.searchParams.set("scope", "openid emoji trust");
authUrl.searchParams.set("state", crypto.randomUUID());
// With PKCE (recommended for SPAs and mobile):
const codeVerifier = generateCodeVerifier(); // 43-128 chars
const codeChallenge = await sha256(codeVerifier);
authUrl.searchParams.set("code_challenge", codeChallenge);
authUrl.searchParams.set("code_challenge_method", "S256");
// Step 2: Redirect user to Passji
window.location.href = authUrl.toString();PKCE is recommended for single-page apps (SPAs) and mobile apps. It prevents authorization code interception attacks without requiring a client secret on the client side.
Step 3Exchange Code for Tokens
After the user authorizes, they're redirected back to your redirect_uri with a code parameter. Exchange this for tokens.
// Step 3: Exchange code for tokens (server-side)
const response = await fetch("https://passji.com/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "authorization_code",
code: authorizationCode,
redirect_uri: "https://yourapp.com/callback",
client_id: "your_client_id",
client_secret: "your_client_secret",
// Include if using PKCE:
code_verifier: codeVerifier,
}),
});
const tokens = await response.json();
// {
// access_token: "eyJhbGciOiJSUzI1NiIs...",
// id_token: "eyJhbGciOiJSUzI1NiIs...",
// token_type: "Bearer",
// expires_in: 3600,
// refresh_token: "refresh_abc123..."
// }Step 4Use the ID Token
The id_token is a signed JWT containing the user's identity claims. Verify the signature using Passji's public keys from the JWKS endpoint.
// Step 4: Decode the ID token
// The id_token contains user claims:
{
"iss": "https://passji.com",
"sub": "usr_abc123", // Unique user ID
"aud": "your_client_id",
"iat": 1709856000,
"exp": 1709859600,
"emoji_id": "🦊🌸🎯", // The user's emoji identity
"emoji_length": 3,
"trust_score": 0.85, // If 'trust' scope requested
"account_age_days": 45,
"auth_method": "webauthn"
}Available Scopes
| Scope | Claims Returned | Description |
|---|---|---|
openid | sub | Required. Returns the unique user ID. |
emoji | emoji_id, emoji_length | The user's emoji sequence. |
profile | emoji_id, emoji_length | Alias for emoji scope. |
trust | trust_score, account_age_days | Trust score and account metadata. |
Next Steps
- Endpoints Reference - Full API documentation
- SDK Integrations - Use Auth.js, Better Auth, or Passport.js
- Trust Scoring - Implement progressive access based on trust