Sign in with Dazr Identity: documentatie voor ontwikkelaars
Standaard OpenID Connect 1.0 met de authorization code flow en PKCE. Geen SDK nodig: elke gecertificeerde OpenID Connect-bibliotheek werkt, en gewone code ook.
Mensen kunnen in Dazr Identity al hun Google- of Microsoft-account gebruiken, dus je app heeft er geen aparte Google- en Microsoft-knoppen naast nodig: één knop in plaats van drie.
Discovery en endpoints
Alles staat in het discovery-document. Wijs je bibliotheek naar de issuer en ze vindt de rest.
| Wat | Adres |
|---|---|
| Issuer | https://identity.dazr.eu |
| Discovery | https://identity.dazr.eu/.well-known/openid-configuration |
| Autorisatie | https://identity.dazr.eu/oauth/authorize |
| Token | https://identity.dazr.eu/oauth/token |
| Gebruikersinfo | https://identity.dazr.eu/oauth/userinfo |
| Publieke sleutels (JWKS) | https://identity.dazr.eu/oauth/jwks |
| Intrekking (RFC 7009) | https://identity.dazr.eu/oauth/revoke |
| Introspectie (RFC 7662), alleen voor webserver-apps | https://identity.dazr.eu/oauth/introspect |
| Uitloggen (RP-initiated logout) | https://identity.dazr.eu/oauth/logout |
Een app registreren
- Apps horen bij een organisatie. Eigenaren en beheerders van de organisatie registreren ze onder Ontwikkelaars.
- Webserver-apps (confidential clients) krijgen een clientgeheim dat één keer wordt getoond. Authenticeer bij het token-endpoint met HTTP Basic (
client_secret_basic) of in de formulierbody (client_secret_post). - Single-page- en mobiele apps (public clients) krijgen geen geheim. Ze sturen
client_idmee en vertrouwen op PKCE. - Redirect-URI’s moeten exact overeenkomen en met
https://beginnen. Zolang de app in de testfase is, werken ookhttp://localhostenhttp://127.0.0.1op elke poort. - Een adres van je privacyverklaring, een support-e-mail en een doel van één regel zijn verplicht: mensen zien ze op het toestemmingsscherm.
Integratiehandleidingen
Elke stack hieronder gebruikt zijn eigen standaardondersteuning voor OpenID Connect. Je hebt drie waarden uit de console nodig: de client-ID, het client secret (alleen voor webserver-apps) en de redirect-URI die je hebt geregistreerd. De rest komt uit het discovery-document.
- Auth.js / NextAuth
- Next.js App Router
- WordPress
- Laravel
- Supabase
- Django
- Node.js (openid-client)
- Elke OpenID Connect-bibliotheek
sub is pairwise: hetzelfde voor elke app van je organisatie, anders voor elke andere organisatie. Hij verandert nooit voor een persoon, een e-mailadres wel. Sla sub op als sleutel van het account en behandel email als contactgegeven.
Auth.js / NextAuth
Auth.js (NextAuth.js v5) accepteert een eigen OpenID Connect-providerobject. Registreer een webserver-app, stel AUTH_SECRET, AUTH_DAZR_ID en AUTH_DAZR_SECRET in en gebruik deze callback-URL als redirect-URI: https://app.example.eu/api/auth/callback/dazr.
// auth.ts
import NextAuth from "next-auth"
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
{
id: "dazr",
name: "Dazr Identity",
type: "oidc",
issuer: "https://identity.dazr.eu",
clientId: process.env.AUTH_DAZR_ID,
clientSecret: process.env.AUTH_DAZR_SECRET,
authorization: { params: { scope: "openid profile email" } },
checks: ["pkce", "state", "nonce"],
client: { id_token_signed_response_alg: "ES256" },
},
],
})
NextAuth.js v4 werkt op dezelfde manier, met type: "oauth" en de discovery-URL:
// pages/api/auth/[...nextauth].ts (NextAuth.js v4), in providers: [ ... ]
{
id: "dazr",
name: "Dazr Identity",
type: "oauth",
wellKnown: "https://identity.dazr.eu/.well-known/openid-configuration",
clientId: process.env.DAZR_CLIENT_ID,
clientSecret: process.env.DAZR_CLIENT_SECRET,
authorization: { params: { scope: "openid profile email" } },
idToken: true,
checks: ["pkce", "state"],
client: { id_token_signed_response_alg: "ES256" },
profile(profile) {
return { id: profile.sub, name: profile.name ?? null, email: profile.email }
},
}
Next.js App Router
Voeg met het bestand auth.ts hierboven de route handler toe en een inlogknop die een server action uitvoert.
// app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth"
export const { GET, POST } = handlers
// app/page.tsx
import { auth, signIn, signOut } from "@/auth"
export default async function Page() {
const session = await auth()
if (session?.user) {
return (
<form action={async () => { "use server"; await signOut() }}>
<p>Signed in as {session.user.email}</p>
<button type="submit">Sign out</button>
</form>
)
}
return (
<form action={async () => { "use server"; await signIn("dazr") }}>
<button type="submit" className="dazr-signin">Sign in with Dazr Identity</button>
</form>
)
}
Geef de knop de stijl uit De knop. In callbacks is de Dazr-sub gelijk aan account.providerAccountId.
WordPress
Gebruik een generieke OpenID Connect-clientplugin, zoals OpenID Connect Generic Client. Registreer een webserver-app en vul de plugininstellingen in met de waarden hieronder. De plugin toont de te registreren redirect-URI op zijn instellingenpagina; standaard is dat https://example.com/wp-admin/admin-ajax.php?action=openid-connect-authorize.
| Instelling | Waarde |
|---|---|
| Client-ID | je client-ID |
| Client secret | je client secret |
| Scope | openid profile email |
| Login-endpoint (autorisatie) | https://identity.dazr.eu/oauth/authorize |
| Token-endpoint | https://identity.dazr.eu/oauth/token |
| Userinfo-endpoint | https://identity.dazr.eu/oauth/userinfo |
| Uitlog-endpoint (end session) | https://identity.dazr.eu/oauth/logout |
| Identity key (identiteitssleutel) | sub |
| PKCE | aan (S256) |
De namen van de instellingen verschillen per plugin. Heeft een plugin geen PKCE-optie, kies dan een andere: Dazr Identity weigert inlogpogingen zonder PKCE.
Laravel
Laravel Socialite heeft geen ingebouwde generieke OpenID Connect-driver, dus voeg je een kleine eigen driver toe. Die stuurt mensen met PKCE naar Dazr en leest de gebruiker uit het userinfo-endpoint. Socialite leest het discovery-document niet, daarom staan de endpoints uitgeschreven. Registreer een webserver-app met de redirect-URI https://app.example.eu/auth/dazr/callback.
<?php
// app/Socialite/DazrProvider.php
namespace App\Socialite;
use Laravel\Socialite\Two\AbstractProvider;
use Laravel\Socialite\Two\User;
class DazrProvider extends AbstractProvider
{
protected $scopes = ['openid', 'profile', 'email'];
protected $scopeSeparator = ' ';
protected $usesPKCE = true;
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase('https://identity.dazr.eu/oauth/authorize', $state);
}
protected function getTokenUrl()
{
return 'https://identity.dazr.eu/oauth/token';
}
protected function getUserByToken($token)
{
$response = $this->getHttpClient()->get('https://identity.dazr.eu/oauth/userinfo', [
'headers' => ['Authorization' => 'Bearer '.$token],
]);
return json_decode((string) $response->getBody(), true);
}
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
'id' => $user['sub'],
'name' => $user['name'] ?? null,
'email' => $user['email'] ?? null,
]);
}
}
// config/services.php
'dazr' => [
'client_id' => env('DAZR_CLIENT_ID'),
'client_secret' => env('DAZR_CLIENT_SECRET'),
'redirect' => env('DAZR_REDIRECT_URI'),
],
// app/Providers/AppServiceProvider.php, in boot()
use Laravel\Socialite\Facades\Socialite;
use App\Socialite\DazrProvider;
Socialite::extend('dazr', function ($app) {
return Socialite::buildProvider(DazrProvider::class, config('services.dazr'));
});
// routes/web.php
Route::get('/auth/dazr', fn () => Socialite::driver('dazr')->redirect());
Route::get('/auth/dazr/callback', function () {
$dazr = Socialite::driver('dazr')->user();
$user = \App\Models\User::updateOrCreate(
['dazr_sub' => $dazr->getId()],
['name' => $dazr->getName() ?? $dazr->getEmail(), 'email' => $dazr->getEmail()],
);
Auth::login($user);
return redirect('/');
});
Supabase
Supabase Auth heeft geen generieke OpenID Connect-provider die je op een willekeurige issuer kunt richten: de inlogproviders en de third-party-auth-integraties zijn vaste lijsten. Je kunt Dazr Identity dus nu niet toevoegen in het Supabase-dashboard. Voegt Supabase generieke OpenID Connect-ondersteuning toe, gebruik dan de waarden uit de checklist hieronder.
Het alternatief: laat mensen inloggen met Dazr Identity op je eigen server (met een van de handleidingen op deze pagina), sla de Dazr-sub op in je gebruikerstabel en praat vanaf die server met Supabase met de service role key, waarbij je de toegang in je eigen code controleert. Stuur de service role key nooit naar een browser.
Django
Gebruik mozilla-django-oidc. Registreer een webserver-app met de redirect-URI https://app.example.eu/oidc/callback/. De bibliotheek leest het discovery-document niet, daarom staan de endpoints uitgeschreven.
# settings.py
import os
INSTALLED_APPS += ["mozilla_django_oidc"] # after django.contrib.auth
AUTHENTICATION_BACKENDS = [
"mozilla_django_oidc.auth.OIDCAuthenticationBackend",
"django.contrib.auth.backends.ModelBackend",
]
OIDC_RP_CLIENT_ID = os.environ["DAZR_CLIENT_ID"]
OIDC_RP_CLIENT_SECRET = os.environ["DAZR_CLIENT_SECRET"]
OIDC_RP_SCOPES = "openid profile email"
OIDC_RP_SIGN_ALGO = "ES256"
OIDC_OP_JWKS_ENDPOINT = "https://identity.dazr.eu/oauth/jwks"
OIDC_OP_AUTHORIZATION_ENDPOINT = "https://identity.dazr.eu/oauth/authorize"
OIDC_OP_TOKEN_ENDPOINT = "https://identity.dazr.eu/oauth/token"
OIDC_OP_USER_ENDPOINT = "https://identity.dazr.eu/oauth/userinfo"
OIDC_USE_PKCE = True
OIDC_PKCE_CODE_CHALLENGE_METHOD = "S256"
LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/"
# urls.py
from django.urls import include, path
urlpatterns += [path("oidc/", include("mozilla_django_oidc.urls"))]
# template: start the sign-in
# <a href="{% url 'oidc_authentication_init' %}">Sign in with Dazr Identity</a>
Standaard koppelt de backend gebruikers op e-mailadres. Om accounts aan sub te koppelen, maak je een subklasse van OIDCAuthenticationBackend en overschrijf je filter_users_by_claims en create_user. Gebruik een actuele versie: oudere versies kunnen ES256-handtekeningen niet controleren.
Node.js (openid-client)
openid-client (versie 6) leest het discovery-document en controleert PKCE, state, nonce en het ID-token voor je.
import * as client from 'openid-client';
const config = await client.discovery(
new URL('https://identity.dazr.eu'),
process.env.DAZR_CLIENT_ID,
process.env.DAZR_CLIENT_SECRET, // public client: pass undefined here and client.None() as the 4th argument
);
const redirect_uri = 'https://app.example.eu/auth/dazr/callback';
// 1. Start: keep the three random values in the user's session.
export async function start(session) {
session.verifier = client.randomPKCECodeVerifier();
session.state = client.randomState();
session.nonce = client.randomNonce();
return client.buildAuthorizationUrl(config, {
redirect_uri,
scope: 'openid profile email',
code_challenge: await client.calculatePKCECodeChallenge(session.verifier),
code_challenge_method: 'S256',
state: session.state,
nonce: session.nonce,
});
}
// 2. Callback: pass the full callback URL (a URL object).
export async function callback(session, currentUrl) {
const tokens = await client.authorizationCodeGrant(config, currentUrl, {
pkceCodeVerifier: session.verifier,
expectedState: session.state,
expectedNonce: session.nonce,
});
const claims = tokens.claims(); // sub, email, name
return { sub: claims.sub, email: claims.email, name: claims.name };
}
Elke OpenID Connect-bibliotheek
Staat je stack er niet bij, dan werkt elke OpenID Connect-client. Controleer deze instellingen:
- Issuer
https://identity.dazr.eu, discovery-URLhttps://identity.dazr.eu/.well-known/openid-configuration. - Response type
code(authorization code flow). Geen implicit of hybrid flow. - PKCE is verplicht, met
code_challenge_method=S256. - Scopes:
openidplus alleen wat je nodig hebt uitprofile,email,address,organisationsenoffline_access. Je app moet geregistreerd zijn voor elke scope die hij vraagt. - Redirect-URI: precies zoals geregistreerd in de console, inclusief het pad en een eventuele querystring.
- Clientauthenticatie:
client_secret_basicofclient_secret_postvoor webserver-apps,nonevoor single-page- en mobiele apps. - Handtekening van het ID-token: ES256. Sommige bibliotheken gaan uit van RS256: stel het algoritme in op ES256.
- Gebruik
subals sleutel van het account. Hij is pairwise, per organisatie. - Met
offline_accesssla je na elke refresh het nieuwe refresh token op: ze roteren.
De flow
- Maak een willekeurige
code_verifier(43 tot 128 tekens) en de bijbehorendecode_challenge= base64url(SHA-256(verifier)). Maak een willekeurigestateennonce. Bewaar alle drie in de sessie van de gebruiker. - Stuur door naar het authorization-endpoint met
response_type=code,client_id,redirect_uri,scope(altijd metopenid),state,nonce,code_challengeencode_challenge_method=S256. - De persoon logt zo nodig in en ziet het toestemmingsscherm met de echte gegevens. Keurt hij of zij het goed, dan komt de persoon terug op je redirect-URI met
code,stateeniss. Controleer datstateklopt en datissgelijk is aanhttps://identity.dazr.eu. - Doe op je server een POST naar het token-endpoint met
grant_type=authorization_code,code, dezelfderedirect_urien decode_verifier. - Verifieer het ID-token: ES256-handtekening met een sleutel uit de JWKS (op basis van de
kid),iss,aud= je client-ID,expen jenonce. Gebruiksubals sleutel van het account.
De parameter prompt accepteert none (meteen antwoorden met login_required of consent_required als de persoon iets zou moeten doen), login (de persoon opnieuw laten inloggen) en consent (het toestemmingsscherm tonen, ook als er eerder toestemming was). max_age wordt ondersteund. Onthouden toestemming: heeft de persoon dezelfde scopes eerder goedgekeurd, dan gaat hij of zij meteen terug naar je app. Vraag je later om meer, dan toont het toestemmingsscherm alleen wat nieuw is.
Scopes en claims
Vraag alleen om wat je nodig hebt. Buiten deze tabel bestaat niets: geen contacten, geen bestanden, geen wachtwoorden.
| Scope | Claims |
|---|---|
openid (verplicht) | sub: een willekeurig ID voor de persoon, gelijk voor elke app van je organisatie en anders voor elke andere organisatie |
profile | name (indien ingevuld) en locale |
email | email en email_verified (altijd true) |
address | address (formatted, street_address, locality, region, postal_code, country), alleen als de persoon een adres heeft opgeslagen |
organisations | organisations: de organisaties die de persoon op het toestemmingsscherm aanvinkt, elk met id, name, country, register_number, vat, vat_verified (alleen true als de EU-dienst VIES het btw-nummer heeft bevestigd), verified, verification (zie Geverifieerde organisaties) en role |
offline_access | geen claims; je krijgt ook een refresh token |
Claims staan in het ID-token voor de scopes die de persoon heeft goedgekeurd. Het userinfo-endpoint geeft dezelfde set terug, met de actuele waarden.
Geverifieerde organisaties
Dazr verifieert een organisatie wanneer de bestuurder, of een andere tekenbevoegde, een korte verklaring ondertekent met een gekwalificeerde elektronische handtekening (QES). Dat gaat direct en werkt in elk EU-land. Zonder gekwalificeerde handtekening kan de vertegenwoordiger in plaats daarvan een uittreksel uit het handelsregister en een identiteitsbewijs sturen; Dazr beoordeelt die handmatig. Elke organisatie in de claim organisations bevat haar actuele verificatie:
| verification.status | Betekenis |
|---|---|
unverified | Niet geverifieerd, of een verificatie is mislukt, verlopen of ingetrokken (bijvoorbeeld nadat de statutaire naam of het registratienummer is gewijzigd). |
pending | De verificatie loopt: de vertegenwoordiger is gevraagd te ondertekenen, of Dazr beoordeelt documenten. |
verified | Geverifieerd. method is qes, qes_org (het certificaat noemt de organisatie zelf, via btw- of registratienummer), documents of extract, en verified_at is een Unix-tijd. |
De boolean verified blijft bestaan voor compatibiliteit en is gelijk aan verification.status === 'verified'. Elke organisatie heeft ook een id, dezelfde die webhooks gebruiken.
"organisations": [{
"id": "org_3k9x2m0q8w1v5t7a", "name": "Conti Logistica S.r.l.", "country": "IT",
"register_number": "REA MI1234567", "vat": "IT12345678903", "vat_verified": true,
"verified": false, "verification": { "status": "pending", "method": "qes" }, "role": "owner"
}]
Geverifieerde organisatie vereisen
Zet in de console Geverifieerde organisatie vereisen op Verificatie gestart of Geverifieerd, of vraag het per verzoek met acr_values=urn:dazr:org:verification-started of acr_values=urn:dazr:org:verified (de strengste van de twee geldt). Je app moet ook de scope organisations aanvragen.
Wie geen geschikte organisatie heeft, doorloopt dat in dezelfde inlog: de naam, de organisatie (met het btw-nummer gecontroleerd in VIES) en dan de verificatie. Diegene kan direct ondertekenen, de verklaring naar zichzelf laten mailen of iemand anders noemen die tekent. Daarna vinkt diegene de organisatie aan op het toestemmingsscherm (alleen geschikte organisaties zijn aan te vinken) en komt terug op je redirect-URI.
Moet iemand anders nog tekenen, dan komt de persoon terug met status pending, ook als je om Geverifieerd vroeg: je ontvangt een code, de claim zegt pending en de acr van het ID-token is urn:dazr:org:verification-started in plaats van urn:dazr:org:verified. Baseer toegang altijd op verification.status, nooit op het feit dat inloggen lukte. Met prompt=none krijg je interaction_required als niet aan de eis wordt voldaan.
Wijzigingen van de instelling gelden vanaf de volgende inlog. Wie al is ingelogd, wordt niet uitgelogd: refresh-antwoorden, het userinfo-endpoint en webhooks bevatten altijd de actuele status, dus gebruik die om te reageren op bestaande sessies.
Webhooks
Voeg in de console een webhook-URL (https) toe. Je ziet het ondertekensecret één keer; je kunt het vervangen. Dazr stuurt organisation.verification.updated wanneer een organisatie die een van je gebruikers met je app deelde pending, verified, failed of unverified wordt. Apps die een organisatie nooit via toestemming ontvingen, horen er nooit iets over.
POST /your/webhook
Content-Type: application/json
Dazr-Event: organisation.verification.updated
Dazr-Delivery: dlv_8Fq2…
Dazr-Signature: t=1791021600,v1=5e0c…
{ "id": "evt_…", "type": "organisation.verification.updated", "created": 1791021600,
"data": { "organisation": { "id": "org_3k9x2m0q8w1v5t7a", "name": "Conti Logistica S.r.l.", "country": "IT" },
"status": "verified", "method": "qes_org", "verified_at": 1791021598,
"subjects": ["Xo1c…"] } }
status is pending, verified, failed of unverified. subjects zijn de sub-waarden, bij jouw app, van de mensen die deze organisatie ermee deelden. Controleer de handtekening voordat je de inhoud vertrouwt:
import crypto from 'node:crypto';
// raw = the request body exactly as received (a string), header = the Dazr-Signature header
function verifyDazrSignature(raw, header, secret) {
const m = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(header || '');
if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false; // reject old deliveries
const want = crypto.createHmac('sha256', secret).update(m[1] + '.' + raw).digest('hex');
return crypto.timingSafeEqual(Buffer.from(want), Buffer.from(m[2]));
}
Antwoord binnen 8 seconden met een 2xx. Anders probeert Dazr het opnieuw na ongeveer 5 minuten, 30 minuten, 2, 6, 12 en 24 uur, en markeert de aflevering daarna als mislukt. Elke poging heeft hetzelfde Dazr-Delivery-ID, dus negeer dubbele. De console toont recente afleveringen, kan er een opnieuw sturen en kan een testevent sturen (webhook.test). Het userinfo-endpoint pollen werkt ook.
Tokens en geldigheid
| Token | Geldigheid en regels |
|---|---|
| Autorisatiecode | 60 seconden, eenmalig, gekoppeld aan je client, redirect-URI en PKCE-challenge. Een code die twee keer wordt gebruikt, trekt de tokens in die ermee zijn uitgegeven. |
| Access token | 10 minuten. Een ondertekende JWT (ES256, typ at+jwt) met aud = je client-ID. Stuur hem mee als Authorization: Bearer. |
| ID-token | 10 minuten. ES256, met iss, sub, aud, exp, iat, auth_time, nonce en amr waar bekend. |
| Refresh token | Alleen met offline_access. Wordt bij elk gebruik vervangen: sla altijd de nieuwe op. Wie een oude refresh token opnieuw stuurt, trekt de hele inlogsessie in. Verloopt na 30 dagen zonder gebruik en na hooguit 180 dagen. |
Ondertekeningssleutels worden vervangen. Kies de sleutel altijd op kid uit de JWKS en haal de JWKS opnieuw op als je een onbekende kid ziet.
Fouten
Zolang je client-ID en redirect-URI geldig zijn, komen fouten terug op je redirect-URI als error, error_description, state en iss: bijvoorbeeld invalid_request (zoals ontbrekende of plain PKCE), invalid_scope, unsupported_response_type, access_denied (de persoon heeft geannuleerd, of de app is in de testfase en de persoon is geen lid), login_required, consent_required en interaction_required (met prompt=none, als een vereiste geverifieerde organisatie ontbreekt). Bij een onbekende client of een niet-geregistreerde redirect-URI toont Dazr een foutpagina en stuurt nooit door. Het token-endpoint antwoordt met JSON-fouten zoals invalid_client, invalid_grant en unsupported_grant_type.
Uitloggen
Om iemand uit te loggen, stuur je de persoon naar het logout-endpoint met id_token_hint (of client_id), een optionele post_logout_redirect_uri die in je app-instellingen staat, en state. Dazr vraagt of de persoon ook op dat apparaat wil uitloggen bij Dazr Identity en stuurt hem of haar daarna terug. Trek refresh tokens die je niet meer nodig hebt in via het revocation-endpoint.
Testen en live gaan
Een nieuwe app staat in de testfase: alleen leden van je organisatie kunnen inloggen, en het toestemmingsscherm vermeldt dat. Om live te gaan, laat je je organisatie verifiëren in Dazr Identity, voeg je een https://-redirect-URI toe en vraag je in de console een beoordeling aan. Wijzig je de naam, het doel, de privacyverklaring of de gegevens van een live app, dan is een nieuwe beoordeling nodig.
De knop
Gebruik de tekst "Sign in with Dazr Identity" en het Dazr-logo zoals hieronder. Je mag de grootte aanpassen, niet het logo, de kleuren of de tekst. Link de knop naar je eigen route die de flow start.
<a href="/auth/dazr" style="display:inline-flex;align-items:center;gap:10px;height:44px;padding:0 18px;border-radius:22px;background:#18203a;color:#fff;font:600 15px/1 system-ui,sans-serif;text-decoration:none">
<svg width="20" height="20" viewBox="0 0 100 100" aria-hidden="true"><path d="M50 4 89 16v31c0 25.5-16.8 42.5-39 50C27.8 89.5 11 72.5 11 47V16z" fill="#fff"/><path d="M50 15.5 79 24.5v22.8c0 19.5-12.4 32.6-29 38.7C33.4 79.9 21 66.8 21 47.3V24.5z" fill="#18203a"/><path d="M50 24 71 30.6v16.9c0 14.5-9 24.4-21 29.2-12-4.8-21-14.7-21-29.2V30.6z" fill="#fff"/></svg>
Sign in with Dazr Identity
</a>
Een minimaal voorbeeld in Node.js
Node 18 of nieuwer, zonder afhankelijkheden. Een webserver-app met een clientgeheim; laat bij een public client de Authorization-header weg en stuur client_id mee in de body.
import crypto from 'node:crypto';
const issuer = 'https://identity.dazr.eu';
const clientId = process.env.DAZR_CLIENT_ID;
const clientSecret = process.env.DAZR_CLIENT_SECRET;
const redirectUri = 'https://app.example.eu/auth/dazr/callback';
const b64url = (buf) => Buffer.from(buf).toString('base64url');
// 1. Start: keep verifier, state and nonce in the user's session.
export function start(session) {
session.verifier = b64url(crypto.randomBytes(32));
session.state = b64url(crypto.randomBytes(16));
session.nonce = b64url(crypto.randomBytes(16));
const challenge = b64url(crypto.createHash('sha256').update(session.verifier).digest());
return issuer + '/oauth/authorize?' + new URLSearchParams({
response_type: 'code', client_id: clientId, redirect_uri: redirectUri,
scope: 'openid profile email', state: session.state, nonce: session.nonce,
code_challenge: challenge, code_challenge_method: 'S256',
});
}
// 2. Callback: check state, exchange the code, verify the ID token.
export async function callback(session, query) {
if (query.error) throw new Error('sign-in failed: ' + query.error);
if (query.state !== session.state || query.iss !== issuer) throw new Error('state or issuer mismatch');
const res = await fetch(issuer + '/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: 'Basic ' + Buffer.from(clientId + ':' + clientSecret).toString('base64'),
},
body: new URLSearchParams({ grant_type: 'authorization_code', code: query.code, redirect_uri: redirectUri, code_verifier: session.verifier }),
});
const tokens = await res.json();
if (!res.ok) throw new Error(tokens.error);
const claims = await verifyIdToken(tokens.id_token, session.nonce);
return { sub: claims.sub, email: claims.email, name: claims.name, tokens };
}
async function verifyIdToken(idToken, nonce) {
const [h, p, s] = idToken.split('.');
const header = JSON.parse(Buffer.from(h, 'base64url'));
const { keys } = await (await fetch(issuer + '/oauth/jwks')).json();
const jwk = keys.find((k) => k.kid === header.kid);
if (!jwk || header.alg !== 'ES256') throw new Error('unknown signing key');
const ok = crypto.verify('sha256', Buffer.from(h + '.' + p),
{ key: crypto.createPublicKey({ key: jwk, format: 'jwk' }), dsaEncoding: 'ieee-p1363' }, Buffer.from(s, 'base64url'));
const c = JSON.parse(Buffer.from(p, 'base64url'));
const now = Math.floor(Date.now() / 1000);
if (!ok || c.iss !== issuer || c.aud !== clientId || c.exp < now - 30 || c.nonce !== nonce) throw new Error('invalid ID token');
return c;
}
Limieten
Op de authorization- en token-endpoints zit een limiet. Krijg je HTTP 429, wacht dan en probeer het opnieuw; probeer het niet in een snelle lus.
Je verplichtingen
Je organisatie is zelfstandig verwerkingsverantwoordelijke voor de gegevens die ze ontvangt. De Ontwikkelaarsvoorwaarden Dazr Identity beschrijven wat je ermee mag doen, hoe je ze beveiligt en hoe je datalekken meldt. Vragen: hello@dazr.eu.