SDK Integrations
Add Passji to your app using your favorite auth library. We're a standard OIDC provider, so any OAuth 2.0 / OpenID Connect library works.
Supported Libraries
Passji works with any OIDC-compliant auth library. Here are guides for the most popular options:
Auth.js (NextAuth)
The most popular auth library for Next.js. Full TypeScript support.
Better Auth
Modern auth for TypeScript apps. Simple API, great DX.
Passport.js
Battle-tested auth middleware for Express/Node.js.
Lucia Auth
Lightweight session management with Arctic for OAuth.
Generic OIDC
Use any OIDC-compliant library with Passji.
Environment Variables
All integrations require your Passji credentials. Get these from the Developer Portal.
# .env
PASSJI_CLIENT_ID=your_client_id
PASSJI_CLIENT_SECRET=your_client_secretAuth.js (NextAuth)Recommended
Auth.js (formerly NextAuth.js) is the most popular authentication library for Next.js applications. It has built-in support for OIDC providers.
// auth.ts (Auth.js v5 / NextAuth)
import NextAuth from "next-auth"
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
{
id: "passji",
name: "Passji",
type: "oidc",
issuer: "https://passji.com",
clientId: process.env.PASSJI_CLIENT_ID,
clientSecret: process.env.PASSJI_CLIENT_SECRET,
authorization: {
params: { scope: "openid emoji trust" }
},
profile(profile) {
return {
id: profile.sub,
emojiId: profile.emoji_id,
trustScore: profile.trust_score,
}
},
},
],
})Coming Soon: We're building an official @passji/authjs provider with full TypeScript types for emoji claims and trust scores.
Better AuthRecommended
Better Auth is a modern authentication library with excellent TypeScript support and a clean API.
// auth.ts (Better Auth)
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
passji: {
clientId: process.env.PASSJI_CLIENT_ID!,
clientSecret: process.env.PASSJI_CLIENT_SECRET!,
issuer: "https://passji.com",
scopes: ["openid", "emoji", "trust"],
// Map claims to user profile
mapProfileToUser: (profile) => ({
id: profile.sub,
name: profile.emoji_id,
image: null, // Passji has no profile images
trustScore: profile.trust_score,
}),
},
},
})Passport.jsSupported
Passport.js is the classic authentication middleware for Node.js/Express applications. Use the passport-openidconnect strategy.
// passport.js (Passport.js with OpenID Connect)
import passport from "passport"
import { Strategy as OIDCStrategy } from "passport-openidconnect"
passport.use("passji", new OIDCStrategy({
issuer: "https://passji.com",
authorizationURL: "https://passji.com/authorize",
tokenURL: "https://passji.com/token",
userInfoURL: "https://passji.com/userinfo",
clientID: process.env.PASSJI_CLIENT_ID,
clientSecret: process.env.PASSJI_CLIENT_SECRET,
callbackURL: "https://yourapp.com/auth/passji/callback",
scope: ["openid", "emoji", "trust"],
},
(issuer, profile, done) => {
// profile contains the claims from userinfo
return done(null, {
id: profile.id,
emojiId: profile._json.emoji_id,
trustScore: profile._json.trust_score,
})
}
))Lucia AuthSupported
Lucia is a lightweight session management library. Use Arctic (same author) for the OAuth flow.
// auth.ts (Lucia Auth with Arctic)
import { Lucia } from "lucia"
import { Arctic } from "arctic"
// Arctic supports generic OIDC
const passji = new Arctic.OIDC(
"https://passji.com",
process.env.PASSJI_CLIENT_ID!,
process.env.PASSJI_CLIENT_SECRET!,
"https://yourapp.com/auth/passji/callback"
)
// Create authorization URL
export function createPassjiAuthURL(state: string, codeVerifier: string) {
return passji.createAuthorizationURL(state, codeVerifier, {
scopes: ["openid", "emoji", "trust"]
})
}
// Exchange code for tokens
export async function validateCallback(code: string, codeVerifier: string) {
const tokens = await passji.validateAuthorizationCode(code, codeVerifier)
// Decode the id_token or call userinfo
return tokens
}Generic OIDC ClientUniversal
Using a library not listed here? Any standard OIDC client will work. Here's an example with openid-client:
// Generic OIDC client (openid-client)
import { Issuer } from "openid-client"
const passjiIssuer = await Issuer.discover("https://passji.com")
const client = new passjiIssuer.Client({
client_id: process.env.PASSJI_CLIENT_ID,
client_secret: process.env.PASSJI_CLIENT_SECRET,
redirect_uris: ["https://yourapp.com/callback"],
response_types: ["code"],
})
// Generate authorization URL
const authUrl = client.authorizationUrl({
scope: "openid emoji trust",
state: crypto.randomUUID(),
code_challenge: codeChallenge,
code_challenge_method: "S256",
})
// Exchange code for tokens
const tokenSet = await client.callback(
"https://yourapp.com/callback",
{ code, state },
{ code_verifier: codeVerifier }
)
// Get user info
const userinfo = await client.userinfo(tokenSet.access_token)Integration Tips
- Always use PKCE for single-page apps (SPAs) and mobile apps
- Request the
trustscope to get trust scores - useful for progressive access control - The
emoji_idclaim can be displayed directly - it's a user-friendly identifier - Store the
subclaim as the unique user ID - emoji sequences could theoretically change - Handle token refresh - access tokens expire after 1 hour, use the refresh token to get new ones