C# and .NET SDK
Install and use AtonNetwork.Sdk.
Melden Sie sich an, um Ihre eigene Test-Client-ID in den Beispielen zu sehen.
Coming soon. AtonNetwork.Sdk is not published on NuGet 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.
AtonNetwork.Sdk is Sign in with Atheron for .NET: PKCE, the authorize address, the code swap, userinfo, webhook signatures, the events stream and the developer API client, on .NET 8 alone. AtonNetwork.AspNetCore adds ASP.NET Core. Both ship to NuGet on 30 October 2026.
Install
dotnet add package AtonNetwork.Sdk # the core
dotnet add package AtonNetwork.AspNetCore # ASP.NET CoreSign somebody in
Build the address on your server and keep started.Kept (the verifier, state and nonce) in their session. In ASP.NET Core, the integration does this for you.
# 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
FinishSignInAsync checks state in constant time, then swaps the code. Send the secret from a server app; leave it out in a public app.
# 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
// 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
AtheronEvents.Follow is an IAsyncEnumerable: it reconnects when the connection drops and resumes after the last event it handed you; it stops on a refusal that waiting cannot fix, such as a key without events:read, when you break out of the loop, or when your token is cancelled. 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
AtheronGraphQL runs typed queries: QueryAsync<UsageQueryData>(AtheronGraphQL.UsageQuery, new { 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. QueryAsync throws an AtheronGraphQLException when any field failed, with the data that could be read on it; RequestAsync answers the data and the field errors side by side. Use AtheronEvents.Follow for events as they happen, or ClientWebSocket 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 an AtheronException with Code (for example invalid_grant), Description, the HTTP Status and, on a 429, RetryAfter. See Errors for every code.
Reference
| Item | What it does |
|---|---|
Oidc.StartSignIn, Oidc.ParseCallback, Oidc.IdTokenClaims, Oidc.SafeReturnTo | The sign-in pieces |
AtheronClient: FinishSignInAsync, ExchangeCodeAsync, RefreshTokensAsync, RevokeTokenAsync, UserinfoAsync | The token endpoint and userinfo; the secret only ever as HTTP Basic |
Webhooks.Verify, Webhooks.ConstructEvent | Webhook signatures |
AtheronEvents.Follow(key, options, cancel) | The events stream, with resume |
new AtheronApi(key): AppAsync(), UsageAsync(days) | The developer API, generated from the OpenAPI description |
AtheronHttp.Client | Route every call through your own HttpClient, or a fake in tests |
Sample app
samples/aspnet-core in the SDK's repository, dotnet run. Its Atheron files are the examples on these pages.
Fragen Sie Atlas
KI-AssistentFragen Sie alles zu Mit Atheron anmelden, zur API oder zum Portal. Die Antwort kommt aus unserer Dokumentation, mit Links zu den genutzten Seiten.