Single Sign-On (SSO)

Facebook

Add Facebook sign-in to VitNode - create a Meta app with the Facebook Login use case, set the OAuth redirect URI and register the Facebook SSO adapter.

Facebook sign-in goes through a Meta app with the Facebook Login use case switched on. Meta calls the two credentials an App ID and an App Secret; VitNode calls them clientId and clientSecret, and they are the same two strings.

Quick start

If you already have an App ID and App Secret, this is the entire integration.

.env
FACEBOOK_CLIENT_ID=1234567890123456
FACEBOOK_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
src/vitnode.api.config.ts
import { FacebookSSOApiPlugin } from '@vitnode/core/api/adapters/sso/facebook'
import { buildApiConfig } from '@vitnode/core/vitnode.config'

export const vitNodeApiConfig = buildApiConfig({
  plugins: [],
  authorization: {
    ssoAdapters: [
      FacebookSSOApiPlugin({
        clientId: process.env.FACEBOOK_CLIENT_ID,
        clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
      }),
    ],
  },
})

Restart the API and a Facebook button appears on /login. The rest of this page is how to get those two values.

Set up the Meta app

Sign in to Meta for Developers

Go to developers.facebook.com and sign in with the Facebook account that should own the app. Meta walks you through registering as a developer the first time, which means confirming your contact details before you can create anything.

Create an app

From My Apps, press Create App. The first screen wants an App name - this is what visitors read on the login dialog - and an App contact email.

Meta's Create an app wizard on the App details step, with App name set to VitNode Test and an App contact email filled in

Choose the Facebook Login use case

On the Use cases step, tick Authenticate and request data from users with Facebook Login. This is the one that gives the app an OAuth dialog; the others (Ads Manager, Threads, games) do not, and Meta will not let you combine some of them on one app.

The Use cases step of Meta's app wizard with Authenticate and request data from users with Facebook Login selected

Finish the wizard - the Business step lets you skip connecting a business portfolio, and Finalize creates the app.

Add the OAuth redirect URI

In the app dashboard, open the Facebook Login use case's settings - Use cases → Authenticate and request data from users with Facebook Login → Customize → Settings on a new app, or Products → Facebook Login → Settings on an older one. Paste your URI into Valid OAuth Redirect URIs and save.

VitNode builds this URI itself, from NEXT_PUBLIC_WEB_URL, so it has to match character for character - no trailing slash, no locale prefix.

EnvironmentValid OAuth Redirect URI
Development (NEXT_PUBLIC_WEB_URL unset)http://localhost:3000/login/sso/facebook
Productionhttps://your-domain.com/login/sso/facebook

Meta wants HTTPS in production

Valid OAuth Redirect URIs rejects a plain http:// URL for a public domain, so a production entry has to be https://. http://localhost is the exception Meta makes for development, which is why the table above has two rows rather than one.

Copy the App ID and App Secret

Go to App settings → Basic in the left navigation. The App ID is in plain text; the App Secret is behind a Show button and your password.

The Meta app dashboard sidebar for an app named VitNode Test, with a red arrow pointing at Basic under the expanded App settings section, and an Unpublished badge next to Publish above it

Set the environment variables

Server-side variables, so no NEXT_PUBLIC_ prefix - that prefix is what marks a value for the browser bundle, and an app secret in the browser bundle is an app secret you have published.

.env
FACEBOOK_CLIENT_ID=1234567890123456
FACEBOOK_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Register the adapter

Add FacebookSSOApiPlugin to authorization.ssoAdapters in your API config. It is a plain factory function, not a class - there is no new.

src/vitnode.api.config.ts
import { FacebookSSOApiPlugin } from '@vitnode/core/api/adapters/sso/facebook'
import { buildApiConfig } from '@vitnode/core/vitnode.config'

export const vitNodeApiConfig = buildApiConfig({
  plugins: [],
  authorization: {
    ssoAdapters: [
      FacebookSSOApiPlugin({
        clientId: process.env.FACEBOOK_CLIENT_ID,
        clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
      }),
    ],
  },
})

The redirect URI is computed when this file is evaluated, so restart the API rather than relying on a hot reload:

bun dev
pnpm dev
npm run dev

Verify it works

Ask the API what it thinks it supports:

curl http://localhost:3000/api/@vitnode/core/middleware
# {"isEmail":false,"sso":[{"id":"facebook","name":"Facebook"}]}

Then open /login and press Facebook. You should get Meta's "Continue as ..." dialog, be bounced back to /login/sso/facebook?code=...&state=..., and land on the front page signed in. Your account now has a row in core_users_sso with providerId facebook.

Publish the app

While the app is Unpublished (the badge next to Publish in the screenshot above), only people with a role on it - admins, developers, testers - can sign in. Everyone else is turned away at Meta's dialog before VitNode ever sees a callback. Go to Publish and switch the app live once the round trip works.

public_profile and email are both standard-access permissions, so this does not require App Review. Ask for anything beyond them and it will.

What the adapter asks Facebook for

Useful when you are debugging a login dialog that shows more or fewer permissions than you expected.

SettingValue
Authorize URLhttps://www.facebook.com/v22.0/dialog/oauth
Token URLhttps://graph.facebook.com/v22.0/oauth/access_token
Profile URLhttps://graph.facebook.com/v22.0/me?fields=id,name,email
Scopespublic_profile,email
Provider idfacebook
Fields readid, name, email

The account name VitNode creates comes from Facebook's name field, stripped of characters that cannot appear in a profile URL. The Graph API version is pinned to v22.0 in the adapter, so a Meta version bump never changes what this install sends.

Gotchas

An account with no email cannot sign in

The adapter requires email in the Graph response and answers 400 when it is missing - which happens if the visitor unticks the email permission on the login dialog, or if the account was created with a phone number and has no address at all. The visitor sees the generic error screen, not an explanation.

Facebook's own email verification is not checked

Unlike the Google adapter, which rejects an unverified address, this one takes the Graph API's email at face value. If you need a stronger guarantee, copy the adapter into your own project and add the check - see Custom adapter.

A missing key fails on click, not on boot

A FacebookSSOApiPlugin whose clientId is undefined registers happily - the button renders and the provider is listed - and throws Missing Facebook client ID the moment somebody presses it. A button that only raises an error toast is an environment problem, not a dashboard problem.

An email that already has a password account is a dead end

If someone registered with the same address using the password form, the callback answers 409 and offers them the login page instead. There is no link-an-existing-account screen yet - see how the callback handles accounts.

Next