New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

webba-id-sdk

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

webba-id-sdk

Official SDK for OAuth 2.0 + PKCE authentication with Webba ID — frontend, backend, and bots.

latest
Source
npmnpm
Version
2.5.0
Version published
Maintainers
1
Created
Source

webba-id-sdk

Official SDK for OAuth 2.0 + PKCE authentication with Webba ID.

npm install webba-id-sdk

If you're using Claude Code, also run:

npx webba-id-sdk init

It installs the /webba-id skill at .claude/skills/webba-id/ (so the AI agents in your project know how the SDK actually works) and generates AuthContext.tsx + AuthCallback.tsx templates.

TL;DR — front-end SPA

// src/main.tsx
import { webba } from 'webba-id-sdk'

webba.configure({
  appKey: import.meta.env.VITE_WEBBA_APP_KEY,        // wba_xxx
  appSecret: import.meta.env.VITE_WEBBA_APP_SECRET,  // wbs_xxx — see security note below
  redirectUri: window.location.origin + '/auth/callback',
  storagePrefix: 'myapp',                             // localStorage keys: myapp_tokens / myapp_user
})
// Login button
import { webba } from 'webba-id-sdk'

<button onClick={() => webba.login()}>Sign in with Webba ID</button>
// /auth/callback page
import { useEffect, useRef } from 'react'
import { webba } from 'webba-id-sdk'

export function AuthCallback() {
  const ran = useRef(false)
  useEffect(() => {
    if (ran.current) return
    ran.current = true
    webba.handleCallback().then(async (tokens) => {
      const user = await webba.getUser(tokens.accessToken)
      webba.saveTokens(tokens)
      webba.saveUser(user)
      location.href = '/'
    })
  }, [])
  return <p>Connecting...</p>
}

That's a full working integration. For a production-quality AuthContext (token refresh, cross-tab sync, focus listeners), copy the template the init CLI installs at src/contexts/AuthContext.tsx.

Security — appSecret and VITE_*

The Webba ID /auth-token endpoint requires the app secret to exchange a code for tokens. There is no PKCE-only flow yet on the server — meaning a true public SPA cannot exchange codes without revealing the secret somewhere.

You have two choices:

Keep your secret server-side. The frontend handles login/state/PKCE and ships the code to your backend; your backend calls Webba ID with the secret.

// Frontend (no secret)
webba.configure({
  appKey: import.meta.env.VITE_WEBBA_APP_KEY,
  redirectUri: location.origin + '/auth/callback',
})

// Backend (Express, Edge Function, ...)
import { createWebbaID } from 'webba-id-sdk'
const server = createWebbaID({
  appKey: process.env.WEBBA_APP_KEY!,
  appSecret: process.env.WEBBA_APP_SECRET!, // server-only env, NO `VITE_` prefix
  redirectUri: 'https://example.com/auth/callback',
})
const tokens = await server.exchangeCode(code, codeVerifier)

Pragmatic — accept the secret in the bundle

If your app's threat model is fine with shipping the app secret to clients (low-stakes apps, internal tools), put it in VITE_WEBBA_APP_SECRET and call webba.handleCallback() directly.

⚠ NEVER use VITE_* for secrets you actually want kept private. Vite inlines VITE_* env vars into the production bundle — anyone hitting the page sees the value in DevTools.

Configuration

interface WebbaIDConfig {
  appKey:        string  // wba_xxx — REQUIRED
  appSecret?:    string  // wbs_xxx — required for token exchange and reverse lookup
  redirectUri?:  string  // defaults to `${origin}/auth/callback`
  apiKey?:       string  // Webba ID Supabase anon key — defaults to production
  baseUrl?:      string  // defaults to https://account.webba-creative.com
  apiUrl?:       string  // defaults to the production Edge Functions URL
  storagePrefix?: string // localStorage prefix — defaults to 'webba'
}

Auto-configure from env

import { webba } from 'webba-id-sdk'

// Reads VITE_WEBBA_APP_KEY, VITE_WEBBA_APP_SECRET, VITE_WEBBA_REDIRECT_URI,
// VITE_WEBBA_API_KEY (and the non-prefixed names on Node).
const ok = webba.configureFromEnv()
if (!ok) {
  // VITE_WEBBA_APP_KEY was missing
}

API reference

Login flow

MethodDescription
login()Generate PKCE + state, redirect to Webba ID's /oauth/authorize
isCallback()True when the URL has ?code= or ?error=
handleCallback()All-in-one: parse URL, validate state, exchange code → AuthTokens. Idempotent under React StrictMode
exchangeCode(code, codeVerifier?)Lower-level: just exchange the code (server-side or advanced)

Tokens

MethodDescription
refreshTokens(refreshToken)Returns new AuthTokens
revokeToken(token)Revoke an access or refresh token
verifyToken(accessToken){ valid: boolean, user_id?, email? } (never throws)
logout(accessToken?, refreshToken?)Revoke + clear local state

User info

MethodDescription
getUser(accessToken)Convenience WebbaUser shape (id, email, name, avatar)
getUserInfo(accessToken, includeProfile?)Raw WebbaIDUser (with subscriptions when includeProfile=true)
getProfile(accessToken)Full profile + subscriptions + roles
updateProfile(accessToken, partial)PUT changes to the profile

Storage helpers (browser)

MethodDescription
saveTokens(tokens) / getStoredTokens() / clearTokens()localStorage at ${storagePrefix}_tokens
saveUser(user) / getStoredUser() / clearUser()localStorage at ${storagePrefix}_user
isTokenExpired(tokens)Date.now() >= tokens.expiresAt

Linked identities (client-side)

