OIDC proxy / token-exchange flow for Discord OAuth → SpacetimeDB (id_token never refreshes) #1765

Closed
opened 2026-03-13 09:01:37 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @JustKira on GitHub (Aug 23, 2025).

Environment

  • Frontend: SvelteKit (Vite dev server http://localhost:5173)
  • Backend: Hono (Node)
  • Auth library: better-auth with Discord OAuth provider
  • ** Database:** SpacetimeDB (self-hosted, expects OIDC-compliant JWT)

What I’m trying to do

  1. User clicks “Login with Discord”.
  2. I need to obtain a JWT id_token issued by Discord that contains:
    • iss: https://discord.com/api/oauth2
    • aud: <my-client-id>
    • sub: <discord-user-id>
  3. Pass that JWT to SpacetimeDB via .withToken(<id_token>).
  4. SpacetimeDB validates the token against Discord’s public keys and returns the same token back to the client (it does not mint a new one).
  5. On every hot-reload / page refresh I must re-use the same id_token; Discord does not issue a new one unless the user re-auths.

The problem

Discord’s id_token expires after 1 h and cannot be refreshed. After an hour the user is forced to log in again, which is terrible UX.

What I need help with

  • Is there a way to configure better-auth (or a companion plugin) to act as an OIDC proxy so that it:
    1. Accepts the Discord access-token/refresh-token pair,
    2. Re-issues a new, fresh id_token on every page load without forcing the user to re-auth, or
    3. Exposes a local endpoint (e.g. /.well-known/openid-configuration) so that SpacetimeDB can validate directly against the proxy instead of Discord?
  • If the above is impossible, what is the recommended pattern for long-lived front-end sessions when the upstream IdP (Discord) does not support refresh of the id_token?

In other words:
I’m new to OIDC and not sure what’s possible.
Do I actually need to run a full OIDC-proxy myself, or is there a simpler way to keep my Discord-authenticated users logged-in to SpacetimeDB without forcing them to re-authenticate every hour?

  • Is there a built-in (or plug-in) way for better-auth to re-issue a fresh id_token on every page load by using the stored Discord refresh-token, or
  • Can I skip the proxy idea entirely and just let SpacetimeDB validate tokens directly against Discord’s public keys?
    Any guidance on the simplest pattern for long-lived front-end sessions when Discord doesn’t support refresh of the id_token would be appreciated!
// 1. Get Discord id_token (expires in 1 h)
const { data } = await authClient.getSession();   // only good for 1 h
const idToken = data.session.token;                // <- need this to auto-refresh

// 2. Connect to SpacetimeDB
DbConnection.builder()
  .withToken(idToken)   // fails after 1 h
  .build();
Originally created by @JustKira on GitHub (Aug 23, 2025). #### Environment - **Frontend:** SvelteKit (Vite dev server `http://localhost:5173`) - **Backend:** Hono (Node) - **Auth library:** `better-auth` with Discord OAuth provider - ** Database:** SpacetimeDB (self-hosted, expects OIDC-compliant JWT) #### What I’m trying to do 1. User clicks **“Login with Discord”**. 2. I need to obtain a **JWT `id_token`** issued by Discord that contains: - `iss: https://discord.com/api/oauth2` - `aud: <my-client-id>` - `sub: <discord-user-id>` 3. Pass that JWT to **SpacetimeDB** via `.withToken(<id_token>)`. 4. SpacetimeDB validates the token against Discord’s public keys and returns the **same token** back to the client (it does **not** mint a new one). 5. On every hot-reload / page refresh I must **re-use the same id_token**; Discord **does not** issue a new one unless the user re-auths. #### The problem Discord’s `id_token` **expires after 1 h** and cannot be refreshed. After an hour the user is forced to log in again, which is terrible UX. #### What I need help with - Is there a way to configure `better-auth` (or a companion plugin) to act as an **OIDC proxy** so that it: 1. Accepts the Discord access-token/refresh-token pair, 2. Re-issues a **new, fresh `id_token`** on every page load without forcing the user to re-auth, **or** 3. Exposes a local endpoint (e.g. `/.well-known/openid-configuration`) so that SpacetimeDB can validate directly against the proxy instead of Discord? - If the above is impossible, what is the recommended pattern for long-lived front-end sessions when the upstream IdP (Discord) does **not** support refresh of the `id_token`? In other words: I’m new to OIDC and not sure what’s possible. Do I actually need to run a full OIDC-proxy myself, or is there a simpler way to keep my Discord-authenticated users logged-in to SpacetimeDB without forcing them to re-authenticate every hour? - Is there a built-in (or plug-in) way for better-auth to re-issue a fresh id_token on every page load by using the stored Discord refresh-token, or - Can I skip the proxy idea entirely and just let SpacetimeDB validate tokens directly against Discord’s public keys? Any guidance on the simplest pattern for long-lived front-end sessions when Discord doesn’t support refresh of the id_token would be appreciated! ```ts // 1. Get Discord id_token (expires in 1 h) const { data } = await authClient.getSession(); // only good for 1 h const idToken = data.session.token; // <- need this to auto-refresh // 2. Connect to SpacetimeDB DbConnection.builder() .withToken(idToken) // fails after 1 h .build(); ``` #### Related links - Discord docs: https://discord.com/developers/docs/topics/oauth2#authorization-code-grant - SpacetimeDB auth spec: https://spacetimedb.com/docs/http/identity
GiteaMirror added the honosvelte labels 2026-03-13 09:01:37 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Aug 23, 2025):

