facebook.twins.la
A digital twin of Facebook Login — OAuth 2.0 and the Graph API slice for signing users in.
What is this?
This is a high-fidelity digital twin of Facebook's Login surface:
the OAuth 2.0 authorization dialog, token exchange, the Graph API
/me user-profile endpoint, and /debug_token
for inspecting access tokens. Code you write against this twin
works against real Facebook with only hostname changes on the
dialog and Graph URLs. No Facebook App registration needed to
develop.
Supported scenarios
- OAuth 2.0 authorization —
response_type=code(web) andresponse_type=token(implicit) - Authorization code → access token exchange, with one-shot code consumption
- App access tokens via
grant_type=client_credentials - Graph API
/mewith field selection (id,name,email) /debug_tokenwith app-attribution to detect token substitution- Graph API versions
v19.0andv21.0served concurrently - Configurable test apps and users via the Twin Plane
- Failure simulation: invalid token, expired token, missing email grant, user denial
How to use it
Cloud: Point your app's Facebook SDK or HTTP
client at https://facebook.twins.la for both the
dialog URL (real: www.facebook.com) and the Graph
URL (real: graph.facebook.com). Ask the operator to
create a test app and user for you, then walk the OAuth code
flow.
Local: Install with pip install
twins-facebook twins-local and run a local instance on
any port. Same API, same behavior, your machine.
For agents
Copy this into your agent's system prompt, tool configuration,
or CLAUDE.md. Also available as plain text at
/_twin/agent-instructions.
# Facebook Login Twin — facebook.twins.la
A high-fidelity digital twin of Facebook Login: OAuth 2.0 authorization,
token exchange, Graph /me user profile, and /debug_token inspection.
Code written against this twin works against real Facebook with only
hostname changes on the dialog and Graph URLs.
Supported Graph API versions: v19.0, v21.0 (served concurrently under
the same host).
## Authentication
OAuth endpoints (no auth, driven by app_id + app_secret in the flow):
/dialog/oauth, /v*/oauth/access_token, /v*/me, /v*/debug_token
Twin Plane tenant ops — HTTP Basic Auth
Username: app_id
Password: app_secret
(POST /_twin/tokens for direct token minting; GET /_twin/logs scoped
to your app.)
Twin Plane admin ops — Bearer token
Header: X-Twin-Admin-Token: <token> (or Authorization: Bearer <token>)
Service-wide operations (create/delete apps, create/update/delete
users, read any logs, update twin settings) require the admin token
set by the deployment owner.
Apps are admin-provisioned only — there is no self-bootstrap endpoint.
Ask the operator to create an app for you and share the app_id and
app_secret back.
## Key Endpoints
Twin Plane (no auth):
GET /_twin/health — status check
GET /_twin/scenarios — supported scenarios + API versions
GET /_twin/settings — twin settings (interactive_dialog)
GET /_twin/references — authoritative sources used to build this twin
Twin Plane (Basic Auth with app_id:app_secret):
POST /_twin/tokens — mint a user access token for one of
your app's users (body: {"fb_id", "scopes"})
GET /_twin/logs — your app's operation logs
Twin Plane (Admin Bearer):
POST /_twin/apps — create an app
GET /_twin/apps — list apps
DELETE /_twin/apps/<app_id> — delete an app (cascades)
POST /_twin/users — create a test user (body: {"app_id", "fb_id", "name", "email", "granted_scopes"})
GET /_twin/users[?app_id=<id>] — list users, optionally scoped
PATCH /_twin/apps/<app_id>/users/<fb_id> — update user
DELETE /_twin/apps/<app_id>/users/<fb_id> — delete user
PUT /_twin/settings — update twin settings
GET /_twin/logs — all operation logs
OAuth & Graph API (for end users of your app):
GET /dialog/oauth — authorization dialog (code + implicit)
GET /v19.0/dialog/oauth — versioned dialog (same semantics)
GET /v21.0/dialog/oauth — versioned dialog (same semantics)
GET /v{19,21}/oauth/access_token — exchange code for token
(also grant_type=client_credentials
for an app access token)
GET /v{19,21}/me — fetch the authenticated user's profile
(fields query param: id,name,email)
GET /v{19,21}/debug_token — inspect an input_token
## Quick Start (OAuth code flow)
1. Operator creates a test app and test user for you via Twin Plane:
curl -X POST https://facebook.twins.la/_twin/apps \
-H "X-Twin-Admin-Token: $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "My App", "redirect_uris": ["https://my-app.test/cb"]}'
curl -X POST https://facebook.twins.la/_twin/users \
-H "X-Twin-Admin-Token: $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"app_id": "<app_id>", "name": "Alice", "email": "alice@test",
"granted_scopes": ["email", "public_profile"]}'
2. Walk the dialog (this is what a browser would do):
curl -I "https://facebook.twins.la/v19.0/dialog/oauth?\
client_id=<app_id>&redirect_uri=https://my-app.test/cb&\
response_type=code&state=xyz&scope=email,public_profile"
# 302 Location: https://my-app.test/cb?code=<code>&state=xyz
3. Exchange the code for an access token:
curl "https://facebook.twins.la/v19.0/oauth/access_token?\
client_id=<app_id>&client_secret=<app_secret>&\
redirect_uri=https://my-app.test/cb&code=<code>"
# {"access_token":"EAA...","token_type":"bearer","expires_in":...}
4. Fetch the user's identity:
curl "https://facebook.twins.la/v19.0/me?fields=id,name,email" \
-H "Authorization: Bearer <access_token>"
# {"id":"...","name":"Alice","email":"alice@test"}
5. Verify app attribution (detect token substitution):
curl "https://facebook.twins.la/v19.0/debug_token?\
input_token=<access_token>&access_token=<app_id>|<app_secret>"
# {"data": {"app_id":"<app_id>","user_id":"...","is_valid":true,...}}
## Local Usage
pip install twins-facebook twins-local
python -c "
from twins_local.storage_sqlite_facebook import SQLiteFacebookStorage
from twins_facebook.app import create_app
storage = SQLiteFacebookStorage('facebook.db')
app = create_app(storage=storage, config={'admin_token': 'dev'})
app.run(port=8081)
"
Then use http://localhost:8081 instead of https://facebook.twins.la.
## Fidelity Notes
- Access tokens are opaque twin values — do NOT hard-code real Facebook
token shapes or length expectations. The EAA prefix is a visual
courtesy, not a fidelity contract.
- Error envelope matches Facebook: {"error": {"message","type","code",
"fbtrace_id"}}. Well-known codes: 100 (parameter), 101 (app
credentials), 190 (access token), 191 (redirect_uri), 803 (unknown
path), 2500 (unknown API version).
- /dialog/oauth is served at the same host as /v*/... — real Facebook
splits them across www.facebook.com and graph.facebook.com; twins.la
collapses both to one host. Configure dialog URL and Graph URL
independently in your client if the SDK expects different hostnames.
- Users are per-app on this twin — the same fb_id under different
apps are different users. Tenants cannot cross-access each other's
users.
## Reference
Detailed docs: https://github.com/twins-la/facebook
Project overview: https://twins.la
All twins: https://github.com/twins-la/twins-la