Secure Identity
One identity.
Every Evantra
experience.
Your Evantra ID provides secure access across the Evantra digital ecosystem.
EVANTRA IDENTITY
Unified Auth & Enterprise SSO
Integration kit
Integration kit
Drop-in code for web, mobile and server integrations with Evantra Identity.
Installation
Add Evantra Identity to your app
Use the React package for web and mobile apps. Use the SDK for servers, desktop apps and CLI tools.
pnpm add @evantra-identity/reactpnpm add @evantra-identity/sdkWeb · start the flow
Authorization code with PKCE
import {
createEvantraAuthorizeUrl,
createEvantraPkcePair,
createEvantraState,
} from "@evantra-identity/react";
// The authorize endpoint lives on the identity API host.
const API = "https://evantra-headquarters.onrender.com";
const REDIRECT_URI = "https://app.example.com/oauth/callback";
// 1. Create the PKCE verifier and a CSRF state value
const { verifier, challenge } = await createEvantraPkcePair();
const state = createEvantraState();
sessionStorage.setItem("evantra_pkce_verifier", verifier);
sessionStorage.setItem("evantra_oauth_state", state);
// 2. Send the user to the authorization endpoint
window.location.assign(
createEvantraAuthorizeUrl(API, {
clientId: "evt_client_...",
redirectUri: REDIRECT_URI,
codeChallenge: challenge,
codeChallengeMethod: "S256",
scope: "openid profile email",
state,
}),
);Web · handle the callback
Exchange the code on your callback route
// app/oauth/callback/page.tsx
"use client";
import { useEffect } from "react";
import {
consumeEvantraWebCallback,
exchangeEvantraCode,
} from "@evantra-identity/react";
const REDIRECT_URI = "https://app.example.com/oauth/callback";
export default function Callback() {
useEffect(() => {
async function complete() {
// Reads ?code= and strips it from the address bar
const { code } = consumeEvantraWebCallback(
sessionStorage.getItem("evantra_oauth_state"),
);
const tokens = await exchangeEvantraCode(
{ clientId: "evt_client_..." },
{
code,
codeVerifier:
sessionStorage.getItem("evantra_pkce_verifier") ?? "",
redirectUri: REDIRECT_URI,
},
);
// Hand the token to YOUR backend to start your own session
await fetch("/api/session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ accessToken: tokens.access_token }),
});
window.location.assign("/dashboard");
}
void complete();
}, []);
return <p>Completing sign in...</p>;
}Mobile · React Native / Expo
Deep-link redirect with secure verifier storage
Register a reverse-DNS scheme such as com.example.app://oauth/callback against your client before using this flow.
import * as WebBrowser from "expo-web-browser";
import * as SecureStore from "expo-secure-store";
import {
createEvantraAuthorizeUrl,
createEvantraPkcePair,
createEvantraState,
consumeEvantraNativeCallback,
exchangeEvantraCode,
} from "@evantra-identity/react";
const API = "https://evantra-headquarters.onrender.com";
const REDIRECT_URI = "com.example.app://oauth/callback";
const CLIENT_ID = "evt_client_...";
// 1. PKCE + state, held in the OS keychain/keystore
const { verifier, challenge } = await createEvantraPkcePair();
const state = createEvantraState();
await SecureStore.setItemAsync("evantra_pkce_verifier", verifier);
await SecureStore.setItemAsync("evantra_oauth_state", state);
// 2. Open the system browser
const result = await WebBrowser.openAuthSessionAsync(
createEvantraAuthorizeUrl(API, {
clientId: CLIENT_ID,
redirectUri: REDIRECT_URI,
codeChallenge: challenge,
state,
}),
REDIRECT_URI,
);
if (result.type !== "success" || !result.url) {
throw new Error("Sign in was cancelled.");
}
// 3. Read the code from the deep link
const { code } = consumeEvantraNativeCallback(result.url, state);
const tokens = await exchangeEvantraCode(
{ clientId: CLIENT_ID },
{
code,
codeVerifier:
(await SecureStore.getItemAsync("evantra_pkce_verifier")) ?? "",
redirectUri: REDIRECT_URI,
},
);Server / desktop / CLI
The SDK handles exchange and profile loading
import { EvantraOAuthClient, parseEvantraCallback } from "@evantra-identity/sdk";
const client = new EvantraOAuthClient({
clientId: "evt_client_...",
redirectUri: "https://api.example.com/oauth/callback",
scope: "openid profile email",
// clientSecret: "..." // confidential clients only
});
// On your callback route
const { code } = parseEvantraCallback(requestUrl, savedState);
// Exchange AND load the profile in one call
const { tokens, profile } = await client.completeAuthorization({
code,
codeVerifier: savedVerifier,
});
console.log("Signed in as", profile.evantra_id);Optional · hosted sign-in buttons
Link straight to the hosted pages
These buttons point at the hosted sign-in and registration pages. Use them for a simple integration, or use the OAuth flow above when you need your own token exchange.
import {
EvantraIdentityProvider,
EvantraSignInButton,
EvantraRegisterButton,
} from "@evantra-identity/react";
// returnTo is a same-origin path on the identity app,
// so the identity service can never be used as an
// open redirect.
export function LoginSection() {
return (
<EvantraIdentityProvider
config={{
identityWebBaseUrl: "https://identity.evantradebuckman.com",
identityApiBaseUrl: "https://evantra-headquarters.onrender.com",
clientId: "evt_client_...",
}}
>
<EvantraSignInButton returnTo="/welcome">
Sign in with Evantra
</EvantraSignInButton>
<EvantraRegisterButton returnTo="/welcome">
Create account
</EvantraRegisterButton>
</EvantraIdentityProvider>
);
}Endpoints
Identity API surface
/oauth/authorizeStarts the flow and, once consent is given, redirects back to your redirect_uri with a code.
/oauth/tokenExchanges an authorization code or refresh token for tokens.
/oauth/userinfoReturns the OpenID Connect profile for a Bearer access token.
/oauth/revokeRevokes an access or refresh token.
/oauth/introspectReports whether a token is active and what it may access.
Base URL: https://evantra-headquarters.onrender.com
Integration checklist
- Register the OAuth client and its exact redirect URIs first.
- Use PKCE with S256 on every authorization request.
- Send state and verify it when the code returns.
- Keep client secrets server-side only — never in a browser or app binary.
- Exchange the single-use code promptly, and refresh tokens as needed.
- Check email_verified before trusting a contact address.
- Keep app-level authorization and role checks in your product.
Protected by Evantra Identity