Hi @JustKira! I'm Dosu and I’m helping the better-auth team.

There’s no built-in way in better-auth to proxy or refresh Discord id_tokens. Discord’s id_token can’t be refreshed, so after it expires, users must re-authenticate. better-auth can mint its own JWTs and expose OIDC endpoints (like /.well-known/openid-configuration), but these tokens will have your own issuer—not Discord’s—and SpacetimeDB would need to trust your better-auth instance as the IdP instead of Discord for this to work source.

If SpacetimeDB requires Discord as the issuer, you’re limited by Discord’s 1-hour expiry and forced re-auth. The recommended pattern for long-lived sessions is to use your own backend session management, issue your own JWTs via better-auth’s OIDC Provider plugin, and configure SpacetimeDB to trust your OIDC provider source.

Let me know if you want details on setting up better-auth as your OIDC provider for SpacetimeDB.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Aug 23, 2025): <!-- Greeting --> Hi @JustKira! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> There’s no built-in way in better-auth to proxy or refresh Discord id_tokens. Discord’s id_token can’t be refreshed, so after it expires, users must re-authenticate. better-auth can mint its own JWTs and expose OIDC endpoints (like `/.well-known/openid-configuration`), but these tokens will have your own issuer—not Discord’s—and SpacetimeDB would need to trust your better-auth instance as the IdP instead of Discord for this to work [source](https://github.com/better-auth/better-auth/blob/577cfb4ab3b16d22f36892c0e843f36abcee80c3/packages/better-auth/src/plugins/oidc-provider/index.ts). If SpacetimeDB requires Discord as the issuer, you’re limited by Discord’s 1-hour expiry and forced re-auth. The recommended pattern for long-lived sessions is to use your own backend session management, issue your own JWTs via better-auth’s OIDC Provider plugin, and configure SpacetimeDB to trust your OIDC provider [source](https://github.com/better-auth/better-auth/discussions/3505#discussioncomment-13825344). Let me know if you want details on setting up better-auth as your OIDC provider for SpacetimeDB. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/5270ba88-0e25-46b6-9e21-59f93b626505?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/5270ba88-0e25-46b6-9e21-59f93b626505?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/5270ba88-0e25-46b6-9e21-59f93b626505?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/5270ba88-0e25-46b6-9e21-59f93b626505?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/5270ba88-0e25-46b6-9e21-59f93b626505?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/5270ba88-0e25-46b6-9e21-59f93b626505?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/5270ba88-0e25-46b6-9e21-59f93b626505?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4179)
Author
Owner

@JustKira commented on GitHub (Aug 23, 2025):

SpacetimeDB doesn’t require Discord; any OpenID-compliant provider will work. I’d like to understand the setup in more detail. Since I can’t serve a UI from the same backend where the Better-Auth server is configured, do I need a separate SvelteKit frontend that has Better-Auth integrated so it can handle the OIDC flow? Also, a quick note: all of my apps share the same root domain, so cookie sharing across subdomains is already handled.

@dosu

@JustKira commented on GitHub (Aug 23, 2025): SpacetimeDB doesn’t require Discord; any OpenID-compliant provider will work. I’d like to understand the setup in more detail. Since I can’t serve a UI from the same backend where the Better-Auth server is configured, do I need a separate SvelteKit frontend that has Better-Auth integrated so it can handle the OIDC flow? Also, a quick note: all of my apps share the same root domain, so cookie sharing across subdomains is already handled. @dosu
Author
Owner

@dosubot[bot] commented on GitHub (Aug 23, 2025):

You do not need to serve your UI from the same backend as better-auth. The recommended setup is to run better-auth as a backend service (with OIDC Provider and OAuth plugins), and have your SvelteKit frontend as a separate app that integrates with better-auth via its client library. Just point the SvelteKit client to your better-auth backend’s API endpoints using the baseURL option. This lets your frontend handle the OIDC flow, session management, and sign-in/out via API calls to better-auth, even when they’re on different servers or subdomains (docs, discussion).

Since all your apps share the same root domain, you can enable cross-subdomain cookie sharing by setting crossSubDomainCookies.enabled to true and domain to ".yourdomain.com" in your better-auth config. Make sure CORS is configured on the backend, frontend requests use credentials: 'include', and all frontend origins are listed in trustedOrigins (cookie docs, issue).

For the OIDC flow, use the genericOAuth plugin on the backend and genericOAuthClient on the frontend. Register the callback URL (e.g., /api/auth/oauth2/callback/:providerId) in both your better-auth backend and your OAuth provider settings (plugin docs).

This architecture is common and fully supported—your SvelteKit frontend can handle all auth flows and session logic by talking to the better-auth backend, and SpacetimeDB can trust JWTs issued by your better-auth OIDC provider.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Aug 23, 2025): <!-- Answer --> You do not need to serve your UI from the same backend as better-auth. The recommended setup is to run better-auth as a backend service (with OIDC Provider and OAuth plugins), and have your SvelteKit frontend as a separate app that integrates with better-auth via its client library. Just point the SvelteKit client to your better-auth backend’s API endpoints using the baseURL option. This lets your frontend handle the OIDC flow, session management, and sign-in/out via API calls to better-auth, even when they’re on different servers or subdomains ([docs](https://better-auth.com/docs/concepts/client), [discussion](https://github.com/better-auth/better-auth/discussions/3505)). Since all your apps share the same root domain, you can enable cross-subdomain cookie sharing by setting crossSubDomainCookies.enabled to true and domain to ".yourdomain.com" in your better-auth config. Make sure CORS is configured on the backend, frontend requests use credentials: 'include', and all frontend origins are listed in trustedOrigins ([cookie docs](https://better-auth.com/docs/concepts/cookies), [issue](https://github.com/better-auth/better-auth/issues/3874)). For the OIDC flow, use the genericOAuth plugin on the backend and genericOAuthClient on the frontend. Register the callback URL (e.g., /api/auth/oauth2/callback/:providerId) in both your better-auth backend and your OAuth provider settings ([plugin docs](https://better-auth.com/docs/plugins/generic-oauth)). This architecture is common and fully supported—your SvelteKit frontend can handle all auth flows and session logic by talking to the better-auth backend, and SpacetimeDB can trust JWTs issued by your better-auth OIDC provider. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/94eb97a8-27e1-4bf0-a72a-b4fd0b95d70d?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/94eb97a8-27e1-4bf0-a72a-b4fd0b95d70d?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/94eb97a8-27e1-4bf0-a72a-b4fd0b95d70d?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/94eb97a8-27e1-4bf0-a72a-b4fd0b95d70d?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/94eb97a8-27e1-4bf0-a72a-b4fd0b95d70d?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/94eb97a8-27e1-4bf0-a72a-b4fd0b95d70d?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/94eb97a8-27e1-4bf0-a72a-b4fd0b95d70d?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4179)
Author
Owner

@JustKira commented on GitHub (Aug 23, 2025):

Still don't quite get it provide some Sudo code if possible i need to understand general basic config also where does Discord OAuth play with that system like provide general guides and how auth Flow will work

@dosu

@JustKira commented on GitHub (Aug 23, 2025): Still don't quite get it provide some Sudo code if possible i need to understand general basic config also where does Discord OAuth play with that system like provide general guides and how auth Flow will work @dosu
Author
Owner

@dosubot[bot] commented on GitHub (Aug 23, 2025):

Here’s a high-level guide and pseudocode for setting up better-auth as your OIDC provider with Discord OAuth, SvelteKit frontend, and SpacetimeDB as the OIDC client.


1. better-auth Backend (Hono/Node) – OIDC Provider + Discord OAuth

// auth.ts (backend)
import { betterAuth } from "better-auth";
import { genericOAuth, oidcProvider, jwt } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    // Discord OAuth provider config
    genericOAuth({
      config: [
        {
          providerId: "discord",
          clientId: process.env.DISCORD_CLIENT_ID,
          clientSecret: process.env.DISCORD_CLIENT_SECRET,
          authorizationUrl: "https://discord.com/oauth2/authorize",
          tokenUrl: "https://discord.com/api/oauth2/token",
          userInfoUrl: "https://discord.com/api/users/@me",
          scopes: ["identify", "email"],
          redirectURI: "https://your-auth-domain.com/api/auth/oauth2/callback/discord"
        }
      ]
    }),
    // OIDC provider plugin (issues JWTs for SpacetimeDB)
    oidcProvider({
      loginPage: "/sign-in", // your custom login page
      consentPage: "/consent", // optional
      useJWTPlugin: true,      // sign tokens with JWT plugin keys
      trustedClients: [
        {
          clientId: "spacetimedb-client",
          clientSecret: "your-client-secret",
          redirectURLs: ["https://your-spacetimedb-app/callback"],
          skipConsent: true
        }
      ]
    }),
    jwt() // enables JWKS endpoint for token verification
  ],
  advanced: {
    crossSubDomainCookies: {
      enabled: true,
      domain: ".yourdomain.com"
    }
  }
});
  • Register https://your-auth-domain.com/api/auth/oauth2/callback/discord as the redirect URI in your Discord developer portal.
  • Run migrations: npx @better-auth/cli migrate
  • Your OIDC endpoints will be exposed at /.well-known/openid-configuration, /oauth2/authorize, /oauth2/token, /oauth2/userinfo, etc.
  • SpacetimeDB will trust JWTs issued by your better-auth instance, not Discord.
    Reference | Reference

