Single Sign-On (SSO)

Single Sign-On (SSO)

Enable social authentication with Google, Discord, Facebook, or custom OAuth2 providers in VitNode.

VitNode provides built-in OAuth2 single sign-on. Register an adapter in your API configuration, and authentication buttons automatically appear on /login and /register.

Quick start

Register an SSO adapter in apps/api/src/vitnode.api.config.ts:

apps/api/src/vitnode.api.config.ts
import { GoogleSSOApiPlugin } from "@vitnode/core/api/adapters/sso/google"
import { buildApiConfig } from "@vitnode/core/vitnode.config"

export const vitNodeApiConfig = buildApiConfig({
  authorization: {
    ssoAdapters: [
      GoogleSSOApiPlugin({
        clientId: process.env.GOOGLE_CLIENT_ID!,
        clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      }),
    ],
  },
})

A Google button automatically renders on your login and registration forms, brand mark included - the built-in adapters ship their own icons, and a custom one can add its own.


Supported Providers


Callback URLs

When configuring OAuth2 applications in provider developer consoles, use the following redirect URI pattern:

https://your-domain.com/login/sso/{provider_id}

For example: https://your-domain.com/login/sso/google.


When a user signs in via SSO:

  1. Known Identity: The provider account was linked before, so the user is signed straight into it.
  2. Existing Email Match: An account already uses that address. The visitor is asked for that account's password; once it checks out, the provider identity is linked to the account and they are signed in. From then on that provider signs them straight in (case 1). A social login can never take over an account on its own - the password is what proves ownership.
  3. New Visitor: A new user is created with their social display name, email, and avatar.

SSO confirms the email

A provider only hands VitNode an address it has verified itself (the built-in adapters refuse anything else), so a social sign-in doubles as email confirmation:

  • a new visitor starts out with emailVerified: true, even when an email adapter is configured and password sign-ups would have to confirm first;
  • a known identity or a freshly linked account whose address is still unconfirmed is marked confirmed - provided the address the provider returned is the one on the account. An account whose email was changed since the link was made is left alone.

Linking an existing account

The callback answers 409 with a short-lived, signed link offer - the account's email, whether it has a password, and a token good for ten minutes. The login page turns that into a "Connect Provider to your account" form: the email is shown read-only, the visitor types their password, and POST /users/sso/{providerId}/link verifies both before it writes the core_users_sso row and mints a session. The token carries the provider account id, so nothing about which identity gets linked is taken from the browser.

An account created by another provider has no password to confirm with. When an email adapter is configured the form offers Set a password (the ordinary reset flow) and asks them to try the provider again afterwards; without one it points back to the login page.

Every successful link emits user.sso.linked.

The address the provider returns is matched in its canonical form, so a Google account that reports jan.kowalski@gmail.com finds the member who registered as jankowalski@gmail.com instead of quietly becoming a second account. See One Mailbox, One Account.

Learn More