install

Add Sign in with dots to any app.

dots.id is an OAuth identity provider for the dots ecosystem. Use it as your only sign-in method, or add it beside an existing auth stack as a connected identity. Supabase backs the OAuth server, while Privy, Vercel, and Better Auth-compatible email flows provide the proofs that make a dots.id portable.

quick start

  1. 1. Create an OAuth app in the dots dashboard.
  2. 2. Add your redirect URI and allowed scopes.
  3. 3. Wire authorize, callback, token exchange, and userinfo.
  4. 4. Store the returned dots subject as the linked dots.id.

model

How dots auth is layered

Supabase is the main dots auth server and system of record. The other providers are upstream proofs or compatibility layers that feed the dots profile before OAuth shares it with ecosystem apps.

Supabase

The dots auth server database. It owns profiles, OAuth clients, consent, issued tokens, username state, and synced identity claims.

Privy

The primary user session and wallet/social login layer used by dots.id before a profile can authorize ecosystem apps.

Vercel

A developer/platform proof. Users connect Vercel during dots.id creation so apps can trust developer identity context.

Better Auth + Resend

The email compatibility and notification layer. dots uses verified backup/email channels for app notifications, recovery, and syncing email identity into apps that use Better Auth.

Issuance flow

A user connects an e-wallet, signs in with Vercel, verifies an email through the Resend-backed flow, and subscribes to vibe through Stripe. Supabase stores the completed dots profile and the OAuth server uses that profile to issue scoped tokens to apps.

verification

What a dots.id verifies

1

e-wallet

Privy sign-in plus an embedded wallet or connected wallet.

Vercel sign-in

A verified Vercel account as the developer/platform proof.

@

email

A verified contact email for account info, notifications, and recovery.

#

Subscribe to vibe

A Stripe subscription that includes #color and unlocks pute.me.

vibe

What the subscription unlocks

The fourth verification step is a vibe subscription. Once active, dots can hand the user into pute.me, where the dots OAuth identity is linked to a Universal Compute Wallet. That wallet connection is what apps use for compute balance, spend caps, and wallet-backed access.

provider

Provider settings

issuer

https://dots.id

response type

code + PKCE

/api/oauth/authorize

Start the OAuth code flow and ask for user consent.

/api/oauth/token

Exchange a code or refresh token for dots tokens.

/api/oauth/userinfo

Read the scoped dots.id profile for the signed-in user.

/api/oauth/revoke

Revoke an app's token when access should end.

/api/oauth/introspect

Validate token state from trusted server code.

env

Add project variables

Register your app at the developer dashboard, then add the generated values to your project. Public or native clients should use PKCE and avoid a client secret.

DOTS_CLIENT_ID="dots_client_..."
DOTS_CLIENT_SECRET="dots_secret_..."
DOTS_REDIRECT_URI="https://yourapp.com/api/auth/dots/callback"
DOTS_ISSUER="https://dots.id"

flow

Wire the OAuth flow

1. Start sign-in

const params = new URLSearchParams({
  client_id: process.env.DOTS_CLIENT_ID!,
  redirect_uri: process.env.DOTS_REDIRECT_URI!,
  response_type: "code",
  scope: "openid profile email wallet",
  state,
  code_challenge: challenge,
  code_challenge_method: "S256",
});

redirect(`${process.env.DOTS_ISSUER}/api/oauth/authorize?${params}`);

2. Exchange the callback code

const tokenRes = await fetch(`${process.env.DOTS_ISSUER}/api/oauth/token`, {
  method: "POST",
  headers: {
    "content-type": "application/x-www-form-urlencoded",
    authorization: `Basic ${Buffer.from(
      `${process.env.DOTS_CLIENT_ID}:${process.env.DOTS_CLIENT_SECRET}`
    ).toString("base64")}`,
  },
  body: new URLSearchParams({
    grant_type: "authorization_code",
    code,
    redirect_uri: process.env.DOTS_REDIRECT_URI!,
    code_verifier: verifier,
  }),
});

3. Read scoped dots.id claims

const profileRes = await fetch(`${process.env.DOTS_ISSUER}/api/oauth/userinfo`, {
  headers: {
    authorization: `Bearer ${accessToken}`,
  },
});

const dotsProfile = await profileRes.json();

modes

Use dots as primary or connected auth

Only provider

Use the dots subject as your app user ID. Every account signs in through dots.id and inherits the same identity, wallet, email, and ecosystem permissions.

Optional provider

Keep your existing auth provider and attach dots.id as a linked account. Use this when dots powers sync, reputation, groups, or ecosystem features without replacing your login.

better auth

Stay compatible with Better Auth

dots should be installable anywhere Better Auth can accept a custom OAuth provider. In that setup, Better Auth owns the local app session while dots.id remains the external identity, sync, notification, and ecosystem authorization layer.

What syncs

The app stores the dots subject, username, verified email claims, wallet, and any requested social claims. Resend-powered email is used for dots notifications, recovery, and syncing email state into apps that already model users through Better Auth.

// Better Auth custom OAuth provider shape
{
  id: "dots",
  name: "dots.id",
  issuer: "https://dots.id",
  authorizationEndpoint: "https://dots.id/api/oauth/authorize",
  tokenEndpoint: "https://dots.id/api/oauth/token",
  userInfoEndpoint: "https://dots.id/api/oauth/userinfo",
  scopes: ["openid", "profile", "email", "wallet"],
}

scopes

Choose what your app can sync

openid

Required OIDC identity scope.

profile

Username and basic dots.id profile information.

email

Verified email claims.

wallet

Connected wallet address.

social:twitter

Connected Twitter/X username.

social:instagram

Connected Instagram username.

social:google

Connected Google account info.

data:read

Read app data from the user's dots data layer.

data:write

Write app data into the user's dots data layer.

offline_access

Issue refresh tokens for long-lived sessions.

sdk

Self-building SDK shape

The install flow can become a generated SDK: choose framework, auth mode, callback path, and scopes, then emit the exact files and environment variables for that project.

{
  "provider": "dots",
  "appUrl": "https://yourapp.com",
  "redirectPath": "/api/auth/dots/callback",
  "scopes": ["openid", "profile", "email", "wallet"],
  "mode": "better-auth-compatible"
}
Future command: npx dots install --framework next --mode optional