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

SDK officiel pour intégrer l'authentification Webba ID dans vos applications

Source
npmnpm
Version
2.1.1
Version published
Weekly downloads
32
-30.43%
Maintainers
1
Weekly downloads
 
Created
Source

Webba ID SDK v2.1

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

Version TypeScript

Installation

Copy src/lib/webba-id.ts into your project, or install via npm:

npm install webba-id-sdk

Required Configuration

Get your credentials at https://account.webba-creative.com/platform/applications

# .env
VITE_WEBBA_API_KEY=your_webba_id_supabase_anon_key
VITE_WEBBA_CLIENT_ID=wba_xxx
VITE_WEBBA_CLIENT_SECRET=wbs_xxx
VITE_WEBBA_REDIRECT_URI=http://localhost:5173/auth/callback

Usage

1. Configuration (main.tsx)

import { WebbaID } from '@/lib/webba-id'

WebbaID.configure({
  apiKey: import.meta.env.VITE_WEBBA_API_KEY,
  clientId: import.meta.env.VITE_WEBBA_CLIENT_ID,
  clientSecret: import.meta.env.VITE_WEBBA_CLIENT_SECRET,
  redirectUri: import.meta.env.VITE_WEBBA_REDIRECT_URI
})

2. Login (redirects to Webba ID)

// In a login button
async function handleLogin() {
  await WebbaID.login()
}

3. Callback (/auth/callback)

