BAP Profiles
Bitcoin Attestation Protocol (BAP) provides decentralized identity management on the Bitcoin blockchain. Sigma Auth automatically fetches and includes BAP profile data when available.
What is BAP?
BAP (Bitcoin Attestation Protocol) is a decentralized identity system that stores profile information on the Bitcoin blockchain. It allows users to create verified, immutable profiles linked to their Bitcoin identity.
BAP profiles follow an extended version of schema.org standards:
- Person - For individual user profiles
- Organization - For company/organization profiles
While maintaining schema.org compatibility, BAP extends these schemas with Bitcoin-specific fields:
bitcoinAddress
- Primary Bitcoin (BSV) addresspaymail
- Human-readable payment addressidentityKey
- BAP identity public key- Additional blockchain-related attestations
This hybrid approach ensures BAP profiles are interoperable with standard web tools while supporting Bitcoin's unique requirements.
BAP Structure
interface BAPProfile {
identityKey: string; // BAP identity key (different from Bitcoin address)
// Schema.org Person fields
name?: string; // Display name (Person.name)
description?: string; // Profile bio (Person.description)
image?: string; // Profile image URL (Person.image)
email?: string; // Email address (Person.email)
url?: string; // Website URL (Person.url)
// Additional common fields
paymail?: string; // Paymail address
jobTitle?: string; // Job title (Person.jobTitle)
worksFor?: string; // Organization (Person.worksFor)
location?: string; // Location (Person.address)
// Extended attestations following schema.org
[key: string]: any; // Additional schema.org compatible fields
}
Getting BAP Profiles
Example: Demo User Lookup
Here's how Sigma Auth fetches BAP profiles internally. Using the demo identity Go8vCHAa4S6AhXKTABGpANiz35J
:
# Direct API call to fetch BAP identity
curl -X POST https://api.sigmaidentity.com/api/v1/identity/get \
-H "Content-Type: application/json" \
-d '{"idKey": "Go8vCHAa4S6AhXKTABGpANiz35J"}'
Response:
{
"status": "OK",
"result": {
"idKey": "Go8vCHAa4S6AhXKTABGpANiz35J",
"currentAddress": "1Demo...",
"rootAddress": "1Root...",
"addresses": ["1Demo...", "1Other..."],
"identity": {
"name": "Demo User",
"alternateName": "Demo",
"description": "Example BAP profile"
},
"profile": {
"@type": "Person",
"email": "demo@example.com",
"url": "https://demo.example.com",
"image": "https://example.com/demo-avatar.jpg",
"jobTitle": "Demo Account",
"bitcoinAddress": "1Demo...",
"paymail": "demo@example.com"
}
}
}
Sigma Auth merges all these fields into the profile returned via /userinfo
.
From UserInfo Endpoint
BAP profiles are automatically included in the /userinfo
endpoint response when available:
const response = await fetch('https://auth.sigmaidentity.com/userinfo', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
const userInfo = await response.json();
console.log(userInfo);
Response with BAP Profile
When a BAP profile exists:
{
"sub": "did:bitcoin:id:03a1b2c3d4e5f6...",
"public_key": "03a1b2c3d4e5f6...",
"profile": {
"identityKey": "03a1b2c3d4e5f6...",
"name": "John Doe",
"description": "Bitcoin developer",
"image": "https://example.com/avatar.jpg",
"paymail": "john@example.com",
"website": "https://johndoe.com",
"twitter": "@johndoe"
}
}
Response without BAP Profile
When no BAP profile exists:
{
"sub": "did:bitcoin:id:03a1b2c3d4e5f6...",
"public_key": "03a1b2c3d4e5f6...",
"profile": {
"identityKey": "03a1b2c3d4e5f6..."
}
}
Profile Availability
When BAP Profiles Are Available
- ✅ User has created a BAP profile on the blockchain
- ✅ Profile is properly formatted and parseable
- ✅ Profile attestations are valid
- ✅ Sigma API can successfully fetch the profile
When BAP Profiles Are Not Available
- ❌ User has never created a BAP profile
- ❌ Profile data is corrupted or unparseable
- ❌ Network issues fetching from Sigma API
- ❌ Profile attestations are invalid
Handling Missing Profiles
Frontend Implementation
Always check for profile data availability:
function UserProfile({ userInfo }) {
const hasFullProfile = userInfo.profile?.name;
if (hasFullProfile) {
return (
<div className="profile">
<img src={userInfo.profile.image} alt="Profile" />
<h2>{userInfo.profile.name}</h2>
<p>{userInfo.profile.description}</p>
{userInfo.profile.paymail && (
<p>Paymail: {userInfo.profile.paymail}</p>
)}
</div>
);
}
// Fallback for users without BAP profiles
return (
<div className="profile">
<div className="avatar-placeholder">
{userInfo.public_key.slice(0, 2)}
</div>
<h2>Bitcoin User</h2>
<p>Public Key: {userInfo.public_key}</p>
<button onClick={() => window.open('https://social.sigmaidentity.com/profile')}>
Create BAP Profile
</button>
</div>
);
}
Backend Validation
Always validate profile data server-side:
function validateBAPProfile(profile: any): boolean {
// At minimum, identityKey should always be present
if (!profile?.identityKey) {
return false;
}
// Additional validation for optional fields
if (profile.name && typeof profile.name !== 'string') {
return false;
}
if (profile.image && !isValidURL(profile.image)) {
return false;
}
return true;
}
function processUserInfo(userInfo: any) {
if (!validateBAPProfile(userInfo.profile)) {
// Handle invalid or missing profile
return {
...userInfo,
profile: {
identityKey: userInfo.public_key,
// Provide sensible defaults
name: `Bitcoin User`,
description: 'Bitcoin identity without BAP profile'
}
};
}
return userInfo;
}
Profile Creation Flow
Directing Users to Create Profiles
When users don't have BAP profiles, you can direct them to create one:
function CreateProfilePrompt({ publicKey }) {
const createProfileURL = `https://social.sigmaidentity.com/profile/create?key=${publicKey}`;
return (
<div className="profile-prompt">
<h3>Complete Your Bitcoin Identity</h3>
<p>Create a BAP profile to add your name, image, and other details to your Bitcoin identity.</p>
<button onClick={() => window.open(createProfileURL)}>
Create BAP Profile
</button>
</div>
);
}
Profile Update Detection
Monitor for profile updates in your application:
async function refreshUserProfile(accessToken: string) {
try {
const response = await fetch('/userinfo', {
headers: { 'Authorization': `Bearer ${accessToken}` }
});
const updatedUserInfo = await response.json();
// Check if profile has been updated
if (updatedUserInfo.profile?.name && !currentUser.profile?.name) {
// User has created a new BAP profile
showNotification('Profile updated successfully!');
updateUserState(updatedUserInfo);
}
} catch (error) {
console.error('Failed to refresh profile:', error);
}
}
Common Profile Fields
Standard BAP Fields (schema.org Person)
Field | Type | Schema.org Property | Description |
---|---|---|---|
identityKey | string | - | BAP identity key (always present) |
name | string | Person.name | Display name |
description | string | Person.description | Profile bio/description |
image | string | Person.image | Profile image URL |
email | string | Person.email | Email address |
url | string | Person.url | Website URL |
paymail | string | - | Paymail address (Bitcoin-specific) |
bitcoinAddress | string | - | Primary Bitcoin (BSV) address |
Extended Fields
BAP profiles can include additional schema.org compatible attestations:
{
"profile": {
"@type": "Person",
"identityKey": "03a1b2c3d4e5f6...",
"name": "John Doe",
"url": "https://johndoe.com",
"jobTitle": "Senior Developer",
"worksFor": {
"@type": "Organization",
"name": "Bitcoin Corp",
"url": "https://bitcoincorp.com"
},
"address": {
"@type": "PostalAddress",
"addressLocality": "San Francisco",
"addressRegion": "CA",
"addressCountry": "US"
},
"sameAs": [
"https://twitter.com/johndoe",
"https://github.com/johndoe",
"https://linkedin.com/in/johndoe"
],
"knowsAbout": ["Bitcoin", "JavaScript", "Go"],
"pgpPublicKey": "-----BEGIN PGP PUBLIC KEY BLOCK-----..."
}
}
Common Extended Fields
Field | Schema.org Property | Description |
---|---|---|
jobTitle | Person.jobTitle | Current job title |
worksFor | Person.worksFor | Organization/employer |
address | Person.address | Location/address |
sameAs | Person.sameAs | Links to social profiles |
knowsAbout | Person.knowsAbout | Skills/expertise |
alumniOf | Person.alumniOf | Educational institutions |
memberOf | Person.memberOf | Organizations/groups |
Organization Profiles
BAP also supports Organization profiles following schema.org/Organization:
{
"profile": {
"@type": "Organization",
"identityKey": "03a1b2c3d4e5f6...",
"name": "Bitcoin Corp",
"legalName": "Bitcoin Corporation Inc.",
"url": "https://bitcoincorp.com",
"logo": "https://bitcoincorp.com/logo.png",
"description": "Leading Bitcoin infrastructure provider",
"address": {
"@type": "PostalAddress",
"streetAddress": "123 Bitcoin St",
"addressLocality": "San Francisco",
"addressRegion": "CA",
"postalCode": "94105",
"addressCountry": "US"
},
"sameAs": [
"https://twitter.com/bitcoincorp",
"https://linkedin.com/company/bitcoincorp"
],
"foundingDate": "2019-01-01",
"founders": [{
"@type": "Person",
"name": "Satoshi Nakamoto"
}]
}
}
Schema.org Benefits
Using schema.org standards for BAP profiles provides:
- Interoperability - Works with existing tools and services
- SEO Benefits - Search engines understand the data structure
- Rich Snippets - Enhanced display in search results
- Standardization - Consistent data format across applications
- Extensibility - Add any schema.org property as needed
Error Handling
Profile Fetch Errors
Handle cases where BAP profile fetching fails:
async function getUserInfo(accessToken: string) {
try {
const response = await fetch('/userinfo', {
headers: { 'Authorization': `Bearer ${accessToken}` }
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const userInfo = await response.json();
// Validate profile data
if (!userInfo.profile?.identityKey) {
console.warn('Invalid or missing BAP profile data');
// Provide fallback profile
userInfo.profile = {
identityKey: userInfo.public_key,
name: 'Bitcoin User'
};
}
return userInfo;
} catch (error) {
console.error('Failed to fetch user info:', error);
throw error;
}
}
Network Resilience
Implement retry logic for profile fetching:
async function fetchProfileWithRetry(accessToken: string, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await getUserInfo(accessToken);
} catch (error) {
if (attempt === maxRetries) {
// Final attempt failed, return minimal profile
return {
sub: 'did:bitcoin:id:unknown',
public_key: 'unknown',
profile: {
identityKey: 'unknown',
name: 'Bitcoin User (Offline)'
}
};
}
// Wait before retry
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
Best Practices
1. Always Provide Fallbacks
Never assume BAP profile data will be available:
const displayName = userInfo.profile?.name || 'Bitcoin User';
const profileImage = userInfo.profile?.image || '/default-avatar.png';
2. Progressive Enhancement
Build your UI to work without BAP profiles, then enhance with profile data:
function UserCard({ userInfo }) {
return (
<div className="user-card">
{/* Always available */}
<div className="public-key">{userInfo.public_key}</div>
{/* Enhanced with BAP profile */}
{userInfo.profile?.name && (
<h3>{userInfo.profile.name}</h3>
)}
{userInfo.profile?.image && (
<img src={userInfo.profile.image} alt="Profile" />
)}
</div>
);
}
3. Cache Profile Data
Cache profile data to reduce API calls:
const profileCache = new Map();
async function getCachedProfile(publicKey: string) {
if (profileCache.has(publicKey)) {
return profileCache.get(publicKey);
}
const profile = await fetchUserProfile(publicKey);
profileCache.set(publicKey, profile);
// Cache for 5 minutes
setTimeout(() => {
profileCache.delete(publicKey);
}, 5 * 60 * 1000);
return profile;
}
4. Validate Profile URLs
Always validate URLs in profile data:
function isValidURL(url: string): boolean {
try {
const parsed = new URL(url);
return parsed.protocol === 'https:' || parsed.protocol === 'http:';
} catch {
return false;
}
}
function sanitizeProfileImage(imageUrl: string): string {
if (!imageUrl || !isValidURL(imageUrl)) {
return '/default-avatar.png';
}
return imageUrl;
}
Resources
- BAP Specification - Official BAP protocol documentation
- Sigma Identity - Create and manage BAP profiles
- BAP Explorer - Browse BAP profiles on the blockchain
- API Reference - Complete userinfo endpoint documentation