2. SvelteKit Frontend – Integrate better-auth Client

// lib/auth-client.ts (frontend)
import { createAuthClient } from "better-auth/client";
import { genericOAuthClient, oidcClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  baseURL: "https://your-auth-domain.com/api/auth", // point to your better-auth backend
  plugins: [genericOAuthClient(), oidcClient()]
});

// Usage in your login page/component
const session = await authClient.useSession(); // get current session

// Trigger Discord login
await authClient.signIn.oauth2({
  providerId: "discord",
  callbackURL: "/dashboard" // where to redirect after login
});

// Get OIDC id_token for SpacetimeDB
const { data } = await authClient.getSession();
const idToken = data.session.token; // JWT issued by better-auth
  • The frontend calls signIn.oauth2 to start the Discord OAuth flow.
  • After login, the frontend gets a JWT id_token from better-auth (not Discord) for SpacetimeDB.
    Reference

3. SpacetimeDB – Trust better-auth as OIDC Provider

Configure SpacetimeDB to accept JWTs from your better-auth instance:

  • Set the OIDC issuer to https://your-auth-domain.com
  • Set JWKS endpoint to https://your-auth-domain.com/api/auth/.well-known/jwks.json
  • Use the id_token from better-auth in .withToken(idToken)

Auth Flow Overview

  1. User clicks “Login with Discord” in SvelteKit frontend.
  2. Frontend calls better-auth backend to start Discord OAuth.
  3. User authenticates with Discord, redirected back to better-auth.
  4. better-auth exchanges code for Discord tokens, fetches user info, creates session.
  5. better-auth issues its own JWT id_token (OIDC) for the user.
  6. Frontend gets this id_token and passes it to SpacetimeDB.
  7. SpacetimeDB validates the JWT against better-auth’s JWKS endpoint.
  8. Session persists via cookies across subdomains.

