Authentication
Understand Ludicord's OAuth2 handshake, encrypted session, and trust boundaries.
Follow one secure sign-in
SDK ready
Discord supplies the channel, guild, and Activity-instance context required to begin safely.
Ludicord can complete Discord OAuth2 inside the Activity, authenticate the Embedded App SDK, and issue an encrypted server session. The framework coordinates the flow; your application chooses scopes and designs the three visible auth states.
#Enable required authentication
1export default defineConfig({2 discord: {3 clientId: process.env.LUDICORD_DISCORD_CLIENT_ID,4 scopes: ["identify", "guilds"],5 auth: {6 required: true,7 session: "encrypted-cookie",8 },9 },10});
When required is true, Ludicord completes authentication before rendering the normal Activity UI. The generated app/auth files control what the player sees while that happens.
#Understand the handshake
- The client requests a one-time state value from
/_ludicord/auth/state. - The Discord SDK
authorizecommand returns a short-lived code. - Ludicord sends the code, state, and Activity context to the trusted exchange route.
- The server exchanges the code using
LUDICORD_DISCORD_CLIENT_SECRET. - The client calls Discord SDK
authenticateand verifies the returned user. - The client confirms the exchange; the server issues the final encrypted session.
Pending credentials cannot authorize API routes or WebSocket connections. A failed SDK authentication clears the pending server state.
#Render auth states
The generator creates three automatic boundaries:
Keep denied and error states different. A denial explains how to reopen or consent; an error offers a retry and displays a safe message.
#Read auth state in React
1import { useLudicordAuth } from "ludicord/auth";23export function Profile() {4 const auth = useLudicordAuth();5 if (auth.status !== "authenticated") return null;6 return <button onClick={() => auth.logout()}>{auth.user?.displayName} · Sign out</button>;7}
Use useLudicordSession() when only session data is needed, useAuthStatus() for status-only UI, and useAuthError() for a custom error surface.
#Enforce the session on the server
Client state is useful for rendering, not authorization. API routes should use the request's verified session.
1import { requireLudicordSession } from "ludicord/server";2import type { LudicordRequest } from "ludicord/server";34export function GET(request: LudicordRequest) {5 const session = requireLudicordSession(request);6 return Response.json({ user: session.user, guildId: session.guildId });7}
WebSocket connections receive the same session as client.ludicord. Expired, missing, development, or still-pending credentials are rejected by production routes.
#Keep credentials on the correct side
#Session behavior
The session includes the user, application ID, granted scopes, issue/expiry time, and available guild, channel, and Activity-instance context. Ludicord automatically expires client state when the session reaches expiresAt.
Use a random LUDICORD_SESSION_SECRET with at least 32 characters. Rotating it invalidates existing encrypted sessions, which is usually the safest outcome after a secret exposure.
#Next step
Open Discord data to read normalized values and the complete raw payloads returned by Discord.