Flagship demo: Sign in with Scrydon + Chat API
Build a tiny React app that signs users in with Scrydon as OIDC provider, then calls a chat deployment using the access token. End-to-end proof that the IdP, scopes, and resource APIs work together.
This demo is the one to run when you want to show somebody — a prospect, an internal stakeholder, a new developer on your team — that the Scrydon IdP actually does something useful. It covers the whole loop:
- A user hits an external app (not Scrydon).
- The app kicks them to Scrydon to sign in.
- Scrydon issues an access token scoped to
chat. - The app uses that token to talk to a Scrydon chat deployment on behalf of the signed-in user, with full tenant + workspace isolation.
If this works, the IdP, consent flow, scope enforcement, workspace binding, and the Chat resource server are all correctly wired.
What you'll build
A single-page React app with three screens:
- Sign in — one button that starts the OIDC flow.
- Signed-in dashboard — shows the user's name and email from the ID token.
- Chat panel — streams responses from a Scrydon chat deployment using the user's access token.
The app lives on http://localhost:3000 during development. You can ship it
to any static host later.
Prerequisites
- A Scrydon workspace with at least one chat deployment published.
If you don't have one, create it at
apps → chat → New Chatand publish the deployment. - A Registered Mini App with the
chatscope enabled and redirect URI set tohttp://localhost:3000/callback. See Register an OAuth client. - Node.js 20+ and a package manager (bun / pnpm / npm).
Option A — Use the built-in boilerplate prompt
When you click Register Mini App, Scrydon's "Get Started" panel offers you a ready-made prompt you can paste into Lovable, v0, or Bolt to scaffold a working app. This is the fastest path — the prompt already includes the correct client ID, redirect URI, scopes, and base URL.
Open the Get Started panel
Right after you click Register App, the dialog switches to the "Get Started" panel with your new client IDs and a tabbed Boilerplate Prompt.
Pick your tool
Lovable, v0, Bolt, or Manual. Copy the prompt.
Paste it into the tool
The generated app will include the OIDC sign-in flow, token handling, and a working chat UI wired to your workspace. You'll have a running demo in minutes.
Option B — Manual scaffold (reference implementation)
If you want to understand what the boilerplate does — or you're integrating into an existing app — here is the minimum viable implementation.
1. Install dependencies
bun create vite@latest scrydon-demo --template react-ts
cd scrydon-demo
bun add oidc-client-tsoidc-client-ts is a well-maintained OIDC library that handles PKCE,
silent refresh, and state storage for you. Any other OIDC library
(e.g. openid-client for Node backends) works equivalently.
2. Configure the OIDC client
// src/auth.ts
import { UserManager, WebStorageStateStore } from "oidc-client-ts";
const AUTH_URL = import.meta.env.VITE_AUTH_URL; // e.g. https://staging.api-platform.scrydon.com
const CLIENT_ID = import.meta.env.VITE_CLIENT_ID;
export const userManager = new UserManager({
authority: AUTH_URL + "/api/auth",
client_id: CLIENT_ID,
redirect_uri: "http://localhost:3000/callback",
response_type: "code",
scope: "openid profile email chat",
userStore: new WebStorageStateStore({ store: window.sessionStorage }),
// PKCE is the default for public clients
});The authority URL is the base Scrydon auth path — the library appends
.well-known/openid-configuration itself.
3. Wire the sign-in + callback routes
// src/App.tsx
import { useEffect, useState } from "react";
import { userManager } from "./auth";
import type { User } from "oidc-client-ts";
import { ChatPanel } from "./ChatPanel";
export function App() {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
// Handle the OAuth callback on /callback
if (window.location.pathname === "/callback") {
userManager.signinRedirectCallback().then((u) => {
setUser(u);
window.history.replaceState({}, "", "/");
});
return;
}
userManager.getUser().then(setUser);
}, []);
if (!user) {
return (
<button onClick={() => userManager.signinRedirect()}>
Sign in with Scrydon
</button>
);
}
return (
<div>
<header>
<p>Signed in as {user.profile.email}</p>
<button onClick={() => userManager.signoutRedirect()}>Sign out</button>
</header>
<ChatPanel accessToken={user.access_token} />
</div>
);
}4. Call the Chat API with the access token
// src/ChatPanel.tsx
import { useState } from "react";
const AGENTIC_URL = import.meta.env.VITE_AGENTIC_URL; // e.g. https://staging.agentic.scrydon.com
const DEPLOYMENT_ID = import.meta.env.VITE_DEPLOYMENT_ID;
export function ChatPanel({ accessToken }: { accessToken: string }) {
const [messages, setMessages] = useState<
Array<{ role: "user" | "assistant"; content: string }>
>([]);
const [input, setInput] = useState("");
async function send() {
const next = [...messages, { role: "user" as const, content: input }];
setMessages(next);
setInput("");
const res = await fetch(
`${AGENTIC_URL}/api/chat/deployments/${DEPLOYMENT_ID}/messages`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({ messages: next }),
},
);
const data = await res.json();
setMessages([...next, { role: "assistant", content: data.reply }]);
}
return (
<div>
<ul>
{messages.map((m, i) => (
<li key={i}>
<strong>{m.role}:</strong> {m.content}
</li>
))}
</ul>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && send()}
/>
<button onClick={send}>Send</button>
</div>
);
}The chat API path and request shape shown here is illustrative. Your
specific deployment may expose a streaming variant, tool-calling
metadata, or different field names — check the app details in Scrydon
or use the TypeScript SDK (@scrydon/sdk) for a typed experience.
5. Environment variables
Create .env.local:
VITE_AUTH_URL=https://staging.api-platform.scrydon.com
VITE_AGENTIC_URL=https://staging.agentic.scrydon.com
VITE_CLIENT_ID=paste-from-registered-apps
VITE_DEPLOYMENT_ID=paste-from-your-chat-deploymentCopy the URLs from the Identity tab on Settings → Platform → Identity,
the client ID from the Registered Apps "Get Started" panel, and the
deployment ID from the chat deployment's detail page.
6. Run it
bun devOpen http://localhost:3000, click Sign in with Scrydon, complete the flow, and try a chat message.
What to show in a live demo
| Moment | What you demonstrate |
|---|---|
| Click Sign in with Scrydon | External app is not Scrydon — the user is about to be federated in. |
| Redirect to Scrydon sign-in | The auth host (staging.api-platform.scrydon.com or equivalent) serves the branded sign-in page. |
| Consent screen (for untrusted clients) | Explicit, user-visible scope grant — a compliance bullet point you can point to. |
| Back to the demo app with tokens | PKCE-secured authorization code flow. Open devtools → Network tab to show the /oauth2/token exchange. |
| Decode the ID token | jwt.io shows email, name, org_id, workspace_id, environment_id — proves tenant binding. |
| Send a chat message | Demonstrates scope enforcement on the resource server. |
| Open a second workspace or environment, switch the client ID | Same user, same IdP, but different environment_id claim → different chat responses. Proves isolation. |
Story-line for an external prospect
"Your support portal lets customers sign in with their Scrydon identity. Because we're the IdP, you don't store passwords, you don't manage MFA, and you don't keep your own user table. Every token we issue carries the workspace and environment the user belongs to, so the chat they talk to is exactly the one their tenant admin configured — no cross-tenant leakage, enforced at the API boundary."
Troubleshooting
The user signs in but the app can't call the Chat API
- Make sure the
chatscope is on your Mini App's Allowed Scopes list and that your OIDC client requested it.openid profile emailon its own is not enough to call resource APIs. - Decode the access token (if it's a JWT) or hit
/oauth2/introspectto confirmchatis present in thescopeclaim.
401 from the chat deployment
- The
Authorization: Bearer ...header must use the access token, not the ID token. The ID token identifies the user to the client; the access token authorizes calls to resource APIs.
CORS error calling the agentic app
- The agentic app's CORS allow-list is derived from registered redirect
URIs. Your local origin (
http://localhost:3000) must be present on your Mini App; if it isn't, add it to the redirect URIs list (the origin portion is what CORS enforces).
The consent screen appears every time, for every user
- That's expected for non-trusted clients. Only the first-party Scrydon agentic app is in the trusted-client set by default. If you want your own Mini App to skip consent (for example, a corporate internal tool), contact Scrydon support to add it to the trusted set for your tenant.