Discord OAuth is just the upstream identity provider. better-auth acts as the OIDC proxy, issuing long-lived JWTs for your apps and SpacetimeDB. You never use Discord’s id_token directly—your users stay logged in as long as your better-auth session is valid.

Let me know if you want more details on any step or config!

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Aug 23, 2025): <!-- Answer --> Here’s a high-level guide and pseudocode for setting up better-auth as your OIDC provider with Discord OAuth, SvelteKit frontend, and SpacetimeDB as the OIDC client. --- ### 1. **better-auth Backend (Hono/Node) – OIDC Provider + Discord OAuth** ```ts // auth.ts (backend) import { betterAuth } from "better-auth"; import { genericOAuth, oidcProvider, jwt } from "better-auth/plugins"; export const auth = betterAuth({ plugins: [ // Discord OAuth provider config genericOAuth({ config: [ { providerId: "discord", clientId: process.env.DISCORD_CLIENT_ID, clientSecret: process.env.DISCORD_CLIENT_SECRET, authorizationUrl: "https://discord.com/oauth2/authorize", tokenUrl: "https://discord.com/api/oauth2/token", userInfoUrl: "https://discord.com/api/users/@me", scopes: ["identify", "email"], redirectURI: "https://your-auth-domain.com/api/auth/oauth2/callback/discord" } ] }), // OIDC provider plugin (issues JWTs for SpacetimeDB) oidcProvider({ loginPage: "/sign-in", // your custom login page consentPage: "/consent", // optional useJWTPlugin: true, // sign tokens with JWT plugin keys trustedClients: [ { clientId: "spacetimedb-client", clientSecret: "your-client-secret", redirectURLs: ["https://your-spacetimedb-app/callback"], skipConsent: true } ] }), jwt() // enables JWKS endpoint for token verification ], advanced: { crossSubDomainCookies: { enabled: true, domain: ".yourdomain.com" } } }); ``` - Register `https://your-auth-domain.com/api/auth/oauth2/callback/discord` as the redirect URI in your Discord developer portal. - Run migrations: `npx @better-auth/cli migrate` - Your OIDC endpoints will be exposed at `/.well-known/openid-configuration`, `/oauth2/authorize`, `/oauth2/token`, `/oauth2/userinfo`, etc. - SpacetimeDB will trust JWTs issued by your better-auth instance, not Discord. [Reference](https://better-auth.com/docs/plugins/generic-oauth) | [Reference](https://better-auth.com/docs/plugins/oidc-provider) --- ### 2. **SvelteKit Frontend – Integrate better-auth Client** ```ts // lib/auth-client.ts (frontend) import { createAuthClient } from "better-auth/client"; import { genericOAuthClient, oidcClient } from "better-auth/client/plugins"; export const authClient = createAuthClient({ baseURL: "https://your-auth-domain.com/api/auth", // point to your better-auth backend plugins: [genericOAuthClient(), oidcClient()] }); // Usage in your login page/component const session = await authClient.useSession(); // get current session // Trigger Discord login await authClient.signIn.oauth2({ providerId: "discord", callbackURL: "/dashboard" // where to redirect after login }); // Get OIDC id_token for SpacetimeDB const { data } = await authClient.getSession(); const idToken = data.session.token; // JWT issued by better-auth ``` - The frontend calls `signIn.oauth2` to start the Discord OAuth flow. - After login, the frontend gets a JWT id_token from better-auth (not Discord) for SpacetimeDB. [Reference](https://better-auth.com/docs/plugins/generic-oauth) --- ### 3. **SpacetimeDB – Trust better-auth as OIDC Provider** Configure SpacetimeDB to accept JWTs from your better-auth instance: - Set the OIDC issuer to `https://your-auth-domain.com` - Set JWKS endpoint to `https://your-auth-domain.com/api/auth/.well-known/jwks.json` - Use the id_token from better-auth in `.withToken(idToken)` --- ### **Auth Flow Overview** 1. User clicks “Login with Discord” in SvelteKit frontend. 2. Frontend calls better-auth backend to start Discord OAuth. 3. User authenticates with Discord, redirected back to better-auth. 4. better-auth exchanges code for Discord tokens, fetches user info, creates session. 5. better-auth issues its own JWT id_token (OIDC) for the user. 6. Frontend gets this id_token and passes it to SpacetimeDB. 7. SpacetimeDB validates the JWT against better-auth’s JWKS endpoint. 8. Session persists via cookies across subdomains. --- **Discord OAuth is just the upstream identity provider. better-auth acts as the OIDC proxy, issuing long-lived JWTs for your apps and SpacetimeDB. You never use Discord’s id_token directly—your users stay logged in as long as your better-auth session is valid.** Let me know if you want more details on any step or config! <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/81b893bd-82b2-46f9-9bd8-643e253f2d40?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/81b893bd-82b2-46f9-9bd8-643e253f2d40?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/81b893bd-82b2-46f9-9bd8-643e253f2d40?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/81b893bd-82b2-46f9-9bd8-643e253f2d40?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/81b893bd-82b2-46f9-9bd8-643e253f2d40?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/81b893bd-82b2-46f9-9bd8-643e253f2d40?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/81b893bd-82b2-46f9-9bd8-643e253f2d40?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4179)
Author
Owner

