Quickstart
Add sign-in to a web app in five minutes with the JavaScript SDK, then call your API with the user's token.
This walks through signing users in to a browser app with @9in-io/sdk, then calling your own API with their access token. For a server-rendered app, follow Server-rendered apps instead; the console steps are the same.
#1. Create a tenant and an application
- Sign in to the console and create a tenant, say
acme. You get two issuers:- production:
https://acme.9in.io - sandbox:
https://acme-sandbox.9in.io(separate users; any password works and one-time codes are always000000)
- production:
- Go to Applications → Create application → Single-Page App and set:
- Allowed callback URLs:
http://localhost:5173/callback - Allowed logout URLs:
http://localhost:5173/ - Allowed web origins:
http://localhost:5173(the browser calls the token endpoint directly, so its origin must be allowed for CORS)
- Allowed callback URLs:
- Copy the Client ID (
app_…). Single-page apps have no client secret.
#2. Install the SDK
npm install @9in-io/sdk#3. Sign in and out
import { NineAuth } from "@9in-io/sdk";
export const auth = new NineAuth({
issuer: "https://acme-sandbox.9in.io", // the sandbox while you develop
clientId: "app_7f3k2m9q",
redirectUri: `${location.origin}/callback`,
});
// A "Sign in" button
document.querySelector("#login")!.addEventListener("click", () => auth.login());
// A "Create account" button opens the sign-up form directly
document.querySelector("#signup")!.addEventListener("click", () => auth.signup());
// On /callback: finish signing in, then go where the user was heading
if (location.pathname === "/callback") {
const { user, returnTo } = await auth.handleRedirect();
console.log(`Signed in as ${user.email}`);
history.replaceState(null, "", returnTo ?? "/");
}
// Anywhere
if (auth.isAuthenticated()) {
const user = await auth.getUser(); // claims from the ID token: sub, email, name, picture…
}
// Sign out of your app and of 9in
document.querySelector("#logout")!.addEventListener("click", () => auth.logout({ returnTo: location.origin + "/" }));login() redirects to the hosted sign-in page with authorization code + PKCE. handleRedirect() checks state, exchanges the code and stores the tokens; by default they are kept in localStorage and refreshed automatically (refresh tokens rotate on every use).
Open your app, click Sign in, and sign in to the sandbox with any email and any password.
#4. Call your API
Tell 9in about your API, so access tokens can be issued for it:
- In the console, go to APIs & Scopes → Create API. Set the identifier to your API's URL, e.g.
https://api.acme.com, and add a scope such asorders:read. - Request tokens for it in the browser:
const auth = new NineAuth({
issuer: "https://acme-sandbox.9in.io",
clientId: "app_7f3k2m9q",
redirectUri: `${location.origin}/callback`,
audience: "https://api.acme.com",
scope: "openid profile email offline_access orders:read",
});
const res = await auth.fetch("https://api.acme.com/orders"); // adds Authorization: Bearer <access token>- Verify the token in your API (Node, Workers, Deno or Bun):
import { verifyAccessToken } from "@9in-io/sdk/server";
export async function handle(request: Request) {
const bearer = request.headers.get("authorization")?.replace(/^Bearer /i, "");
const claims = await verifyAccessToken(bearer, {
issuer: "https://acme-sandbox.9in.io",
audience: "https://api.acme.com",
scope: "orders:read",
}); // throws NineError (invalid_token, insufficient_scope) otherwise
return Response.json({ orders: await listOrders(claims.sub) });
}verifyAccessToken checks the signature against your tenant's public keys (cached), the issuer, audience, expiry and scopes. To also notice revoked tokens within seconds, see Protecting your API.
#5. Go to production
- Add your real URLs to the application (callback, logout and web origin).
- Switch the issuer to
https://acme.9in.io, or to your custom domain. - Users, sessions and activity in production are separate from the sandbox.
#Next
- Turn on more sign-in methods, social login or enterprise SSO.
- Make the pages look like your product with themes.
- Get notified about sign-ups with webhooks.