@brah.ma/aham
v1.0.6
Published
Brahma OS Identity Toolkit
Downloads
585
Readme
@brah.ma/aham — SDK V3
Liquid SSO + Unified Karma + On-Chain Tokens for the Brahma ecosystem.
Table of Contents
- Installation
- Quick Start (3 Lines)
- Upgrading from V2
- BrahmaProvider
- Authentication (Liquid SSO)
- Session Management
- Karma System
- On-Chain Tokens
- Utilities
- API Reference
- Security
Installation
npm install @brah.ma/aham @brah.ma/sthanPeer dependencies (required in your project):
npm install react react-dom framer-motion lucide-react clsx tailwind-mergeQuick Start
Three lines to authenticate a user on any Brahma website:
import { BrahmaProvider, AhamInitiateButton } from '@brah.ma/aham';
export default function App() {
return (
<BrahmaProvider config={{ ahamGatewayUrl: "https://aham.brah.ma/gateway" }}>
<AhamInitiateButton onVerified={(user) => console.log("Welcome:", user)} />
</BrahmaProvider>
);
}That's it. The button handles the entire SSO flow, session persistence, and token management.
Upgrading from V2
What Changed
| Feature | V2 (aham-auth-sdk.js) | V3 (@brah.ma/aham) |
|---|---|---|
| Architecture | Vanilla JS class (AhamAuth) | React hooks + Context Provider |
| SSO | Popup only | Popup, Redirect, Silent (Omni-Flow) |
| Karma | Single karmaPoints integer | Multi-denomination (KARMA, PUNYA, DHANA, ...) |
| Web3 | Not integrated | Unified — one call for Web2 + Web3 sync |
| Sessions | localStorage with manual restore | Auto-restore via BrahmaProvider |
| API Calls | Manual fetch with Bearer token | useBrahmaAPI hook with auto-auth |
Migration Steps
Step 1: Replace the script tag
- <script src="https://aham.brah.ma/aham-auth-sdk.js"></script>
- <script>
- window.ahamAuth = new AhamAuth({ authDomain: 'https://aham.brah.ma' });
- </script>
+ npm install @brah.ma/aham @brah.ma/sthanStep 2: Wrap your app in BrahmaProvider
// _app.tsx or layout.tsx
import { BrahmaProvider } from '@brah.ma/aham';
export default function App({ children }) {
return (
<BrahmaProvider config={{
ahamGatewayUrl: "https://aham.brah.ma/gateway",
ahamApiUrl: "https://aham.brah.ma",
}}>
{children}
</BrahmaProvider>
);
}Step 3: Replace V2 API calls
// Before (V2)
- const isLoggedIn = window.ahamAuth.isAuthenticated();
- const user = window.ahamAuth.getUser();
- await window.ahamAuth.addKarma(5, 'page_visit');
// After (V3)
+ const { isAuthenticated, user } = useBrahmaSession();
+ const { add } = useKarma();
+ await add('KARMA', 5, 'page_visit');Step 4: Replace the login button
- <button onclick="window.ahamAuth.login()">Login</button>
+ <AhamInitiateButton
+ flow="popup"
+ onVerified={(payload) => console.log('User:', payload)}
+ />BrahmaProvider
Wraps your entire app. Manages shared auth state, session auto-restore, and config.
import { BrahmaProvider } from '@brah.ma/aham';
<BrahmaProvider config={{
ahamGatewayUrl: "https://aham.brah.ma/gateway",
ahamApiUrl: "https://aham.brah.ma",
flow: "popup", // Default SSO flow
sessionMaxAge: 3600000, // 1 hour (ms)
}}>
<App />
</BrahmaProvider>Config Options
| Prop | Type | Default | Description |
|---|---|---|---|
| ahamGatewayUrl | string | https://aham.brah.ma/gateway | URL of the SSO gateway |
| ahamApiUrl | string | https://aham.brah.ma | Base URL for API calls |
| flow | 'popup' \| 'redirect' \| 'silent' | 'popup' | Default authentication flow |
| sessionMaxAge | number | 3600000 | Session expiry in milliseconds |
| allowedOrigins | string[] | ['https://aham.brah.ma'] | Origins accepted for postMessage |
Authentication
useAham
The core SSO hook. Supports three authentication flows.
import { useAham } from '@brah.ma/aham';
function LoginPage() {
const { initiate, intercept, isVerified, atmaSutra, error } = useAham({
flow: 'popup',
ahamGatewayUrl: 'https://aham.brah.ma/gateway',
onSuccess: (payload) => {
console.log('AtmaSutra:', payload.atmaSutra);
console.log('MoolSthan:', payload.moolSthan);
},
onFailed: (err) => console.error(err),
});
return <button onClick={initiate}>Login to Brahma</button>;
}Return Values
| Property | Type | Description |
|---|---|---|
| initiate() | () => Promise<void> | Triggers the configured SSO flow |
| intercept(action, callback) | (string, () => void) => void | Auth-gate an action |
| isInitiating | boolean | True while SSO is in progress |
| isVerified | boolean | True after successful auth |
| atmaSutra | string \| null | User's permanent ID |
| error | string \| null | Error message if auth failed |
AhamInitiateButton
Pre-built, animated "hold-to-authenticate" button.
import { AhamInitiateButton } from '@brah.ma/aham';
<AhamInitiateButton
flow="popup"
ahamGatewayUrl="https://aham.brah.ma/gateway"
theme="mystical-dark" // "mystical-dark" | "light"
holdDurationMs={2000} // How long to hold (ms)
onVerified={(payload) => {}} // Success callback
onFailed={(err) => {}} // Error callback
/>Omni-Flow Modes
popup — The Seamless Path
Opens a popup window to aham.brah.ma/gateway. On success, sends the token back via a secure postMessage. No page navigation.
useAham({ flow: 'popup' })Best for: SPAs, dashboards, any page where you don't want to lose state.
redirect — The Traditional Path
Full page redirect to the gateway. After auth, redirects back to your page with the token.
useAham({ flow: 'redirect' })Best for: Server-rendered pages, mobile browsers that block popups.
silent — The Invisible Path
Opens a hidden <iframe> to check if the user already has an active Aham session. If yes, auto-authenticates without any UI. Falls back to popup if blocked by ITP (Safari/Brave).
useAham({ flow: 'silent' })Best for: Auto-login on return visits, "stay logged in" experiences.
Action Interception
Gate any action behind authentication. If the user isn't logged in, the SDK triggers auth first, then automatically resumes the action after success.
const { intercept } = useAham({ flow: 'popup' });
function handlePurchase() {
intercept('BUY_ITEM', () => {
// This only runs after the user is authenticated
processPurchase();
});
}
<button onClick={handlePurchase}>Buy Now — ₹99</button>Redirect flow handles this too: The pending action is serialized to localStorage and replayed after the redirect returns.
Session Management
useBrahmaSession
Quick access to auth state. Reads from BrahmaProvider context.
import { useBrahmaSession } from '@brah.ma/aham';
function Header() {
const { user, token, isAuthenticated, isLoading, logout, refresh } = useBrahmaSession();
if (isLoading) return <Spinner />;
return isAuthenticated ? (
<div>
<span>Welcome, {user.atmaSutra}</span>
<button onClick={logout}>Sign Out</button>
</div>
) : (
<AhamInitiateButton />
);
}| Property | Type | Description |
|---|---|---|
| user | BrahmaUser \| null | { atmaSutra, moolSthan, ...any } |
| token | string \| null | JWT session token |
| isAuthenticated | boolean | Quick auth check |
| isLoading | boolean | True during initial session restore |
| logout() | () => void | Clears all session data |
| refresh() | () => void | Re-validates session from storage |
useBrahmaAPI
Authenticated fetch wrapper. Auto-attaches the Bearer token. Auto-handles 401 by calling logout().
import { useBrahmaAPI } from '@brah.ma/aham';
function ProfilePage() {
const { fetch: brahmaFetch } = useBrahmaAPI();
async function loadProfile() {
const res = await brahmaFetch('/api/user/profile');
const data = await res.json();
// Token is already attached, 401s auto-logout
}
}| Method | Signature | Description |
|---|---|---|
| fetch | (endpoint: string, options?: RequestInit) => Promise<Response> | Relative paths resolve against ahamApiUrl |
Karma System
useKarma
One function for the user. Two layers behind the scenes.
Every add() call instantly writes to Web2 (Prisma DB) AND queues an on-chain mint. The sync status is tracked per-record.
import { useKarma } from '@brah.ma/aham';
function RewardPanel() {
const { balances, add, fetchBalances, getHistory, isLoading } = useKarma();
// Add karma — single call handles both Web2 and Web3
const reward = async () => {
await add('PUNYA', 5, 'temple_visit', 'Visited morning aarti');
await fetchBalances(); // Refresh UI
};
return (
<div>
<p>KARMA: {balances.KARMA?.web2 || 0}</p>
<p>PUNYA: {balances.PUNYA?.web2 || 0}</p>
<button onClick={reward}>+5 Punya</button>
</div>
);
}add(denom, amount, action, description?)
| Param | Type | Description |
|---|---|---|
| denom | string | Token denomination: 'KARMA', 'PUNYA', 'DHANA', or any custom |
| amount | number | Positive integer to add |
| action | string | Machine-readable action ID (e.g. 'daily_sankalp') |
| description | string? | Optional human-readable note |
Returns: { transaction, balances } — the transaction record and updated balances.
Balance Shape
balances = {
KARMA: { web2: 42, onChain: 35, pending: 7 },
PUNYA: { web2: 10, onChain: 10, pending: 0 },
DHANA: { web2: 0, onChain: 0, pending: 0 },
}| Field | Meaning |
|---|---|
| web2 | Total accumulated in the database (instant) |
| onChain | Confirmed on the Brahman blockchain |
| pending | Queued for chain mint (web2 − onChain) |
Multi-Denomination Support
Denominations are open strings, not enums. You can create any denomination without a schema migration:
add('KARMA', 11, 'daily_sankalp'); // Standard karma
add('PUNYA', 5, 'temple_visit'); // Merit points
add('DHANA', 100, 'donation'); // Wealth tokens
add('SHAKTI', 1, 'meditation'); // Custom — works immediately!Chain Sync Lifecycle
add('KARMA', 5, 'action')
│
├── INSTANT → KarmaBalance.KARMA += 5 (DB)
├── INSTANT → KarmaTransaction { chainStatus: 'pending' }
│
└── BACKGROUND (cron) → /api/karma/v3/sync
├── Reads all pending transactions
├── Batches per user per denom
├── Mints ukarma on Brahman chain via Treasury
└── Updates chainStatus: 'minted', stores txHashThe cron/sync is a backend concern. Partners never need to think about it — add() is fire-and-forget from their perspective.
On-Chain Tokens
useTokens
Live query of the Brahman blockchain for all token denominations.
import { useTokens } from '@brah.ma/aham';
function WalletView() {
const { balances, refreshBalances, isLoading } = useTokens();
useEffect(() => { refreshBalances(); }, []);
return (
<div>
{Object.entries(balances).map(([denom, amount]) => (
<p key={denom}>{denom}: {amount}</p>
))}
{/* Output: KARMA: 11, PUNYA: 10, DHANA: 0 */}
</div>
);
}New denominations minted on the chain auto-appear — no SDK update needed.
Utilities
Token Inspection (Client-Side)
import { decodeToken, isTokenExpired, getTokenClaims } from '@brah.ma/aham';
const claims = decodeToken(jwt);
// { atmaSutra, moolSthan, exp, iat, iss, aud }
if (isTokenExpired(jwt)) {
// Trigger re-auth
}
const valid = getTokenClaims(jwt); // null if expiredNote: These decode the JWT payload without verifying the signature. Signature verification must happen server-side.
Session Persistence (Low-Level)
import { saveSession, getSession, clearSession, isSessionValid } from '@brah.ma/aham';
saveSession(token, { atmaSutra: '...', moolSthan: '...' });
const session = getSession(); // { token, user, createdAt }
const valid = isSessionValid(); // true if < 1 hour old
clearSession(); // Wipes everythingYou normally don't need these — BrahmaProvider handles persistence automatically. These are for edge cases only.
API Reference
Backend Routes (Aham Server)
| Method | Route | Auth | Description |
|---|---|---|---|
| POST | /api/karma/v3/add | Bearer JWT | Add karma (any denom). Writes DB + queues chain. |
| GET | /api/karma/v3/balance | Bearer JWT | Get all denom balances with sync status. ?history=true&limit=20 for tx log. |
| POST | /api/karma/v3/sync | x-cron-secret | Admin: batch-mint pending karma on-chain. |
| GET | /api/tokens/balance | Bearer JWT | Query live on-chain balances for all denoms. |
| POST | /api/tokens/distribute | x-cron-secret | Admin: mint specific token amounts to a user. Body: { userId, amounts: { KARMA: 5 } } |
Security
PostMessage Origin Validation
All popup and silent flows use strict origin whitelisting. Only domains matching *.brah.ma and explicitly allowed origins can communicate via postMessage. Wildcard (*) is never used.
JWT Tokens
Session tokens are short-lived JWTs (10 min). They contain atmaSutra and moolSthan claims. Raw private keys or mnemonics are never exposed to the client.
ITP Fallback
Safari and Brave block third-party cookies/iframes. The silent flow auto-degrades to popup when ITP is detected, ensuring auth always works.
Session Storage
Sessions are stored in localStorage with timestamps. BrahmaProvider auto-validates session age on mount and clears expired data.
Full Export Surface
// SSO
useAham, AhamInitiateButton
// Context
BrahmaProvider, useBrahmaContext
// Session & Auth
useBrahmaSession, useBrahmaAPI
// Karma & Tokens
useKarma, useTokens
// Utilities
saveSession, getSession, clearSession, isSessionValid
decodeToken, isTokenExpired, getTokenClaims
// Types
BrahmaConfig, BrahmaContextValue, BrahmaUser, BrahmaSession
BrahmaTokenClaims, AhamFlow, AhamConfig, AhamState
KarmaBalances, KarmaDenomBalance, KarmaHistoryEntry, TokenBalances