@ping-maxwell commented on GitHub (Oct 3, 2025):

Hey first of all I just want to say I'm not 100% sure on everything here, I've never personally used SpaceTimeDB myself and am not sure how or why you need an id_token for a DB in the first place.

With that, based off what I understand, here are my thoughts:
You don’t need a full OIDC proxy; mint your own app JWT and rotate it using Discord’s refresh token, then let SpacetimeDB accept that app JWT instead of Discord’s id_token.

The core constraint is Discord’s id_token is short‑lived and non‑refreshable. Treat it only as a one‑time proof of identity, then issue your own durable session and short‑lived DB tokens.

  1. Use Discord OAuth to get access_token + refresh_token and a one‑hour id_token. Verify the id_token server‑side once to bind the Discord user (iss/aud/sub).
  2. On each page load (or when the DB token is near expiry), your backend mints a fresh app JWT for SpacetimeDB, signed by your keys, containing claims SpacetimeDB needs (sub = discord user id, aud = spacetime client, exp ~ 5–15 min). Return it to the client, not the Discord id_token.
  3. Configure SpacetimeDB to trust your issuer and keys (JWKs). It validates your app JWT against your JWKS, not Discord’s. This removes the one‑hour Discord id_token limitation while preserving identity integrity.

Regarding how you would integrate this with BetterAuth:

  1. After the OAuth callback, persist the Discord user’s sub and the refresh_token in your user record.
  2. Add a server endpoint, e.g. GET /db-token, that:
    • Validates the user session.
    • Optionally refreshes the Discord access_token if you need to call Discord APIs.
    • Mints a short‑lived JWT for SpacetimeDB signed by your private key and with iss = your domain, aud = spacetime client/app, sub = discord user id, exp = now + 10m.
  3. Then you can use that JWT as you need for SpaceTimeDB.

