Real working examples — from a single protected page to full cross-domain SSO and AI agent auth.
Add the OrgNet SDK and protect any page with two lines of configuration. If the user isn't authenticated, they're redirected to your OrgNet login page and returned after sign-in.
Works in any HTML page — no framework required.
<!-- 1. Add the OrgNet SDK to your <head> -->
<!-- SDK URL available after registering your app in the dashboard -->
<script src="https://cdn.orgnet.app/v1/orgnet.min.js"></script>
<script>
// 2. Initialise with your client ID from the OrgNet dashboard
const orgnet = new OrgNetClient({
clientId: "your_client_id",
redirectUri: "https://yourapp.com/callback"
});
// 3. Protect the page — redirects to OrgNet login if not authenticated.
// Returns silently if already authenticated.
orgnet.protectPage();
</script>
protectPage() checks for a valid Access Token silently via the OrgNet SSO bridge. If no token exists, it initiates the OAuth 2.1 Authorization Code + PKCE flow. The user logs in on auth.orgnet.app and is returned to your redirectUri with a code, which the SDK exchanges for tokens automatically.On a protected page, read the full user object. OrgNet's JWT carries both the person's identity and their active org context — no extra API call needed for most cases.
The user object reflects the active org identity — including emp_id, roles, and permissions.
(async () => {
if (await orgnet.isAuthenticated()) {
const user = await orgnet.getUser();
// The full user object from the OrgNet JWT:
// {
// sub: "per_01HXABC123", // OrgNet person ID (stable)
// email: "alice@acme.com", // Login email
// emp_id: "E001", // Employee ID from your system
// org_id: "org_01HXACME", // Active organisation
// org_name: "Acme Corp", // Display name
// roles: ["Editor"], // Active roles for this app
// perms: ["doc:read","doc:write"], // Resolved permissions
// identity_count: 2 // Total orgs this person belongs to
// }
document.getElementById("username").textContent = user.email;
document.getElementById("org-name").textContent = user.org_name;
// Check a permission before showing a UI element
if (user.perms.includes("doc:write")) {
document.getElementById("edit-btn").style.display = "block";
}
}
})();
Users who are logged in on any of your registered domains are automatically authenticated on every other domain — no login prompt. The SDK handles the token bridge silently.
app2.com gets a valid token from a session started on app1.com — zero user friction.
// On app2.com — user logged in on app1.com, no login prompt shown
const orgnet = new OrgNetClient({
clientId: "app2_client_id",
redirectUri: "https://app2.com/callback"
});
// getAccessToken() calls POST /token/derive on auth.orgnet.app
// via a secure hidden iframe — the Master Session Token (MST)
// in the HttpOnly cookie on auth.orgnet.app is used to issue a
// fresh, app-scoped Access Token. The user sees nothing.
const token = await orgnet.getAccessToken();
// Use the token on your API calls
const res = await fetch("https://api.app2.com/data", {
headers: { "Authorization": `Bearer ${token}` }
});
// Listen for auth state changes (e.g. logout on another domain)
orgnet.onAuthStateChange((event) => {
if (event === "SIGNED_OUT") window.location.href = "/logged-out";
});
Validate OrgNet JWTs on your PHP/Laravel backend before processing any API request. The Composer package handles signature verification, expiry checking, and revocation lookup.
Install via Composer, add one middleware class, and protect any route.
# Install the OrgNet PHP SDK
composer require orgnet/php-sdk
# Add to config/orgnet.php
'jwks_uri' => 'https://auth.orgnet.app/.well-known/jwks.json',
'issuer' => 'https://auth.orgnet.app',
'audience' => 'https://yourapp.com',
OrgNetMiddleware validates the Bearer token, checks revocation, and injects the user into the request.
// routes/api.php
Route::middleware(['orgnet.auth'])->group(function () {
Route::get('/documents', function (Request $request) {
// OrgNetUser is injected — all claims from the JWT
$user = $request->orgnet_user;
// $user->sub — OrgNet person ID
// $user->emp_id — Employee ID from your system
// $user->org_id — Active organisation
// $user->roles — Array of active roles
// $user->perms — Array of resolved permissions
// Check permission before proceeding
if (!$user->hasPermission('doc:read')) {
return response()->json(['error' => 'Forbidden'], 403);
}
// Scope data to the user's active org
return Document::where('org_id', $user->org_id)->get();
});
});
AI agents are not users. They authenticate via the OAuth 2.1 Client Credentials grant — no browser, no redirect, no user involved. Tokens are short-lived and scope-locked.
Load credentials from your secrets vault, authenticate, and use the token on every API call.
// 1. Load credentials from your secrets vault — never hardcode
const CLIENT_ID = process.env.ORGNET_AGENT_CLIENT_ID;
const CLIENT_SECRET = process.env.ORGNET_AGENT_CLIENT_SECRET;
async function getAgentToken() {
const res = await fetch("https://auth.orgnet.app/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
scope: "openclaw:task:execute openclaw:memory:read"
// ↑ Request only the scopes your agent actually needs
})
});
const { access_token, expires_in } = await res.json();
// Token claims include: orgnet:type="agent", orgnet:org_id,
// orgnet:agent_id, orgnet:scopes, exp (15 min from now)
return { access_token, expires_at: Date.now() + (expires_in - 60) * 1000 };
// ↑ Store expires_at 60s early to rotate proactively
}
// 2. Use the token — re-authenticate when close to expiry
let tokenCache = null;
async function callApi(endpoint, body) {
if (!tokenCache || Date.now() > tokenCache.expires_at) {
tokenCache = await getAgentToken(); // Re-authenticate
}
return fetch(endpoint, {
method: "POST",
headers: {
"Authorization": `Bearer ${tokenCache.access_token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(body)
});
}
access_token value.