Access Control
Access Control Examples
Coming Soon
These examples demonstrate planned functionality. The API is not yet available.
This page provides complete examples of integrating Access Control into your applications.
NFT Membership Site
A premium content platform that requires NFT ownership for access.
Setup
// lib/access-control.ts
import { sigmaAuth } from './auth-client';
export async function checkMembership(publicKey: string, accessToken: string) {
const response = await fetch('https://auth.sigmaidentity.com/api/verify/nft', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
publicKey,
collection: 'premium-members'
})
});
const { verified } = await response.json();
return verified;
}Middleware
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { verifyJWT } from './lib/jwt';
import { checkMembership } from './lib/access-control';
export async function middleware(request: NextRequest) {
// Check if accessing premium content
if (request.nextUrl.pathname.startsWith('/premium')) {
const token = request.cookies.get('access_token')?.value;
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
try {
const jwt = await verifyJWT(token);
const hasMembership = await checkMembership(jwt.pubkey, token);
if (!hasMembership) {
return NextResponse.redirect(new URL('/upgrade', request.url));
}
} catch (error) {
return NextResponse.redirect(new URL('/login', request.url));
}
}
return NextResponse.next();
}
export const config = {
matcher: '/premium/:path*'
};UI Component
// components/membership-gate.tsx
'use client';
import { useEffect, useState } from 'react';
import { checkMembership } from '@/lib/access-control';
import { useAuthStore } from '@/lib/auth-store';
export function MembershipGate({ children }: { children: React.ReactNode }) {
const { publicKey, accessToken } = useAuthStore();
const [hasMembership, setHasMembership] = useState<boolean | null>(null);
useEffect(() => {
if (publicKey && accessToken) {
checkMembership(publicKey, accessToken).then(setHasMembership);
}
}, [publicKey, accessToken]);
if (hasMembership === null) {
return <div>Checking membership...</div>;
}
if (!hasMembership) {
return (
<div className="text-center p-8">
<h2 className="text-2xl font-bold mb-4">Premium Content</h2>
<p className="mb-6">This content requires a Premium Member NFT.</p>
<a href="/upgrade" className="btn-primary">
Get Membership
</a>
</div>
);
}
return <>{children}</>;
}Tiered Rewards Program
A loyalty platform with Gold/Diamond/Platinum tiers based on token holdings.
Tier Configuration
// lib/tiers.ts
export const REWARD_TIERS = {
bronze: {
threshold: 100,
discount: 0.05,
name: 'Bronze',
icon: '🥉'
},
silver: {
threshold: 1000,
discount: 0.10,
name: 'Silver',
icon: '🥈'
},
gold: {
threshold: 10000,
discount: 0.15,
name: 'Gold',
icon: '🥇'
},
diamond: {
threshold: 50000,
discount: 0.25,
name: 'Diamond',
icon: '💎'
}
};
export async function getUserTier(publicKey: string, accessToken: string) {
const thresholds = Object.fromEntries(
Object.entries(REWARD_TIERS).map(([key, { threshold }]) => [key, threshold])
);
const response = await fetch('https://auth.sigmaidentity.com/api/verify/threshold', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
publicKey,
token: 'REWARD',
thresholds
})
});
const { tier, balance } = await response.json();
return { tier, balance, config: REWARD_TIERS[tier as keyof typeof REWARD_TIERS] };
}Checkout Integration
// app/api/checkout/route.ts
import { NextResponse } from 'next/server';
import { verifyJWT } from '@/lib/jwt';
import { getUserTier } from '@/lib/tiers';
export async function POST(request: Request) {
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const jwt = await verifyJWT(token);
const { items, total } = await request.json();
// Get user's tier and apply discount
const { tier, config } = await getUserTier(jwt.pubkey, token);
const discount = config?.discount || 0;
const discountedTotal = total * (1 - discount);
return NextResponse.json({
items,
subtotal: total,
discount: discount * 100, // Percentage
total: discountedTotal,
tier: config?.name || 'No tier'
});
}Tier Display Component
// components/tier-badge.tsx
'use client';
import { useEffect, useState } from 'react';
import { getUserTier } from '@/lib/tiers';
import { useAuthStore } from '@/lib/auth-store';
export function TierBadge() {
const { publicKey, accessToken } = useAuthStore();
const [tierData, setTierData] = useState<any>(null);
useEffect(() => {
if (publicKey && accessToken) {
getUserTier(publicKey, accessToken).then(setTierData);
}
}, [publicKey, accessToken]);
if (!tierData?.config) {
return null;
}
const { tier, balance, config } = tierData;
return (
<div className="inline-flex items-center gap-2 rounded-full bg-gradient-to-r from-amber-500 to-amber-600 px-4 py-2 text-white">
<span className="text-xl">{config.icon}</span>
<div>
<div className="font-bold text-sm">{config.name} Member</div>
<div className="text-xs opacity-90">{(config.discount * 100)}% discount</div>
</div>
</div>
);
}DAO Voting System
A governance platform where voting power is based on token holdings.
Voting Logic
// lib/governance.ts
export async function getVotingPower(publicKey: string, accessToken: string) {
const response = await fetch('https://auth.sigmaidentity.com/api/verify/threshold', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
publicKey,
token: 'GOV',
thresholds: {
voter: 1000 // Minimum 1000 tokens to vote
}
})
});
const { tier, balance } = await response.json();
return {
canVote: tier === 'voter',
votingPower: balance,
tokensNeeded: tier ? 0 : 1000 - balance
};
}
export async function castVote(
proposalId: string,
vote: 'for' | 'against',
publicKey: string,
accessToken: string
) {
const { canVote, votingPower } = await getVotingPower(publicKey, accessToken);
if (!canVote) {
throw new Error('Insufficient tokens to vote');
}
// Record vote with voting power
const response = await fetch('/api/governance/vote', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
proposalId,
vote,
votingPower
})
});
return response.json();
}Voting UI
// components/proposal-vote.tsx
'use client';
import { useState, useEffect } from 'react';
import { getVotingPower, castVote } from '@/lib/governance';
import { useAuthStore } from '@/lib/auth-store';
export function ProposalVote({ proposalId }: { proposalId: string }) {
const { publicKey, accessToken } = useAuthStore();
const [votingData, setVotingData] = useState<any>(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (publicKey && accessToken) {
getVotingPower(publicKey, accessToken).then(setVotingData);
}
}, [publicKey, accessToken]);
const handleVote = async (vote: 'for' | 'against') => {
if (!publicKey || !accessToken) return;
setLoading(true);
try {
await castVote(proposalId, vote, publicKey, accessToken);
alert('Vote cast successfully!');
} catch (error) {
alert(error.message);
} finally {
setLoading(false);
}
};
if (!votingData) {
return <div>Loading...</div>;
}
if (!votingData.canVote) {
return (
<div className="border rounded-lg p-6">
<h3 className="font-bold text-lg mb-2">Voting Requires 1,000 GOV Tokens</h3>
<p className="text-muted-foreground mb-4">
You need {votingData.tokensNeeded} more tokens to participate in governance.
</p>
<a href="/get-tokens" className="btn-primary">
Get GOV Tokens
</a>
</div>
);
}
return (
<div className="border rounded-lg p-6">
<h3 className="font-bold text-lg mb-4">Cast Your Vote</h3>
<p className="text-muted-foreground mb-4">
Voting power: <strong>{votingData.votingPower.toLocaleString()}</strong> GOV
</p>
<div className="flex gap-3">
<button
onClick={() => handleVote('for')}
disabled={loading}
className="btn-primary flex-1"
>
Vote For
</button>
<button
onClick={() => handleVote('against')}
disabled={loading}
className="btn-secondary flex-1"
>
Vote Against
</button>
</div>
</div>
);
}Subscription NFT System
Monthly subscription tokens that serve as access passes.
Subscription Check
// lib/subscription.ts
export async function checkSubscription(publicKey: string, accessToken: string) {
// Current month identifier (e.g., "2025-01" for January 2025)
const currentMonth = new Date().toISOString().slice(0, 7);
const response = await fetch('https://auth.sigmaidentity.com/api/verify/nft', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
publicKey,
collection: 'monthly-subscription',
tokenId: currentMonth
})
});
const { verified } = await response.json();
return {
isSubscribed: verified,
currentMonth,
nextMonth: getNextMonth(currentMonth)
};
}
function getNextMonth(current: string): string {
const [year, month] = current.split('-').map(Number);
const next = new Date(year, month, 1); // month is 0-indexed
return next.toISOString().slice(0, 7);
}Purchase Flow
// app/api/subscription/purchase/route.ts
import { NextResponse } from 'next/server';
import { verifyJWT } from '@/lib/jwt';
export async function POST(request: Request) {
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!token) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const jwt = await verifyJWT(token);
const { month } = await request.json();
// Calculate pro-rated price
const daysInMonth = new Date(month + '-01').getDate();
const daysRemaining = daysInMonth - new Date().getDate() + 1;
const fullPrice = 50; // $50/month
const proratedPrice = (fullPrice / daysInMonth) * daysRemaining;
// Mint NFT for the specified month
// This would integrate with js-1sat-ord to create the NFT
const nft = await mintSubscriptionNFT({
publicKey: jwt.pubkey,
month,
price: proratedPrice
});
return NextResponse.json({
nftId: nft.id,
month,
price: proratedPrice,
paymentUrl: nft.paymentUrl
});
}Next Steps
- NFT Verification - NFT ownership details
- Threshold Verification - Token balance details
- API Reference - Complete API documentation