Ludicord
Guide 09 · Discord

Authentication

Understand Ludicord's OAuth2 handshake, encrypted session, and trust boundaries.

Ludicord 3.1.0Public source 20c4889
Authentication journey

Follow one secure sign-in

Interactive
Discord

SDK ready

Discord supplies the channel, guild, and Activity-instance context required to begin safely.

ProducesSigned launch contextkept on the correct side

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

ludicord.config.mjsjs
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

  1. The client requests a one-time state value from /_ludicord/auth/state.
  2. The Discord SDK authorize command returns a short-lived code.
  3. Ludicord sends the code, state, and Activity context to the trusted exchange route.
  4. The server exchanges the code using LUDICORD_DISCORD_CLIENT_SECRET.
  5. The client calls Discord SDK authenticate and verifies the returned user.
  6. 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:

FileWhen it renders
app/auth/loading.tsxSDK connection, authorization, or exchange is pending
app/auth/error.tsxA recoverable SDK, network, or server failure occurs
app/auth/denied.tsxThe player declines or Discord denies authorization

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

components/profile.tsxtsx
1import { useLudicordAuth } from "ludicord/auth";
2
3export 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.

app/api/account/route.tsts
1import { requireLudicordSession } from "ludicord/server";
2import type { LudicordRequest } from "ludicord/server";
3
4export 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

ValueClientServer
Application / Client IDYesYes
Display-ready session userYesYes
Guild, channel, and Activity instance IDsYesYes, verified before trust
Client SecretNeverYes
Session SecretNeverYes
Bot tokenNeverOptional
Raw OAuth access tokenNever exposeInternal exchange only

#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.