Sigma Auth
Reference

Profile API

Sigma Auth provides profile management endpoints for creating and retrieving user profiles associated with Bitcoin identities.

Overview

User profiles in Sigma Auth are:

  • Stored in the auth server's KV storage (not on blockchain)
  • Associated with BAP (Bitcoin Attestation Protocol) identities
  • Included in JWT tokens and available via the /userinfo endpoint
  • Following Schema.org Person structure for future extensibility

Profile Structure

interface UserProfile {
  bapId: string;          // BAP identity key
  address: string;        // Bitcoin address derived from BAP
  identity: {             // Schema.org Person-compatible fields
    alternateName?: string;  // Username/handle
    image?: string;          // Avatar URL
    description?: string;    // Bio/description
  };
  isPublished: boolean;   // Whether published on-chain (future feature)
  createdAt: number;      // Unix timestamp
  updatedAt: number;      // Unix timestamp
  block?: number;         // Block height when published (future feature)
}

Endpoints

Get User Profile

GET /api/profile

Retrieves the profile for the authenticated user.

Headers:

Authorization: Bearer <access_token>

Response (200 OK):

{
  "success": true,
  "profile": {
    "bapId": "GpWrvdYWq3DUWMcw8vF8cFYoBK7",
    "address": "1ProfileAddressExample...",
    "identity": {
      "alternateName": "johndoe",
      "image": "https://example.com/avatar.jpg",
      "description": "Bitcoin developer and enthusiast"
    },
    "isPublished": false,
    "createdAt": 1705123456789,
    "updatedAt": 1705123456789
  }
}

Response (404 Not Found):

{
  "success": false,
  "error": "profile_not_found",
  "message": "No profile exists for this user"
}

Create User Profile

POST /api/profile/create

Creates a new BAP identity and profile for the authenticated user.

Headers:

Authorization: Bearer <access_token>
Content-Type: application/json

Request Body:

{
  "alternateName": "johndoe",        // Optional: username/handle
  "image": "https://example.com/avatar.jpg",  // Optional: avatar URL
  "description": "Bitcoin developer",         // Optional: bio
  "password": "backup-password"      // REQUIRED: Password to decrypt user's backup
}

Important: The password is used ONLY to decrypt the user's encrypted backup containing their BAP keys. It is never stored.

Response (200 OK):

{
  "success": true,
  "profile": {
    "idKey": "GpWrvdYWq3DUWMcw8vF8cFYoBK7",
    "address": "1ProfileAddressExample...",
    "profileData": {
      "alternateName": "johndoe",
      "image": "https://example.com/avatar.jpg",
      "description": "Bitcoin developer"
    }
  },
  "message": "Profile created successfully. Please re-authenticate to get updated token."
}

Response (400 Bad Request):

{
  "error": "no_backup_found",
  "message": "Please create a backup first using /backup endpoint"
}

Response (401 Unauthorized):

{
  "error": "invalid_password"
}

Response (409 Conflict):

{
  "error": "profile_already_exists"
}

Update User Profile

PUT /api/profile

Updates the authenticated user's profile data.

Headers:

Authorization: Bearer <access_token>
Content-Type: application/json

Request Body:

{
  "alternateName": "newusername",    // Optional
  "image": "https://example.com/new-avatar.jpg",  // Optional
  "description": "Updated bio"       // Optional
}

Response (200 OK):

{
  "success": true,
  "profile": {
    "bapId": "GpWrvdYWq3DUWMcw8vF8cFYoBK7",
    "address": "1ProfileAddressExample...",
    "identity": {
      "alternateName": "newusername",
      "image": "https://example.com/new-avatar.jpg",
      "description": "Updated bio"
    },
    "isPublished": false,
    "createdAt": 1705123456789,
    "updatedAt": 1705127856789
  }
}

Profile in UserInfo Endpoint

Once a profile is created, it's automatically included in the /userinfo endpoint response:

GET /userinfo

Response with profile:

{
  "pubkey": "02a94e09bcd6085e580f9214d2814e985f348b1b24121b2aff70a169f971ff5699",
  "bapIdKey": "GpWrvdYWq3DUWMcw8vF8cFYoBK7",
  "profile": {
    "bapId": "GpWrvdYWq3DUWMcw8vF8cFYoBK7",
    "address": "1ProfileAddressExample...",
    "identity": {
      "alternateName": "johndoe",
      "image": "https://example.com/avatar.jpg",
      "description": "Bitcoin developer and enthusiast"
    },
    "isPublished": false,
    "createdAt": 1705123456789,
    "updatedAt": 1705123456789
  }
}

Response without profile:

{
  "pubkey": "02a94e09bcd6085e580f9214d2814e985f348b1b24121b2aff70a169f971ff5699"
}

Integration Guide

1. Check if User Has Profile

const response = await fetch('https://auth.sigmaidentity.com/userinfo', {
  headers: {
    'Authorization': `Bearer ${accessToken}`
  }
});

const userInfo = await response.json();

if (userInfo.profile) {
  // User has a profile
  console.log('Username:', userInfo.profile.identity.alternateName);
} else {
  // User needs to create a profile
  showCreateProfileUI();
}

2. Create Profile

// Note: User must have a backup first
const createProfile = async (profileData: {
  alternateName?: string;
  image?: string;
  description?: string;
  password: string;  // Required to decrypt backup
}) => {
  const response = await fetch('https://auth.sigmaidentity.com/api/profile/create', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(profileData)
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.message || error.error);
  }

  const result = await response.json();
  
  // Important: User needs to re-authenticate to get updated JWT with profile
  window.location.href = '/api/auth/signin';
};

3. Update Profile

const updateProfile = async (updates: {
  alternateName?: string;
  image?: string;
  description?: string;
}) => {
  const response = await fetch('https://auth.sigmaidentity.com/api/profile', {
    method: 'PUT',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(updates)
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.message || error.error);
  }

  return await response.json();
};

Important Notes

Profile Creation Requirements

  1. Backup Required: User must have created an encrypted backup first
  2. Password Required: The backup password is needed to decrypt BAP keys
  3. One Profile Per User: Each Bitcoin identity can have one profile
  4. Re-authentication: After creating a profile, users should re-authenticate to get an updated JWT token containing their profile

Profile Data Storage

  • Profiles are stored in Cloudflare KV storage, not on the blockchain
  • The isPublished field is for future blockchain publishing functionality
  • Profile data is included directly in JWT tokens for performance

Schema.org Compatibility

The identity object follows Schema.org Person structure, allowing future expansion with fields like:

  • name (full name)
  • email
  • url (website)
  • location
  • jobTitle
  • And other Schema.org Person properties

Error Handling

Always handle profile endpoint errors gracefully:

async function getUserProfile(accessToken: string) {
  try {
    const response = await fetch('https://auth.sigmaidentity.com/userinfo', {
      headers: { 'Authorization': `Bearer ${accessToken}` }
    });
    
    const userInfo = await response.json();
    
    // Profile may not exist - handle gracefully
    return userInfo.profile || null;
    
  } catch (error) {
    console.error('Failed to fetch profile:', error);
    return null;
  }
}

Next Steps