Skip to main content

Better Auth

Official

ThunderID provider helper for the Better Auth Generic OAuth plugin. It returns a typed GenericOAuthConfig for a ThunderID issuer, so there is no ThunderID SDK dependency and no protocol logic to keep in sync.

@thunderid/better-auth
Apache 2.0 licence
Get startedOptionsBetter Auth docs

Get started

Works with a new project or one you already have. Every path below is relative to the project root, and the App Router layout is what the sample uses. Requires Node.js 18 or later and a ThunderID application with an OAuth 2.0 or OIDC (`authorization_code`) client.

1

Start from a Next.js app

Create one, or skip this step and use the app you already have. An existing App Router project needs no restructuring.

bash
npx create-next-app@latest my-app --typescript --app
cd my-app
2

Install the packages

Better Auth does the OAuth 2.0 and OIDC work; the helper supplies the ThunderID configuration.

bash
npm install better-auth @thunderid/better-auth
3

Add the environment variables

`THUNDERID_CLIENT_ID` and `THUNDERID_CLIENT_SECRET` come from the application's Credentials tab in the ThunderID console. `BETTER_AUTH_URL` is the base URL this app is served from, and the callback URL is derived from it.

.env
dotenv
BETTER_AUTH_SECRET=<run: openssl rand -base64 32>
BETTER_AUTH_URL=http://localhost:3000
THUNDERID_ISSUER=https://localhost:8090
THUNDERID_CLIENT_ID=<your-client-id>
THUNDERID_CLIENT_SECRET=<your-client-secret>
Note

Against a ThunderID instance with a self-signed certificate, local development also needs `NODE_TLS_REJECT_UNAUTHORIZED=0`. It disables TLS verification for the whole process, so never set it outside local development.

4

Create the Better Auth server instance

This is the file that wires ThunderID in: `thunderid()` returns the provider configuration, and the Generic OAuth plugin registers it. `nextCookies()` must come last in the plugin array, since it only applies cookies set by plugins before it.

lib/auth.ts
ts
import {thunderid} from '@thunderid/better-auth';
import {betterAuth} from 'better-auth';
import {nextCookies} from 'better-auth/next-js';
import {genericOAuth} from 'better-auth/plugins';

export const auth = betterAuth({
  // Point this at your own database. See better-auth.com/docs/adapters.
  database: yourDatabaseAdapter,
  plugins: [
    genericOAuth({
      config: [
        thunderid({
          clientId: process.env.THUNDERID_CLIENT_ID!,
          clientSecret: process.env.THUNDERID_CLIENT_SECRET!,
          issuer: process.env.THUNDERID_ISSUER!,
        }),
      ],
    }),
    nextCookies(),
  ],
});
5

Mount the route handler

A single catch-all route exposes every Better Auth endpoint, including the callback the next step registers.

app/api/auth/[...all]/route.ts
ts
import {toNextJsHandler} from 'better-auth/next-js';
import {auth} from '../../../../lib/auth';

export const {GET, POST} = toNextJsHandler(auth);
6

Register the redirect URI

Add this callback URL to the same ThunderID application. The path is fixed by the route handler above and the provider id, so only the origin changes between environments.

text
http://localhost:3000/api/auth/callback/thunderid
7

Create the client

No client plugin is needed. The Generic OAuth plugin exposes sign-in through the standard social-provider API.

lib/auth-client.ts
ts
import {createAuthClient} from 'better-auth/react';

export const authClient = createAuthClient();
8

Sign in and out from a component

Any client component can read the session and start the flow.

app/page.tsx
tsx
'use client';

import {authClient} from '../lib/auth-client';

export default function Home() {
  const {data: session, isPending} = authClient.useSession();

  if (isPending) return null;

  if (!session) {
    return (
      <button onClick={() => authClient.signIn.social({provider: 'thunderid', callbackURL: '/'})}>
        Sign in with ThunderID
      </button>
    );
  }

  return (
    <>
      <p>Signed in as {session.user.email}</p>
      <button onClick={() => authClient.signOut()}>Sign out</button>
    </>
  );
}

Options

Full reference

Every option other than issuer is passed through from Better Auth's BaseOAuthProviderOptions and behaves exactly as it does for the built-in provider helpers.

Functions
functionthunderid()

Returns a typed GenericOAuthConfig for a ThunderID issuer.

Options
issuerrequired
string

ThunderID issuer URL, for example https://thunderid.example.com. A trailing slash is trimmed. The OIDC discovery URL is derived as {issuer}/.well-known/openid-configuration, so all endpoints come from the discovery document.

clientIdrequired
string

OAuth client ID.

clientSecretoptional
string

OAuth client secret. Omit for public clients using tokenEndpointAuth: {method: 'none'}.

scopesoptional
string[]

Requested scopes. Defaults to ['openid', 'profile', 'email'].

tokenEndpointAuthoptional
object

Token endpoint authentication method, for example {method: 'client_secret_post'}.

pkceoptional
boolean

Force PKCE on or off. Better Auth enables PKCE by default.

redirectURIoptional
string

Override the callback URL. It must still resolve to Better Auth's callback route for this provider, which ends in `/callback/thunderid`.

endSessionEndpointoptional
string

RP-initiated logout endpoint. Defaults to the discovery document.

postLogoutRedirectURIoptional
string

Where ThunderID returns the user after logout.

disableProviderLogoutoptional
boolean

Skip provider logout on sign-out. Defaults to false.

disableImplicitSignUpoptional
boolean

Require an explicit sign-up request before creating a user. Defaults to false.

disableSignUpoptional
boolean

Reject sign-in for users who do not already exist. Defaults to false.

overrideUserInfooptional
boolean

Refresh the stored user profile from ThunderID on every sign-in. Defaults to false.

ThunderID LogoThunderID Logo

Product

DocsAPIsSDKs
© Copyright Linux Foundation Europe.For web site terms of use, trademark policy and other project policies please see https://linuxfoundation.eu/en/policies.