//Checking Roles in Your App

Checking Roles in Your App

This tutorial shows how to validate user roles from both your app's frontend and backend — covering platform admin, app admin, org roles, and feature entitlements.

The Three Checks

| What you want to know | Use | |---|---| | Who is this user and what can they do? | GET /api/auth/me | | Does this user have feature X? | POST /api/entitlements/check | | Is this user an admin of my specific app? | GET /api/marketplace/apps/:id/access |

---

Frontend: Driving Permission-Aware UI

Fetch /api/auth/me once after login and store the result. Use it to show or hide UI elements without making a call per page.

~~~typescript // React / SolidJS — call once after auth const me = await fetch('/api/auth/me', { headers: { Authorization: Bearer ${token} }, }).then(r => r.json());

// me.app_admin → string[] (app UUIDs this user can admin) // me.platform_admin → boolean // me.entitlements → string[] (active feature slugs)

const isAppAdmin = me.app_admin.includes(APP_ID); const canUseAnalytics = me.entitlements.includes('advanced_analytics'); ~~~

SolidJS example with a reactive store:

~~~typescript import { createResource } from 'solid-js';

const [me] = createResource(token, (tok) => fetch('/api/auth/me', { headers: { Authorization: Bearer ${tok} } }) .then(r => r.json()) );

// In JSX: // // // ~~~

---

Backend: Middleware Gate (Rust / Axum)

Use the SDK's verify_token for local validation and check_entitlement for server-side feature checks.

~~~rust use axum::{extract::State, http::HeaderMap, Json}; use buttrbase_sdk::ButtrBaseClient;

// Gate a route on app admin status async fn admin_only_handler( State(bb): State, headers: HeaderMap, ) -> Result, AppError> { let bearer = extract_bearer(&headers)?;

// Local JWKS verification — no network call let claims = bb.verify_token(&bearer).await?;

if !claims.app_admin.contains(&APP_ID) { return Err(AppError::forbidden("app admin required")); }

// ... handler logic Ok(Json(serde_json::json!({ "ok": true }))) } ~~~

For entitlement checks:

~~~rust let result = bb.check_entitlement(&bearer, "advanced_analytics").await?; if !result.granted { return Err(AppError::forbidden("entitlement required: advanced_analytics")); } ~~~

---

Backend: Middleware Gate (Node / Express)

~~~typescript import { ButtrBaseClient } from '@buttrbase/sdk';

const bb = new ButtrBaseClient({ clientId: process.env.BB_CLIENT_ID!, clientSecret: process.env.BB_CLIENT_SECRET! });

// Middleware export async function requireAppAdmin(req, res, next) { const bearer = req.headers.authorization?.slice(7); if (!bearer) return res.status(401).json({ error: 'missing token' });

const claims = await bb.verifyToken(bearer); if (!claims.app_admin.includes(process.env.APP_ID!)) { return res.status(403).json({ error: 'app admin required' }); }

req.claims = claims; next(); }

// Usage router.get('/admin/installs', requireAppAdmin, async (req, res) => { // ... }); ~~~

---

Platform Admin Check

Platform admin routes are guarded server-side by ButtrBase itself. If you are building a self-hosted management tool that needs to distinguish platform admins from regular users:

~~~typescript const claims = await bb.verifyToken(bearer); if (!claims.platform_admin) { return res.status(403).json({ error: 'platform admin required' }); } ~~~

---

Token Refresh After Grant Changes

Role grants (app_admin, entitlements) are embedded at token issuance. If you grant or revoke access, the user's current token remains valid until it expires (15 minutes by default). For non-time-critical changes this is acceptable.

For immediate effect, tell the user to refresh:

~~~http POST /api/auth/refresh { "refresh_token": "..." } ~~~

The new access token will reflect the updated claims.