Trust & Transparency

How Login with Discord Works

Last updated: July 15, 2026

A moderator asked a fair question: is there any evidence of what happens to your Discord token when you log in here? This page answers that directly, with the actual code — not a summary of it.

1. Short Answer

Your Discord access token is used exactly twice — to read your username and your server list — then discarded. It is never stored, never logged, and never leaves the single request that handles login.

What does get stored is a small, signed session — your Discord ID, username, avatar hash, and the list of servers where you have moderation permissions. No token, no email, no password. Sections 3 and 4 below show the exact code that makes this true.

2. What "Login with Discord" Actually Does

This uses Discord's standard OAuth2 Authorization Code flow — the same mechanism used by thousands of sites that offer "Login with Discord." Step by step:

  1. You click Log In and are redirected to Discord's own login/consent screen — never a form on this site.
  2. Discord shows you exactly what's being requested (see Section 3) and asks you to approve it.
  3. If you approve, Discord redirects back to /api/auth/discord/callback with a one-time authorization code.
  4. My server exchanges that code for an access token directly with Discord's API.
  5. That token is used twice — to fetch your profile and your server list — then goes out of scope and is discarded.
  6. A signed session cookie is issued so you stay logged in; the raw Discord token plays no further part.

A random state value is generated per login attempt and checked on callback, which is the standard defense against CSRF on this flow:

const state = crypto.randomBytes(24).toString("hex");
// ...sent to Discord, and stored in an httpOnly cookie

// on callback:
if (!code || !state || state !== storedState) {
  return NextResponse.redirect(new URL("/?auth=invalid_state", request.url));
}

3. Permissions (Scopes) Requested

Discord shows you the requested scopes on its own consent screen before you approve anything. This site requests exactly two:

  • identify — your Discord user ID, username, and avatar.
  • guilds — the list of servers you're in, so I can figure out which ones you moderate.

That's it. This is not the botscope (that's a separate, explicit "Add to Discord" action with its own permission screen), and it is not guilds.join, email, or messages.read. Logging in cannot post on your behalf, read your DMs, join servers, or see your email address — it can only read your profile and guild list.

4. Do I Access or Store Your Discord Token?

I access it, briefly, because that's how OAuth2 works — there's no way to read your username or server list without it. I do not store it. Here is the callback handler, trimmed to the token-relevant lines, exactly as it runs in production:

const tokenRes = await fetch(DISCORD_TOKEN_URL, {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: tokenBody.toString(), // client_id, client_secret, code, redirect_uri
  cache: "no-store",
});

const tokenData = (await tokenRes.json()) as { access_token?: string };

// Used once, to read your profile:
const userRes = await fetch(DISCORD_USER_URL, {
  headers: { Authorization: `Bearer ${tokenData.access_token}` },
});

// Used a second time, to read your server list:
const guildsRes = await fetch(DISCORD_GUILDS_URL, {
  headers: { Authorization: `Bearer ${tokenData.access_token}` },
});

// tokenData is a local variable inside this one request handler.
// It is never written to a cookie, a database, or a log line,
// and never sent anywhere other than discord.com's own API.

Once this function returns, tokenData is gone. The refresh_token Discord also returns is never even read out of the response — I only pull access_token, and nothing keeps you logged in past your 7-day session other than logging in again.

6. Is This Built on a Known Auth Library?

No — and I want to be upfront about that rather than imply otherwise. There's no NextAuth/Auth.js, Clerk, Lucia, or Passport here. The entire flow is hand-written using Node's built-in crypto module and the standard fetch API, across three small files.

The tradeoff cuts both ways: a widely-used library gets far more eyes and battle-testing at scale, but a hand-rolled implementation this small is fully readable in one sitting — nothing is hidden behind a dependency. It follows the same fundamentals a library would enforce: the standard OAuth2 Authorization Code flow, a random per-login CSRF state, and a signed, HttpOnly session cookie. If that ever changes, this page gets updated to match.

7. How the Dashboard Talks to the Bot

Your Discord token plays no role here either. When you use the server dashboard — say, uploading a Custom Game character image — the request is authorized by checking your signed session's moderatedGuildslist against the server you're acting on. The actual call to the bot's backend then authenticates with a static, server-only secret — never anything derived from your login.

8. Session Expiry & Logging Out

Sessions expire automatically after 7 days. You can also end one immediately from the Logout button in the header, which clears the session cookie server-side. There is nothing to "revoke" on Discord's end from this — since no token was retained, there's nothing outstanding for Discord to revoke either. If you want to fully disconnect this app from your Discord account, you can remove it under Discord's own Authorized Apps settings.

9. Common Questions

"Is there evidence you don't access my token maliciously?"

I do access it — that's unavoidable with OAuth2 — but only to call Discord's own /users/@me and /users/@me/guildsendpoints, shown verbatim in Section 4. It's never sent anywhere else, stored, or logged.

Can you post messages or take actions as me?

No. The scopes requested (Section 3) don't allow it, and the token isn't retained even if they did.

Could this change without you updating this page?

If the login flow ever changes, this page gets updated alongside it — the "Last updated" date at the top reflects the current behavior, not a one-time snapshot.

10. Questions or Concerns

If anything here doesn't match what you're seeing, or you'd like to walk through specifics, reach me through the Discord support server. I'd rather answer a direct question than have it stay a doubt.