Authentication
Macrofy uses a server-side HMAC handshake so your users never interact with Macrofy directly. You keep full ownership of user identity — Macrofy only verifies your signature and issues a scoped session.
Never expose your appSecret in client code. Signatures must be generated
on your server. The SDK only receives the finished signature.
How it works
The handshake is a three-party exchange between your backend, the client app, and Macrofy:
- Your backend signs a
userIdwith yourappSecret(HMAC-SHA256). - The client app passes
{ appId, userId, signature }to the SDK. - The SDK sends the payload to Macrofy, which verifies the signature and returns a scoped JWT.
Handshake sequence
Your Backend Client App Macrofy
│ │ │
│── signature ──────────▶│ │
│ (HMAC-SHA256) │── connect ──────────▶│
│ │ │── verify signature
│ │ │── check quota
│ │◀──── JWT session ────│
│ │ │
Once connected, the SDK persists the session token to the device keychain and restores it automatically on relaunch.
Generate a signature
Your backend computes an HMAC-SHA256 of the userId using your appSecret, then returns the { appId, userId, signature } triple to the client.
Node.js
Since HMAC is a standard cryptographic algorithm, you can generate the signature using the native Node.js crypto module without needing to install any Macrofy-specific packages on your backend.
Node.js — HMAC signature endpoint
import { createHmac } from 'crypto'
export function createMacrofyPayload(userId: string) {
const appId = process.env.MACROFY_APP_ID!
const appSecret = process.env.MACROFY_APP_SECRET!
// Generate HMAC-SHA256 signature using native Node.js crypto
const signature = createHmac('sha256', appSecret)
.update(userId)
.digest('hex')
return { appId, userId, signature }
}
Vercel Serverless Function
app/api/macrofy-signature/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { createHmac } from 'crypto'
export async function POST(req: NextRequest) {
const { userId } = await req.json()
if (!userId) {
return NextResponse.json({ error: 'Missing userId' }, { status: 400 })
}
const signature = createHmac('sha256', process.env.MACROFY_APP_SECRET!)
.update(userId)
.digest('hex')
return NextResponse.json({
appId: process.env.MACROFY_APP_ID,
userId,
signature,
})
}
Supabase Edge Function
supabase/functions/macrofy-signature/index.ts
import { createHmac } from 'node:crypto'
function generateHmac(secret: string, payload: string): string {
return createHmac('sha256', secret).update(payload).digest('hex')
}
Deno.serve(async (req) => {
const { userId } = await req.json()
if (!userId) {
return new Response(JSON.stringify({ error: 'Missing userId' }), {
status: 400,
headers: { 'content-type': 'application/json' },
})
}
const appId = Deno.env.get('MACROFY_APP_ID')!
const secret = Deno.env.get('MACROFY_APP_SECRET')!
const signature = generateHmac(secret, userId)
return new Response(JSON.stringify({ appId, userId, signature }), {
headers: { 'content-type': 'application/json' },
})
})
Connect from the SDK
Pass the triple your backend produced to Macrofy.connect(). The SDK validates inputs locally before making a network request.
React Native — connect
import { Macrofy } from '@macrofy/react-native'
const macrofy = new Macrofy()
const session = await macrofy.connect({
appId: 'your-app-id',
userId: 'end-user-id',
signature: 'hmac-signature-from-your-backend',
})
connect() returns an AuthResponse:
AuthResponse
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "macrofy-scoped-id",
"appId": "your-app-id",
"externalId": "end-user-id"
}
}
The SDK automatically persists token and user via the configured StorageAdapter. The React Native package automatically resolves to use native secure storage via expo-secure-store or similar when available.
Session lifecycle
Restore on relaunch
Call restoreSession() at app startup. If a valid session exists in the keychain, the SDK returns it without a network request.
Restore session
const session = await macrofy.restoreSession()
if (session) {
// session.token and session.user are available
}
Check connection state
Inspect state
macrofy.isConnected() // boolean
macrofy.getSessionUser() // SessionUser | null
Disconnect
Clears the JWT and all persisted credentials from the keychain.
Disconnect
await macrofy.disconnect()
Connect endpoint reference
The SDK sends the following payload to the Macrofy gateway:
POST /api/auth/connect
{
"appId": "aBcDeFgHiJkL",
"userId": "developer-owned-user-id",
"signature": "64-char-hex-hmac-sha256"
}
- Name
appId- Type
- string (10-12 char nanoid)
- Description
The application ID from your Macrofy Dashboard.
- Name
userId- Type
- string
- Description
Your application's user identifier. Macrofy does not interpret this value — it is echoed back as
externalId.
- Name
signature- Type
- string (64-char hex)
- Description
HMAC-SHA256(appSecret, userId)computed on your server.
Responses
- Name
200- Type
- success
- Description
Returns
{ token, user }. The token is a JWT valid for 7 days.
- Name
401- Type
- error
- Description
Invalid signature — the HMAC does not match.
- Name
403- Type
- error
- Description
Sandbox quota exceeded — your app has reached its user limit.