import { useEffect, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { WebbaID } from '@/lib/webba-id'

export default function AuthCallback() {
  const navigate = useNavigate()
  const [error, setError] = useState<string | null>(null)
  const processingRef = useRef(false)

  useEffect(() => {
    const processCallback = async () => {
      // Prevent double execution (React StrictMode)
      if (processingRef.current) return
      processingRef.current = true

      if (!WebbaID.isCallback()) {
        setError('Invalid callback page')
        return
      }

      try {
        const tokens = await WebbaID.handleCallback()
        const user = await WebbaID.getUser(tokens.accessToken)

        // Save
        WebbaID.saveTokens(tokens)
        WebbaID.saveUser(user)

        navigate('/dashboard')
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Unknown error')
        processingRef.current = false
      }
    }

    processCallback()
  }, [navigate])

  if (error) return <div>Error: {error}</div>
  return <div>Connecting...</div>
}

4. Using Tokens

// Get stored user
const user = WebbaID.getStoredUser()
const tokens = WebbaID.getStoredTokens()

// Check if connected
if (user && tokens && !WebbaID.isTokenExpired(tokens)) {
  // User is connected
}

// Refresh expired tokens
if (tokens && WebbaID.isTokenExpired(tokens)) {
  try {
    const newTokens = await WebbaID.refreshTokens(tokens.refreshToken)
    WebbaID.saveTokens(newTokens)
  } catch {
    // Refresh failed, redirect to login
    WebbaID.clearTokens()
  }
}

5. Logout

async function handleLogout() {
  const tokens = WebbaID.getStoredTokens()
  await WebbaID.logout(tokens?.accessToken)
  window.location.href = '/'
}

API Reference

Configuration

interface WebbaIDConfig {
  apiKey: string      // Webba ID Supabase anon key (REQUIRED)
  clientId: string    // App key wba_xxx (REQUIRED)
  clientSecret?: string // App secret wbs_xxx (required for token exchange)
  redirectUri?: string  // Callback URL (default: origin + '/auth/callback')
  baseUrl?: string      // Webba ID URL (default: https://account.webba-creative.com)
  apiUrl?: string       // API URL (default: Supabase Edge Functions)
}

Methods

MethodDescription
configure(config)Configure the SDK (call once at startup)
isConfigured()Check if SDK is configured
login()Redirect to Webba ID for authentication
handleCallback()Handle OAuth callback and return tokens
getUser(accessToken)Get user info
refreshTokens(refreshToken)Refresh expired tokens
logout(accessToken?)Revoke token and clear storage
isCallback()Check if on callback page
saveTokens(tokens)Save tokens to localStorage
getStoredTokens()Get stored tokens
clearTokens()Remove stored tokens
isTokenExpired(tokens)Check if tokens are expired
saveUser(user)Save user to localStorage
getStoredUser()Get stored user

Organizations

MethodDescription
getOrganizations(accessToken)List user's organizations
getOrganization(accessToken, orgId)Get organization details
createOrganization(accessToken, data)Create organization (becomes owner)
updateOrganization(accessToken, orgId, data)Update organization
deleteOrganization(accessToken, orgId)Delete organization
getOrganizationMembers(accessToken, orgId)List members
inviteMember(accessToken, orgId, data)Invite member by email
removeMember(accessToken, orgId, memberId)Remove member
updateMemberRole(accessToken, orgId, memberId, role)Change member role

App Access

MethodDescription
checkAppAccess(accessToken, orgId?)Check if user has access (returns details)
hasAccess(accessToken, orgId?)Quick boolean check for access
enableAppForOrg(accessToken, orgId)Enable this app for an organization
disableAppForOrg(accessToken, orgId)Disable this app for an organization

Types

interface AuthTokens {
  accessToken: string
  refreshToken: string
  expiresAt: number
  userId: string
}

interface WebbaUser {
  id: string
  email: string
  name: string
  firstName?: string
  lastName?: string
  avatar?: string
}

type OrgRole = 'owner' | 'admin' | 'sso_admin' | 'viewer' | 'member'

interface Organization {
  id: string
  name: string
  slug: string
  logo_url: string | null
  is_active: boolean
  created_at: string
  role: OrgRole
  members_count?: number
  domains_count?: number
}

interface OrganizationMember {
  id: string
  organization_id: string
  user_id: string | null
  role: OrgRole
  email?: string
  profile?: {
    first_name?: string
    last_name?: string
    display_name?: string
    avatar_url?: string
  }
}

interface CreateOrganizationData {
  name: string
  slug?: string
  logo_url?: string
}

interface InviteMemberData {
  email: string
  role?: 'admin' | 'sso_admin' | 'viewer' | 'member'
}

interface AppAccessResult {
  has_access: boolean
  reason: 'subscription' | 'org_access' | 'subscription_and_org' | 'no_access'
  user_id: string
  app_id: string
  subscription: {
    id: string
    plan: string
    status: string
    features: Record<string, any> | null
    starts_at: string | null
    ends_at: string | null
  } | null
  organizations: Array<{
    id: string
    name: string
    slug: string
    role: string
  }>
}

OAuth Flow

1. User clicks "Login"
   |
2. WebbaID.login() → Redirect to account.webba-creative.com
   |
3. User authenticates on Webba ID
   |
4. Redirect back to /auth/callback?code=xxx&state=xxx
   |
5. WebbaID.handleCallback() → Exchange code for tokens
   |
6. WebbaID.getUser() → Get user info
   |
7. Save tokens & user → Redirect to dashboard

Security

PKCE (Proof Key for Code Exchange)

The SDK automatically uses PKCE to protect against interception attacks:

  • code_verifier randomly generated (32 bytes)
  • code_challenge = SHA-256(code_verifier) in base64url
  • Stored in cookie + localStorage (cross-origin safe)

State Parameter

The SDK generates a random state for each login and verifies it on callback (CSRF protection).

Token Storage

  • Tokens stored in localStorage (webba_tokens, webba_user)
  • PKCE data stored in cookie + localStorage with 10 minute expiration
  • Automatic cleanup of old PKCE sessions

Troubleshooting

"Session expired or invalid"

PKCE data has expired or is not accessible. Possible causes:

  • More than 10 minutes between login() and callback
  • Cookies blocked by browser
  • Different origin (e.g., 127.0.0.1 vs localhost)

Solution: Use the same origin (localhost) in development.

"Invalid or expired code"

The authorization code has already been used or expired.

Solution: Add useRef to prevent double execution in React StrictMode (see callback example above).

"clientSecret is required"

The clientSecret was not provided in configuration.

Solution: Add clientSecret: import.meta.env.VITE_WEBBA_CLIENT_SECRET to configure().

Organizations Example

import { WebbaID } from '@/lib/webba-id'

const tokens = WebbaID.getStoredTokens()

// List organizations
const orgs = await WebbaID.getOrganizations(tokens.accessToken)
console.log(orgs) // [{ id: '...', name: 'My Company', role: 'owner', ... }]

// Create organization
const newOrg = await WebbaID.createOrganization(tokens.accessToken, {
  name: 'My Company',
  slug: 'my-company' // optional
})

// Get organization details
const org = await WebbaID.getOrganization(tokens.accessToken, newOrg.id)
console.log(org.domains, org.members_count)

// Update organization (owner/admin)
await WebbaID.updateOrganization(tokens.accessToken, org.id, {
  name: 'New Name'
})

// List members
const members = await WebbaID.getOrganizationMembers(tokens.accessToken, org.id)

// Invite member (owner/admin)
await WebbaID.inviteMember(tokens.accessToken, org.id, {
  email: 'newuser@example.com',
  role: 'member'
})

// Change member role (owner only)
await WebbaID.updateMemberRole(tokens.accessToken, org.id, memberId, 'admin')

// Remove member (owner/admin)
await WebbaID.removeMember(tokens.accessToken, org.id, memberId)

// Delete organization (owner only)
await WebbaID.deleteOrganization(tokens.accessToken, org.id)

App Access Example

Use this to control access to your application based on subscriptions or organization membership.

import { WebbaID } from '@/lib/webba-id'

const tokens = WebbaID.getStoredTokens()

// Full access check with details
const access = await WebbaID.checkAppAccess(tokens.accessToken)

if (!access.has_access) {
  // User doesn't have access
  console.log('Access denied. Reason:', access.reason)
  // Redirect to upgrade page or show error
  window.location.href = '/upgrade'
}

// Access granted - check details
if (access.subscription) {
  console.log('User plan:', access.subscription.plan)
  // access.subscription.features contains plan features
}

if (access.organizations.length > 0) {
  console.log('Organizations with access:', access.organizations)
  // User has org-level access through these organizations
}

// Quick boolean check (simpler)
if (await WebbaID.hasAccess(tokens.accessToken)) {
  // User can use the app
} else {
  // No access
}

// Check access for a specific organization
const orgAccess = await WebbaID.checkAppAccess(tokens.accessToken, 'org-uuid')

Access is granted if:

  • User has an active subscription for this app, OR
  • User is member of an organization where the app is enabled

Auto-enable for Organization

Apps can enable themselves for an organization when a user logs in:

import { WebbaID } from '@/lib/webba-id'

const tokens = WebbaID.getStoredTokens()

// Get user's organizations
const orgs = await WebbaID.getOrganizations(tokens.accessToken)

// Enable this app for the first organization
if (orgs.length > 0) {
  await WebbaID.enableAppForOrg(tokens.accessToken, orgs[0].id)
}

// Now checkAppAccess will return has_access: true for this org
const access = await WebbaID.checkAppAccess(tokens.accessToken, orgs[0].id)
console.log(access.has_access) // true

AuthContext Example

import { createContext, useContext, useState, useEffect, ReactNode } from 'react'
import { WebbaID, AuthTokens, WebbaUser } from '@/lib/webba-id'

interface AuthContextType {
  user: WebbaUser | null
  tokens: AuthTokens | null
  isLoading: boolean
  login: () => Promise<void>
  logout: () => Promise<void>
  setAuth: (tokens: AuthTokens, user: WebbaUser) => void
}

const AuthContext = createContext<AuthContextType | null>(null)

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<WebbaUser | null>(null)
  const [tokens, setTokens] = useState<AuthTokens | null>(null)
  const [isLoading, setIsLoading] = useState(true)

  useEffect(() => {
    const storedTokens = WebbaID.getStoredTokens()
    const storedUser = WebbaID.getStoredUser()

    if (storedTokens && storedUser && !WebbaID.isTokenExpired(storedTokens)) {
      setTokens(storedTokens)
      setUser(storedUser)
    }
    setIsLoading(false)
  }, [])

  const login = async () => {
    await WebbaID.login()
  }

  const logout = async () => {
    await WebbaID.logout(tokens?.accessToken)
    setUser(null)
    setTokens(null)
  }

  const setAuth = (newTokens: AuthTokens, newUser: WebbaUser) => {
    WebbaID.saveTokens(newTokens)
    WebbaID.saveUser(newUser)
    setTokens(newTokens)
    setUser(newUser)
  }

  return (
    <AuthContext.Provider value={{ user, tokens, isLoading, login, logout, setAuth }}>
      {children}
    </AuthContext.Provider>
  )
}

export function useAuth() {
  const context = useContext(AuthContext)
  if (!context) throw new Error('useAuth must be used within AuthProvider')
  return context
}

License

MIT - Webba Creative Technologies 2026

Keywords

webba

FAQs

Package last updated on 02 Jan 2026

Related posts