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.
FACEBOOK_CLIENT_ID=1234567890123456
FACEBOOK_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxximport { 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.

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.

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.
| Environment | Valid OAuth Redirect URI |
|---|---|
Development (NEXT_PUBLIC_WEB_URL unset) | http://localhost:3000/login/sso/facebook |
| Production | https://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.

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.
FACEBOOK_CLIENT_ID=1234567890123456
FACEBOOK_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxRegister the adapter
Add FacebookSSOApiPlugin to authorization.ssoAdapters in your API config. It
is a plain factory function, not a class - there is no new.
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 devpnpm devnpm run devVerify 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.
| Setting | Value |
|---|---|
| Authorize URL | https://www.facebook.com/v22.0/dialog/oauth |
| Token URL | https://graph.facebook.com/v22.0/oauth/access_token |
| Profile URL | https://graph.facebook.com/v22.0/me?fields=id,name,email |
| Scopes | public_profile,email |
| Provider id | facebook |
| Fields read | id, 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.