Welcome to the Atlas Rig integration guide. This document explains how third-party and internal applications can connect to the Atlas Rig system to authenticate users via the Authorization Code Flow with PKCE.
Atlas Rig Auth acts as a central Identity Provider (IdP) following OAuth 2.0 and OpenID Connect (OIDC) standards.
Base URL: https://auth.adolfrey.com
| Endpoint | Path | Method | Description |
|---|---|---|---|
| Discovery | /api/.well-known/openid-configuration |
GET | Returns the OIDC metadata document. |
| Authorization | /api/authorize |
GET | Initiates the login flow and redirects the user to the login UI. |
| Token | /api/token |
POST | Exchanges an authorization code for an ID token, access token, and refresh token. |
| UserInfo | /api/userinfo |
GET/POST | Returns profile information about the authenticated user. |
| JWKS | /.well-known/jwks.json |
GET | JSON Web Key Set used to verify the signature of JWTs. |
The easiest way to integrate is by using a standard OIDC library, which handles the complex tasks of state management, PKCE generation, and token rotation automatically.
For React/TypeScript applications, we recommend oidc-client-ts.
npm install oidc-client-tsimport { UserManager, WebStorageStateStore } from 'oidc-client-ts';
const userManager = new UserManager({
authority: 'https://auth.adolfrey.com/api', // Discovery URL base
client_id: 'your-client-id',
redirect_uri: window.location.origin + '/auth/callback',
popup_redirect_uri: window.location.origin + '/auth/popup-callback',
response_type: 'code',
scope: 'openid profile email', // Add other scopes as needed
userStore: new WebStorageStateStore({ store: window.sessionStorage }),
monitorSession: false // Turn off if iframe session monitoring isn't fully supported
});This will redirect the user entirely to the auth domain and back.
const login = () => {
userManager.signinRedirect();
};On the route corresponding to your redirect_uri (e.g., /auth/callback):
const handleCallback = async () => {
try {
const user = await userManager.signinRedirectCallback();
console.log("Logged in user:", user);
// Redirect to dashboard
} catch (e) {
console.error("Login failed", e);
}
};Alternatively, you can open a popup window for login, keeping your main application state intact.
const loginWithPopup = async () => {
try {
const user = await userManager.signinPopup();
console.log("Logged in user:", user);
} catch (e) {
console.error("Popup login failed", e);
}
};On the route corresponding to your popup_redirect_uri (e.g., /auth/popup-callback):
// The library handles this automatically when called on the popup page
userManager.signinPopupCallback();For the most streamlined experience in React/TypeScript applications, use our official wrapper library which provides a useAuth hook and simplified state management.
import { AuthProvider } from 'ar-auth-client';
const config = {
authority: 'https://auth.adolfrey.com',
clientId: 'my-app-id',
redirectUri: window.location.origin + '/callback',
scope: 'openid profile email'
};
root.render(
<AuthProvider config={config}>
<App />
</AuthProvider>
);import { useAuth } from 'ar-auth-client';
export const Navbar = () => {
const { user, login, logout, loginState, isLoading } = useAuth();
if (isLoading) return <span>...</span>;
return user ? (
<div>
<span>{user.name}</span>
<button onClick={logout}>Logout</button>
</div>
) : (
<button onClick={() => login({ state: { from: 'navbar' } })}>Login</button>
);
};The login function resolves with a result object (for popup flows) containing the OIDC state, the user, and the tokens. For redirect flows, the state is available via the loginState property in the context.
const handleLogin = async () => {
const result = await login({
state: { returnUrl: '/dashboard' }
});
if (result) {
console.log("Flow state:", result.state);
console.log("ID Token:", result.idToken);
}
};If you cannot use a library, you must implement the Authorization Code Flow with PKCE manually.
Before redirecting the user, cryptographically generate a high-entropy code_verifier and use SHA-256 to hash it into a code_challenge.
// Pseudo-code for Browser APIs
const verifier = generateRandomString(43); // Ensure it's between 43-128 chars
const challenge = base64URLEncode(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier)));
sessionStorage.setItem('pkce_verifier', verifier); // Store temporarilyConstruct the URL and redirect the user's browser.
Endpoint: GET https://auth.adolfrey.com/api/authorize
Parameters:
client_id: Your registered Client ID.redirect_uri: Whitelisted redirect URI for your app.response_type: Must becode.state: Random string to prevent CSRF attacks. Verify this on callback.code_challenge: The hashed string generated in Step 1.code_challenge_method: Must beS256.scope: E.g.,openid profile. For cross-app access, useapi://{target-client-id}/{scope}.
Example URL with Cross-App Scope:
https://auth.adolfrey.com/api/authorize?client_id=my-app&redirect_uri=https://myapp.com/callback&response_type=code&state=xyz123&code_challenge=AbCdEf1234&code_challenge_method=S256&scope=openid%20profile%20api://inventory-service/read:stock
The user will be redirected back to your redirect_uri with the parameters:
?code=AUTH_CODE_HERE&state=xyz123
Verify that the state matches the one you sent in Step 2. If it does, extract the code.
Make a POST request to the Token endpoint, passing the code and the original code_verifier.
Endpoint: POST https://auth.adolfrey.com/api/token
Content-Type: application/x-www-form-urlencoded or application/json
Payload:
grant_type:authorization_codeclient_id: Your Client ID.redirect_uri: Must match the redirect URI from Step 2.code: The code extracted from the URL.code_verifier: The original random string you stored in Step 1.client_secret: (Only required if your application is a backend/confidential client).
Response:
{
"access_token": "eyJhb...",
"refresh_token": "dj9a...",
"token_type": "Bearer",
"expires_in": 300,
"scope": "openid profile api://inventory-service/read:stock"
}The access_token is a Signed JWT. Use it in the Authorization: Bearer <token> header of subsequent API requests. The Backend will validate the signature against the public key found at /.well-known/jwks.json.
Atlas Rig supports Cross-App Trust, allowing one application (App A) to request access to resources/scopes owned by another application (App B).
Cross-app scopes must use the following URI format:
api://{target-client-id}/{scope-name}
Example: api://inventory-service/read:stock
For a cross-app scope to be granted in an access token, the following conditions must be met:
- Trust Relationship: An administrator must create a "Cross-App Trust" record in the Admin UI authorizing
App A(Requesting Client) to request a specific scope fromApp B(Target Client). - Authorization Rule:
- For User Sessions: The user must have been manually assigned the scope for
App B. - For App Principal Sessions: The scope must be marked as Client-Only (App Principal).
- OR, the scope must be marked as Admin Approved (Auto-grant) for
App B(applies to user sessions).
- For User Sessions: The user must have been manually assigned the scope for
Applications can request tokens directly using their own credentials to access trusted cross-app scopes.
Token Request:
POST https://auth.adolfrey.com/api/token
Payload:
grant_type:client_credentialsclient_id:app-aclient_secret:secret-ascope:api://app-b/read:data
Add the qualified scope to your authorization or token request alongside standard OIDC scopes.
const userManager = new UserManager({
// ... other config
scope: 'openid profile email api://inventory-service/read:stock'
});https://auth.adolfrey.com/api/authorize?client_id=my-app&...&scope=openid%20profile%20api://inventory-service/read:stock
When granted, these scopes appear in the scope claim of the access token and are also mapped to the role claim to simplify integration with standard authorization middleware.
{
"sub": "user_123",
"client_id": "my-app",
"scope": "openid profile api://inventory-service/read:stock",
"role": ["api://inventory-service/read:stock"]
}Atlas Rig provides a dedicated Prototype UI Bypass mode designed for UI-only prototypes. This mode allows you to simulate a complete authentication flow (with realistic-looking login screens) without needing to register a backend application, configure a database, or validate OIDC tokens.
The easiest way to use the prototype bypass is via the Atlas Rig Auth Client library.
Simply add the isPrototype: true flag to your AuthConfig:
import { AuthProvider } from 'ar-auth-client';
const config = {
authority: 'https://auth.adolfrey.com',
clientId: 'arbitrary-prototype-id', // No registration needed!
redirectUri: window.location.origin + '/callback',
scope: 'openid profile email',
isPrototype: true // Automatically handles the prototype bypass
};If you are not using the client library, you can manually append &prototype=true to the /api/authorize endpoint URL and handle the return ?code=prototype_bypass_code yourself.
After your frontend obtains an access token, your backend API must verify it before trusting the request. Atlas Rig provides official SDKs for .NET, Python, and JavaScript/TypeScript.
Important: Token verification does NOT require
client_idorclient_secret. The SDK only needs the Authority URL to fetch the public signing keys (JWKS) and validate the RS256 signature.
Install from GitHub Packages:
dotnet add package Ar.Auth.OpenId
dotnet add package Ar.Auth.OpenId.AspNetCore # For ASP.NET Core
dotnet add package Ar.Auth.OpenId.AzureFunctions # For Azure Functionsusing Ar.Auth.OpenId.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
// One-liner: adds JWT Bearer auth pointing at Atlas Rig OIDC discovery
builder.Services.AddArAuth();
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/api/profile", (ClaimsPrincipal user) =>
{
return Results.Ok(new { sub = user.FindFirst("sub")?.Value });
}).RequireAuthorization();
app.Run();using Ar.Auth.OpenId.AzureFunctions;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(worker =>
{
worker.UseArAuth(
configure: options => { options.Authority = "https://auth.adolfrey.com/api"; },
configureMiddleware: mw => { mw.RequiredRoles = new[] { "admin" }; }
);
})
.Build();
host.Run();Then access the user in your function via context.Items["ArAuthUser"].
using Ar.Auth.OpenId;
var client = new ArAuthClient(); // defaults to https://auth.adolfrey.com/api
var principal = await client.VerifyTokenAsync("eyJhbGciOiJSUzI1Ni...");
Console.WriteLine($"User: {principal.FindFirst("sub")?.Value}");Install from PyPI:
pip install ar-auth-client
pip install ar-auth-client[fastapi] # For FastAPI
pip install ar-auth-client[flask] # For Flask
pip install ar-auth-client[azure] # For Azure Functionsfrom fastapi import FastAPI, Depends
from ar_auth.fastapi import ArAuthBearer
app = FastAPI()
admin_auth = ArAuthBearer(required_roles=["admin"])
@app.get("/admin/dashboard")
def get_dashboard(user_payload: dict = Depends(admin_auth)):
return {"user_id": user_payload["sub"], "roles": user_payload["roles"]}from flask import Flask, jsonify, g
from ar_auth.flask import requires_auth
app = Flask(__name__)
@app.route("/api/profile")
@requires_auth(required_scopes=["profile"])
def get_profile():
user = g.ar_auth_user
return jsonify({"sub": user["sub"], "email": user.get("email")})from ar_auth import ArAuthClient
client = ArAuthClient() # defaults to https://auth.adolfrey.com/api
claims = client.verify_token("eyJhbGciOiJSUzI1Ni...")
print("User:", claims["sub"])The frontend ar-auth-client library handles the full authentication flow. See Method 3 above for the frontend setup.
For backend (Node.js) token verification, use standard OIDC/JWT libraries such as jose:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const JWKS = createRemoteJWKSet(
new URL('https://auth.adolfrey.com/.well-known/jwks.json')
);
async function verifyToken(token: string) {
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://auth.adolfrey.com/api',
});
return payload;
}| Step | Frontend | Backend |
|---|---|---|
| 1. Login | Use ar-auth-client (React) or oidc-client-ts to initiate the Authorization Code + PKCE flow |
β |
| 2. Get Token | Library exchanges the auth code for an access_token automatically |
β |
| 3. API Call | Send Authorization: Bearer <access_token> header with each request |
β |
| 4. Verify Token | β | Use Ar.Auth.OpenId (.NET), ar-auth-client (Python), or jose (Node.js) to validate the token's signature, issuer, and expiration |
| 5. Authorize | β | Check roles and scope claims in the verified token to enforce access control |