SSO
Twitter (X)
Add X (formerly Twitter) sign-in to VitNode with OAuth 2.0 and PKCE. Set up user authentication, request the confirmed email and wire up the adapter.
Add a Continue with X button using X's OAuth 2.0 authorization code flow with PKCE. The provider id stays twitter, so the callback is /login/sso/twitter.
Before you begin
- A running VitNode app. This page covers only the X parts; SSO setup explains the shared steps and where the API's
.envand config live. - An X developer account with a Project and App. Reading the signed-in account (
GET /2/users/me) is a billed API call on X's current plans, so check the access your account has.
Set up the app
Open user authentication settings
In the X developer portal, open your App inside a Project and press Set up (or Edit) under User authentication settings.
Choose a confidential web app
Set:
- App permissions: Read only. Turn on Request email from users to receive the confirmed email.
- Type of App: Web App, Automated App or Bot (a confidential client, because VitNode keeps the secret on the server).
- Website URL: your site.
Add the callback URI
Add each callback URI. X matches them exactly, including a trailing slash:
| Environment | Callback URI |
|---|---|
| Development | http://127.0.0.1:3000/login/sso/twitter |
| Production | https://your-domain.com/login/sso/twitter |
X documents 127.0.0.1 rather than localhost for local work. Set VITNODE_WEB_URL=http://127.0.0.1:3000 and open the site at that address, because VitNode builds the URI as {VITNODE_WEB_URL}/login/sso/twitter and the sign-in cookie belongs to the host you started on.
Copy the OAuth 2.0 credentials
After saving, copy the OAuth 2.0 Client ID and Client Secret. The API key and secret belong to OAuth 1.0a and won't work here:
TWITTER_CLIENT_ID=AbCdEfGhIjKlMnOpQrStUvWxYz
TWITTER_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAdd the adapter
Add TwitterSSOApiPlugin to authorization.ssoAdapters in the API's vitnode.api.config.ts (where it lives), next to any adapters you already have. Keep the rest of the config as it is:
import { TwitterSSOApiPlugin } from '@vitnode/core/api/adapters/sso/twitter'
import { buildApiConfig } from '@vitnode/core/vitnode.config'
export const vitNodeApiConfig = buildApiConfig({
// ...
authorization: {
ssoAdapters: [
// ...
TwitterSSOApiPlugin({
clientId: process.env.TWITTER_CLIENT_ID,
clientSecret: process.env.TWITTER_CLIENT_SECRET,
}),
],
},
})No authorization block yet? Create one with just ssoAdapters, as in the SSO setup. Then restart the dev server, since .env and the API config are only read at start-up.
Check the result
Open /login, press the X button under Continue with and Authorize app. You return to /login/sso/twitter signed in, or are asked to confirm an email when X shares none.
Email access
With Request email from users on, the adapter requests the users.email scope and reads confirmed_email, which X only returns for a confirmed address. Some accounts (for example phone-only ones) have none.
If your app cannot request email, X rejects the users.email scope. Turn it off in the adapter, and new members confirm an address by email instead:
TwitterSSOApiPlugin({
clientId: process.env.TWITTER_CLIENT_ID,
clientSecret: process.env.TWITTER_CLIENT_SECRET,
requestEmail: false,
}),Options
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
clientId | string | undefined | Yes | - | OAuth 2.0 Client ID |
clientSecret | string | undefined | Yes | - | OAuth 2.0 Client Secret |
requestEmail | boolean | No | true | Request users.email and the confirmed email |
Scopes and profile fields
| Setting | Value |
|---|---|
| Scopes | tweet.read users.read users.email (users.email optional) |
| PKCE | S256 (X requires PKCE) |
| Token auth | HTTP Basic with the client ID and secret |
| Account ID | data.id |
| Email trusted | confirmed_email, when present |
| Profile fields | avatar |
tweet.read is required by X to read the signed-in account, even though VitNode reads no posts.
Troubleshooting
- "Something went wrong - You weren't able to give access to the App". The callback URI differs from
{VITNODE_WEB_URL}/login/sso/twitter, orusers.emailis requested while email access is off. - "unauthorized_client". The app type is a public client (Native App or Single page App), or you used the OAuth 1.0a keys.
- A provider error after authorizing.
GET /2/users/mewas refused; check your API access and plan in the developer portal.