Tutorial: OAuth From Scratch
Build the install flow yourself, step by step, with no framework in the way.
This walks through exactly what
prebit-examples/oauth-example does, one step at a
time, so you understand every line before you copy it.
1. Register a PartnerApp
In the Developer Portal, create an org (if you haven't), then an app.
You'll get a client_id and client_secret — treat the secret like a
password.
2. Register a redirect URI
While developing, this is a tunnel URL (prebit dev prints one if you
have cloudflared installed) plus /callback. It must match exactly
what you send in step 3 — no partial-path matching.
3. Generate a PKCE pair and build the authorize URL
import { generatePkcePair, buildAuthorizationUrl } from "@prebit/sdk";
const { codeVerifier, codeChallenge } = generatePkcePair();
const state = crypto.randomUUID();
const authorizeUrl = buildAuthorizationUrl({
baseUrl: "https://admin.prebit.in",
clientId: process.env.PREBIT_CLIENT_ID!,
redirectUri: "http://localhost:8787/callback",
scopes: ["read_products", "read_orders"],
codeChallenge,
state,
});codeVerifier and state both need to survive until step 5 — in a real
app, store them server-side (session, signed cookie, or a short-lived DB
row keyed by state). In this from-scratch version, they just live in a
variable because the whole script is one process.
4. Send the merchant there and start a callback server
console.log(`Open this URL: ${authorizeUrl}`);You need something listening on redirectUri to catch the redirect. A
plain http.createServer works fine — see the full example for the
complete version.
5. Verify state, then exchange the code
// req.url is something like /callback?code=...&state=...
const url = new URL(req.url!, "http://localhost:8787");
const code = url.searchParams.get("code")!;
const returnedState = url.searchParams.get("state");
if (returnedState !== state) throw new Error("State mismatch — possible CSRF");
const tokens = await exchangeAuthorizationCode({
baseUrl: "https://admin.prebit.in",
code,
codeVerifier,
redirectUri: "http://localhost:8787/callback",
clientId: process.env.PREBIT_CLIENT_ID!,
clientSecret: process.env.PREBIT_CLIENT_SECRET!,
});6. Prove it worked
const client = new PrebitPartnerClient({ baseUrl: "https://admin.prebit.in", accessToken: tokens.access_token });
const { store } = await client.getStore();
console.log(`Installed on ${store.name}`);If this prints a real store name, you have a working OAuth flow. From
here: persist tokens in your own database keyed by whatever identifies
this installation to you, and set up a webhook receiver (see
Webhooks and
prebit-examples/webhook-listener) if you
subscribed to any topics.
Common mistakes
- Sending the wrong
redirect_urion exchange. It must be byte- identical to what you sent on the authorize step and what's registered on yourPartnerApp— all three. - Losing
codeVerifierbetween steps 3 and 5. If your process restarts (or you're running two server instances behind a load balancer) between generating the PKCE pair and receiving the callback, the exchange fails. Store it somewhere that survives — not just a module-level variable, in anything beyond a single-process demo. - Not checking
state. Skipping this makes your callback vulnerable to CSRF — an attacker tricking a merchant into completing someone else's install flow.