PHP SDK
Install and use aton-network/sdk.
Connectez-vous pour voir votre propre ID client de test dans les exemples.
Coming soon. aton-network/sdk is not published on Packagist yet, so the install line on this page does not work until it is. Until then, everything works over plain HTTP: pick cURL in any code block, or start from Endpoints.
aton-network/sdk is Sign in with Atheron for PHP: PKCE, the authorize address, the code swap, userinfo, webhook signatures, the events stream and the developer API client, on PHP 8.1 and its own extensions alone. A Laravel Socialite driver, a Symfony bundle and a WordPress plugin come with it.
Install
PHP 8.1 and later, with ext-openssl to reach Atheron over https (it is on almost everywhere). No HTTP library is needed: the SDK uses PHP's own streams, follows no redirects and gives every call ten seconds. To route its calls through your own client or a proxy, hand AtonNetwork\Transport::use() a function.
composer require aton-network/sdkSign somebody in
Build the address on your server and keep $started->kept() (the verifier, state and nonce) in their session.
# The authorize request is a page the person opens, not an API call. Build the address on your
# server: a fresh PKCE pair, state and nonce for this browser, then redirect them to it.
VERIFIER=$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=\n')
CHALLENGE=$(printf '%s' "$VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr '+/' '-_' | tr -d '=\n')
STATE=$(openssl rand -hex 16)
echo "https://accounts.aton-network.org/oauth/authorize?response_type=code&client_id=$ATHERON_CLIENT_ID&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&scope=openid%20profile%20email&state=$STATE&code_challenge=$CHALLENGE&code_challenge_method=S256"(no body: the browser follows the redirect)Handle the callback
finishSignIn checks state in constant time, then swaps the code. Send the secret from a server app; leave it out in a public app.
For a whole web app on PHP's own session, AtonNetwork\Atheron does both ends and keeps the person: begin() in your sign-in route, complete() in your callback, user() anywhere after and signOut() on a POST from your own site. The SDK's plain PHP sample app (samples/php) is one file.
# Swap the code from the redirect for tokens, on your server. HTTP Basic carries your client ID and
# secret; the verifier proves this is the browser that started the sign-in.
curl https://accounts.aton-network.org/oauth/token \
-u "$ATHERON_CLIENT_ID:$ATHERON_CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code="$CODE" \
-d redirect_uri=https://app.example.com/callback \
-d code_verifier="$VERIFIER"{
"access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCJ9...",
"token_type": "Bearer",
"expires_in": 900,
"scope": "openid profile email offline_access",
"id_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6IjIwMjYtMTAtMDEiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmF0b24tbmV0d29yay5vcmciLCJzdWIiOiI5YTZk...",
"refresh_token": "ath_rt_6Qm9..."
}Read who it is now
# Who the person is as of now, with an access token issued to your client.
curl https://accounts.aton-network.org/oauth/userinfo \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"sub": "9a6d4c1e-1f0b-4a51-8a8e-3b2f6c0d7e11",
"name": "Ada Lovelace",
"email": "ada@example.com",
"email_verified": true,
"age": { "verified": true, "adult": true, "sixteen_or_over": true }
}Keep them signed in
# A new access token and a new refresh token for the newest refresh token you hold. The old one stops working.
curl https://accounts.aton-network.org/oauth/token \
-u "$ATHERON_CLIENT_ID:$ATHERON_CLIENT_SECRET" \
-d grant_type=refresh_token \
-d refresh_token="$REFRESH_TOKEN"{
"access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCJ9...",
"token_type": "Bearer",
"expires_in": 900,
"scope": "openid profile email offline_access",
"refresh_token": "ath_rt_k2Lp..."
}Check a webhook
Pass the raw body, exactly as it arrived: file_get_contents('php://input'), or $request->getContent() in Laravel and Symfony.
// Check that a webhook came from Atheron before you act on it. Pass the raw body, exactly as received.
import { verifyWebhook, type WebhookEvent } from "@aton-network/sdk";
export async function handleWebhook(request: Request): Promise<Response> {
const raw = await request.text();
const ok = await verifyWebhook(request.headers.get("atheron-signature"), raw, process.env.ATHERON_WEBHOOK_SECRET!);
if (!ok) return new Response("bad signature", { status: 400 });
const event = JSON.parse(raw) as WebhookEvent;
if (event.type === "consent.revoked") {
// Delete the refresh token you hold for event.data.sub, and stop using what you read.
}
return new Response(null, { status: 204 });
}Follow the events stream
Events::follow is a generator. It reconnects when the connection drops and resumes after the last event it handed you; it stops only on a refusal that waiting cannot fix, such as a key without events:read. See the events stream.
# Follow the events stream. -N keeps curl from buffering; pass the last id you handled to resume.
curl -N https://developers.aton-network.org/api/v1/events \
-H "Authorization: Bearer $ATHERON_API_KEY" \
-H "Accept: text/event-stream" \
-H "Last-Event-ID: $LAST_EVENT_ID"retry: 3000
event: ready
data: {"types":null}
id: 5b1c2a8e-0000-4000-8000-000000000001
event: user.signed_in
data: {"id":"5b1c2a8e-0000-4000-8000-000000000001","type":"user.signed_in","created":"2026-10-15T17:02:11.000Z","client_id":"live-1a2b3c4d5e6f7a8b","data":{"sub":"9a6d4c1e-1f0b-4a51-8a8e-3b2f6c0d7e11","scopes":["openid","email"],"at":"2026-10-15T17:02:11.000Z"}}
: beatGraphQL
AtonNetwork\GraphQL runs queries on PHP's own streams: query(GraphQL::USAGE_QUERY, ['days' => 30]). The documents the SDK ships (the App, Usage, Overview and Events documents the GraphQL reference publishes) go as their SHA-256 alone, so the server knows them from the first request; your own go in full unless you turn persisted queries on. query() throws GraphQLFieldErrors when any field failed, with the data that could be read on it; request() answers the data and the field errors side by side. Use Events::follow for events as they happen, or a WebSocket library of your own with wsCloseAction, wsErrorAction and resumeVariables.
// Your app and its usage in one GraphQL request, then its events as they happen over the subscription.
// Node 22 has a global WebSocket; on Node 20, pass { WebSocket } from the ws package.
import { AtheronGraphQL, UsageQuery } from "@aton-network/sdk";
export async function usageThisMonth(apiKey: string): Promise<number> {
const { usage } = await new AtheronGraphQL({ apiKey }).query(UsageQuery, { days: 30 });
return usage ? usage.usage.reduce((sum, day) => sum + day.calls, 0) : 0;
}
export async function watchConsent(apiKey: string, lastId: string | null, forget: (sub: string | undefined) => void, signal?: AbortSignal): Promise<string | null> {
for await (const event of new AtheronGraphQL({ apiKey }).events({ types: ["consent.revoked"], lastEventId: lastId, ...(signal ? { signal } : {}) })) {
forget(event.data.sub);
lastId = event.id; // keep it somewhere durable, to pass back after a restart
}
return lastId;
}The developer API
# Your app as registered, for the environment this API key belongs to. Needs app:read.
curl https://developers.aton-network.org/api/v1/app \
-H "Authorization: Bearer $ATHERON_API_KEY"{
"name": "Price Check",
"state": "live",
"kind": "confidential",
"quota": "free",
"env": "live",
"client_id": "live-1a2b3c4d5e6f7a8b",
"redirect_uris": ["https://app.example.com/callback"],
"scopes": ["openid", "profile", "email"],
"approved_scopes": ["openid", "profile", "email"],
"privacy_url": "https://app.example.com/privacy",
"homepage_url": "https://app.example.com",
"credentials": 2,
"testers": 0,
"webhooks": 1
}Errors
Every failure is thrown as AtonNetwork\AtheronError with $e->error (for example invalid_grant), $e->description, the HTTP $e->status and, on a 429, $e->retryAfter. See Errors for every code.
Frameworks
Laravel Socialite, Symfony and the WordPress plugin.
Demandez à Atlas
Assistant IAPosez n'importe quelle question sur Se connecter avec Atheron, l'API ou le portail. La réponse vient de notre documentation, avec les liens des pages utilisées.