MethodDescription
getLinkedIdentities(accessToken)All linked OAuth providers
hasProviderLinked(accessToken, provider)Boolean shortcut
getProviderIdentity(accessToken, provider)Single identity or null
getProviderId(accessToken, provider)Just the provider id, or null
hasCfxreLinked(accessToken) / getCfxreIdentity / getCfxreIdCfx.re shortcuts

Reverse lookup (server-side; requires appSecret)

MethodDescription
lookupUserByProvider(provider, providerUserId)Find a Webba user from a provider id
lookupUsersByProvider(provider, providerUserIds[])Batch (max 100)
getUserIdentitiesByUserId(userId)All providers linked to a Webba user
getProviderIdByUserId(userId, provider)Provider id for a user, or null
getCfxreIdByUserId(userId) / getDiscordIdByUserId(userId)Shortcuts

App access

MethodDescription
checkAppAccess(accessToken, orgId?){ has_access, reason, subscription, organizations }
hasAccess(accessToken, orgId?)Boolean shortcut
enableAppForOrg(accessToken, orgId) / disableAppForOrg(...)Toggle the app for an org

Two ways to use it

Singleton — most apps

import { webba } from 'webba-id-sdk'

webba.configure({ appKey: 'wba_xxx', appSecret: 'wbs_xxx', redirectUri: '...' })
await webba.login()

Factory — multi-tenant or server-side

import { createWebbaID } from 'webba-id-sdk'

const client = createWebbaID({
  appKey: process.env.WEBBA_APP_KEY!,
  appSecret: process.env.WEBBA_APP_SECRET!,
  redirectUri: 'https://example.com/callback',
})

// Discord bot example
const result = await client.lookupUserByProvider('discord', message.author.id)
if (result.user) {
  console.log('Webba user:', result.user.user_id)
}

Storage prefix

If two Webba apps share the same origin (e.g. an admin panel and a customer app on app.example.com), set distinct prefixes so their tokens don't collide:

webba.configure({ appKey, appSecret, redirectUri, storagePrefix: 'admin' })
// → admin_tokens / admin_user

Murphy's Workshop uses storagePrefix: 'murphy'. The default is 'webba'.

Production-grade AuthContext

The basic example above misses cross-tab sync, proactive refresh on focus, and lock coordination. The init CLI ships a battle-tested template (~230 lines) that handles all of it. Read .claude/skills/webba-id/auth-context.md after running npx webba-id-sdk init, or check the source on the Webba ID repo.

Server-side OAuth flow (Discord bot, Express, ...)

import { createWebbaID } from 'webba-id-sdk'

const webbaId = createWebbaID({
  appKey: process.env.WEBBA_APP_KEY!,
  appSecret: process.env.WEBBA_APP_SECRET!,
  redirectUri: 'https://example.com/auth/callback',
})

// Find a Webba user from a Discord user
const result = await webbaId.lookupUserByProvider('discord', '1234567890')
if (result.user) {
  // Their Cfx.re id?
  const cfxreId = await webbaId.getCfxreIdByUserId(result.user.user_id)
}

// Batch lookup
const batch = await webbaId.lookupUsersByProvider('discord', ['111', '222', '333'])
console.log(batch.mapping)
// { '111': 'webba-uuid-1', '222': 'webba-uuid-2' }   // 333 not linked

Supported providers: discord, google, github, azure, cfxre.

Troubleshooting

Every API call returns 401

You're likely using a self-hosted Webba ID and forgot to set apiKey. The SDK sends apikey + Authorization: Bearer <apiKey> headers on every request — if the value isn't a valid Supabase anon key for your Webba ID instance, Supabase's gateway rejects every call.

"Authorization code already processed"

You called handleCallback() twice with the same code (e.g. React StrictMode double-mount, or you remounted the callback component). The SDK has internal protection against this, but check that your callback page guards with a useRef flag too:

const ran = useRef(false)
useEffect(() => {
  if (ran.current) return
  ran.current = true
  webba.handleCallback().then(...)
}, [])

"Session expired or invalid"

PKCE data is gone. Common causes:

  • More than 10 minutes between login() and the callback.
  • Browser blocks third-party cookies and localStorage was cleared (e.g. cross-origin redirect through Cfx.re OAuth).
  • You used different origins (localhost vs 127.0.0.1).

"appSecret is required"

The exchange-code call hit the server without an app secret. Either:

  • Set appSecret in webba.configure(...) (frontend; ships secret in bundle), or
  • Move the exchange to your backend (recommended).

TypeScript: webba is unknown

Make sure you import from the package, not from a local file:

import { webba } from 'webba-id-sdk'    // ✓
import { webba } from './webba-id-sdk'   // ✗

OAuth flow diagram

1. User clicks "Sign in"
   ↓
2. webba.login()  →  generates PKCE pair + state
                  →  redirects to https://account.webba-creative.com/oauth/authorize
3. User authenticates on Webba ID
   ↓
4. Webba ID redirects to /auth/callback?code=...&state=...
   ↓
5. webba.handleCallback()
     ↓ verify state (CSRF)
     ↓ POST /auth-token { code, app_key, app_secret, redirect_uri, code_verifier }
     ↓ returns AuthTokens
6. webba.getUser(accessToken)
     ↓ GET /auth-userinfo?app_key=...&access_token=...&include_profile=true
     ↓ returns WebbaUser
7. webba.saveTokens(tokens) / webba.saveUser(user)
   ↓
8. Redirect to your app's home page

License

MIT — Webba Creative Technologies 2026

Keywords

webba

FAQs

Package last updated on 08 Jun 2026

Related posts