Let me know if this helps!

@ping-maxwell commented on GitHub (Oct 3, 2025): Hey first of all I just want to say I'm not 100% sure on everything here, I've never personally used SpaceTimeDB myself and am not sure how or why you need an id_token for a DB in the first place. With that, based off what I understand, here are my thoughts: You don’t need a full OIDC proxy; mint your own app JWT and rotate it using Discord’s refresh token, then let SpacetimeDB accept that app JWT instead of Discord’s id_token. The core constraint is Discord’s id_token is short‑lived and non‑refreshable. Treat it only as a one‑time proof of identity, then issue your own durable session and short‑lived DB tokens. 1. Use Discord OAuth to get access_token + refresh_token and a one‑hour id_token. Verify the id_token server‑side once to bind the Discord user (iss/aud/sub). 2. On each page load (or when the DB token is near expiry), your backend mints a fresh app JWT for SpacetimeDB, signed by your keys, containing claims SpacetimeDB needs (sub = discord user id, aud = spacetime client, exp ~ 5–15 min). Return it to the client, not the Discord id_token. 3. Configure SpacetimeDB to trust your issuer and keys (JWKs). It validates your app JWT against your JWKS, not Discord’s. This removes the one‑hour Discord id_token limitation while preserving identity integrity. Regarding how you would integrate this with BetterAuth: 1. After the OAuth callback, persist the Discord user’s sub and the refresh_token in your user record. 2. Add a server endpoint, e.g. GET `/db-token`, that: * Validates the user session. * Optionally refreshes the Discord access_token if you need to call Discord APIs. * Mints a short‑lived JWT for SpacetimeDB signed by your private key and with iss = your domain, aud = spacetime client/app, sub = discord user id, exp = now + 10m. 3. Then you can use that JWT as you need for SpaceTimeDB. Let me know if this helps!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1765