Sign in with Dazr Identity: dokumentacja dla deweloperów

Użytkownicy mogą już korzystać z konta Google lub Microsoft w Dazr Identity, więc Twoja aplikacja nie potrzebuje obok osobnych przycisków Google i Microsoft: jeden przycisk zamiast trzech.

Discovery i endpointy

Wszystko jest opisane w dokumencie discovery. Wskaż bibliotece wystawcę, a resztę znajdzie sama.

CoAdres
Wystawcahttps://identity.dazr.eu
Discoveryhttps://identity.dazr.eu/.well-known/openid-configuration
Autoryzacjahttps://identity.dazr.eu/oauth/authorize
Tokenhttps://identity.dazr.eu/oauth/token
Informacje o użytkownikuhttps://identity.dazr.eu/oauth/userinfo
Klucze publiczne (JWKS)https://identity.dazr.eu/oauth/jwks
Unieważnianie (RFC 7009)https://identity.dazr.eu/oauth/revoke
Introspekcja (RFC 7662), tylko aplikacje serwerowehttps://identity.dazr.eu/oauth/introspect
Wylogowanie (RP-initiated logout)https://identity.dazr.eu/oauth/logout

Rejestracja aplikacji

Instrukcje integracji

Każdy stack poniżej korzysta z własnej standardowej obsługi OpenID Connect. Potrzebujesz trzech wartości z konsoli: identyfikatora klienta, sekretu klienta (tylko aplikacje serwerowe) i zarejestrowanego URI przekierowania. Resztę zawiera dokument discovery.

Auth.js / NextAuth

Auth.js (NextAuth.js v5) przyjmuje własny obiekt dostawcy OpenID Connect. Zarejestruj aplikację serwerową, ustaw AUTH_SECRET, AUTH_DAZR_ID i AUTH_DAZR_SECRET i użyj tego adresu callback jako URI przekierowania: 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 działa tak samo, z type: "oauth" i adresem discovery:

// 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

Mając plik auth.ts powyżej, dodaj route handler i przycisk logowania, który uruchamia server action.

// 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>
  )
}

Nadaj przyciskowi styl pokazany w sekcji Przycisk. W callbackach sub z Dazr to account.providerAccountId.

WordPress

Użyj ogólnej wtyczki klienta OpenID Connect, np. OpenID Connect Generic Client. Zarejestruj aplikację serwerową i wypełnij ustawienia wtyczki wartościami poniżej. Wtyczka pokazuje URI przekierowania do zarejestrowania na swojej stronie ustawień; domyślnie jest to https://example.com/wp-admin/admin-ajax.php?action=openid-connect-authorize.

UstawienieWartość
Identyfikator klientaTwój identyfikator klienta
Sekret klientaTwój sekret klienta
Scopeopenid profile email
Endpoint logowania (autoryzacji)https://identity.dazr.eu/oauth/authorize
Endpoint tokenówhttps://identity.dazr.eu/oauth/token
Endpoint userinfohttps://identity.dazr.eu/oauth/userinfo
Endpoint zakończenia sesjihttps://identity.dazr.eu/oauth/logout
Identity key (klucz tożsamości)sub
PKCEwłączone (S256)

Nazwy ustawień różnią się nieco między wtyczkami. Jeśli wtyczka nie ma opcji PKCE, wybierz inną: Dazr Identity odrzuca logowania bez PKCE.

Laravel

Laravel Socialite nie ma wbudowanego ogólnego sterownika OpenID Connect, więc dodaj mały własny sterownik. Kieruje on ludzi do Dazr z PKCE i odczytuje użytkownika z endpointu userinfo. Socialite nie czyta dokumentu discovery, dlatego endpointy są wpisane wprost. Zarejestruj aplikację serwerową z URI przekierowania 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 nie ma ogólnego dostawcy OpenID Connect, którego można skierować na dowolnego wystawcę: dostawcy logowania i integracje uwierzytelniania zewnętrznego to stałe listy. Dlatego dziś nie dodasz Dazr Identity w panelu Supabase. Jeśli Supabase doda ogólną obsługę OpenID Connect, użyj wartości z listy poniżej.

Alternatywa: loguj ludzi przez Dazr Identity na własnym serwerze (według jednej z instrukcji na tej stronie), zapisz sub z Dazr w tabeli użytkowników i komunikuj się z Supabase z tego serwera za pomocą klucza service role, sprawdzając dostęp we własnym kodzie. Nigdy nie wysyłaj klucza service role do przeglądarki.

Django

Użyj mozilla-django-oidc. Zarejestruj aplikację serwerową z URI przekierowania https://app.example.eu/oidc/callback/. Biblioteka nie czyta dokumentu discovery, dlatego endpointy są wpisane wprost.

# 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>

Domyślnie backend dopasowuje użytkowników po adresie e-mail. Aby łączyć konta przez sub, utwórz podklasę OIDCAuthenticationBackend i nadpisz filter_users_by_claims oraz create_user. Używaj aktualnej wersji: starsze wersje nie weryfikują podpisów ES256.

Node.js (openid-client)

openid-client (wersja 6) czyta dokument discovery i sprawdza za Ciebie PKCE, state, nonce oraz token ID.

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 };
}

Dowolna biblioteka OpenID Connect

Jeśli Twojego stacku nie ma na liście, zadziała dowolny klient OpenID Connect. Sprawdź te ustawienia:

Przepływ

  1. Utwórz losowy code_verifier (od 43 do 128 znaków) i jego code_challenge = base64url(SHA-256(verifier)). Utwórz losowe state i nonce. Zachowaj wszystkie trzy w sesji użytkownika.
  2. Przekieruj do endpointu autoryzacji z response_type=code, client_id, redirect_uri, scope (zawsze z openid), state, nonce, code_challenge i code_challenge_method=S256.
  3. Osoba w razie potrzeby się loguje i widzi ekran zgody z prawdziwymi danymi. Gdy wyrazi zgodę, wraca na Twój URI przekierowania z code, state i iss. Sprawdź, czy state się zgadza, a iss to https://identity.dazr.eu.
  4. Na swoim serwerze wyślij POST do endpointu tokenów z grant_type=authorization_code, code, tym samym redirect_uri i code_verifier.
  5. Zweryfikuj ID token: podpis ES256 kluczem z JWKS (według kid), iss, aud = Twój identyfikator klienta, exp i Twój nonce. Używaj sub jako klucza konta.

Parametr prompt przyjmuje none (natychmiastowa odpowiedź login_required lub consent_required, jeśli osoba musiałaby coś zrobić), login (poproś o ponowne zalogowanie) i consent (pokaż ekran zgody, nawet jeśli wcześniej ją wyrażono). Obsługiwany jest max_age. Zapamiętana zgoda: jeśli osoba wcześniej zatwierdziła te same scope’y, od razu wraca do Twojej aplikacji. Jeśli później poprosisz o więcej, ekran zgody pokaże tylko nowe pozycje.

Scope’y i claimy

Proś tylko o to, czego potrzebujesz. Poza tą tabelą nic nie istnieje: żadnych kontaktów, plików ani haseł.

ScopeClaimy
openid (wymagany)sub: losowy identyfikator osoby, taki sam dla każdej aplikacji Twojej organizacji i inny dla każdej innej organizacji
profilename (jeśli ustawione) i locale
emailemail i email_verified (zawsze true)
addressaddress (formatted, street_address, locality, region, postal_code, country), tylko jeśli osoba zapisała adres
organisationsorganisations: organizacje zaznaczone przez osobę na ekranie zgody, każda z id, name, country, register_number, vat, vat_verified (true tylko wtedy, gdy unijna usługa VIES potwierdziła numer VAT), verified, verification (zob. Zweryfikowane organizacje) i role
offline_accessbrak claimów; dodatkowo otrzymujesz refresh token

Claimy trafiają do ID tokenu dla scope’ów zatwierdzonych przez osobę. Endpoint informacji o użytkowniku zwraca ten sam zestaw z aktualnymi wartościami.

Zweryfikowane organizacje

Dazr weryfikuje organizację, gdy członek zarządu lub inna osoba upoważniona do podpisu podpisze krótkie oświadczenie kwalifikowanym podpisem elektronicznym (QES). To działa od razu, w każdym kraju UE. Bez podpisu kwalifikowanego reprezentant może zamiast tego przesłać odpis z rejestru i dokument tożsamości; Dazr sprawdza je ręcznie. Każda organizacja w claimie organisations zawiera swoją aktualną weryfikację:

verification.statusZnaczenie
unverifiedNiezweryfikowana albo weryfikacja się nie powiodła, wygasła lub została cofnięta (na przykład po zmianie nazwy lub numeru rejestrowego).
pendingWeryfikacja trwa: reprezentanta poproszono o podpis albo Dazr sprawdza dokumenty.
verifiedZweryfikowana. method to qes, qes_org (sam certyfikat wskazuje organizację, przez numer VAT lub rejestrowy), documents lub extract, a verified_at to czas uniksowy.

Wartość logiczna verified zostaje dla zgodności i równa się verification.status === 'verified'. Każda organizacja ma też id, to samo co w webhookach.

"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"
}]

Wymaganie zweryfikowanej organizacji

W konsoli ustaw Wymagaj zweryfikowanej organizacji na Weryfikacja rozpoczęta lub Zweryfikowana albo żądaj tego w pojedynczym żądaniu przez acr_values=urn:dazr:org:verification-started lub acr_values=urn:dazr:org:verified (obowiązuje surowsza z dwóch opcji). Twoja aplikacja musi też prosić o scope organisations.

Osoba bez odpowiedniej organizacji przechodzi przez to w tym samym logowaniu: imię i nazwisko, organizacja (z numerem VAT sprawdzonym w VIES), a potem weryfikacja. Może podpisać od razu, wysłać sobie oświadczenie e-mailem albo wskazać inną osobę, która podpisze. Następnie zaznacza organizację na ekranie zgody (można zaznaczyć tylko odpowiednie organizacje) i wraca na Twój URI przekierowania.

Jeśli ktoś inny musi jeszcze podpisać, osoba wraca ze statusem pending, także gdy żądasz Zweryfikowana: otrzymujesz kod, claim mówi pending, a acr tokenu ID to urn:dazr:org:verification-started zamiast urn:dazr:org:verified. Zawsze decyduj na podstawie verification.status, nigdy na podstawie udanego logowania. Z prompt=none otrzymasz interaction_required, gdy wymaganie nie jest spełnione.

Zmiany ustawienia obowiązują od następnego logowania. Zalogowani użytkownicy nie są wylogowywani: odpowiedzi refresh, endpoint userinfo i webhooki zawsze zawierają aktualny status, więc używaj ich do reagowania w istniejących sesjach.

Webhooki

Dodaj w konsoli adres URL webhooka (https). Sekret podpisu widzisz raz; możesz go zmienić. Dazr wysyła organisation.verification.updated, gdy organizacja udostępniona Twojej aplikacji przez jednego z użytkowników zmieni status na pending, verified, failed lub unverified. Aplikacje, które nigdy nie otrzymały organizacji przez zgodę, nigdy się o niej nie dowiedzą.

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 to pending, verified, failed lub unverified. subjects to wartości sub, w Twojej aplikacji, osób, które jej udostępniły tę organizację. Sprawdź podpis, zanim zaufasz treści:

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]));
}

Odpowiedz kodem 2xx w ciągu 8 sekund. W przeciwnym razie Dazr ponawia próbę po około 5 minutach, 30 minutach, 2, 6, 12 i 24 godzinach, a potem oznacza dostarczenie jako nieudane. Każda próba ma ten sam identyfikator Dazr-Delivery, więc ignoruj duplikaty. Konsola pokazuje ostatnie dostarczenia, może wysłać jedno ponownie i wysłać zdarzenie testowe (webhook.test). Odpytywanie endpointu userinfo też działa.

Tokeny i ich ważność

TokenWażność i zasady
Kod autoryzacji60 sekund, jednorazowy, powiązany z Twoim klientem, URI przekierowania i challenge’em PKCE. Dwukrotne użycie kodu unieważnia wydane nim tokeny.
Access token10 minut. Podpisany JWT (ES256, typ at+jwt) z aud = Twój identyfikator klienta. Wysyłaj go jako Authorization: Bearer.
ID token10 minut. ES256, z iss, sub, aud, exp, iat, auth_time, nonce i amr, jeśli jest znane.
Refresh tokenTylko z offline_access. Zmienia się przy każdym użyciu: zawsze zapisuj nowy. Ponowne wysłanie starego refresh tokenu unieważnia całe logowanie. Wygasa po 30 dniach nieużywania i najpóźniej po 180 dniach.

Klucze podpisu są wymieniane. Zawsze wybieraj klucz według kid z JWKS i pobierz JWKS ponownie, gdy zobaczysz nieznany kid.

Błędy

Dopóki identyfikator klienta i URI przekierowania są prawidłowe, błędy wracają na Twój URI przekierowania jako error, error_description, state i iss: na przykład invalid_request (np. brak PKCE lub plain), invalid_scope, unsupported_response_type, access_denied (osoba anulowała albo aplikacja jest w trybie testowym, a osoba nie jest członkiem), login_required, consent_required i interaction_required (z prompt=none, gdy brakuje wymaganej zweryfikowanej organizacji). Przy nieznanym kliencie lub niezarejestrowanym URI przekierowania Dazr pokazuje stronę błędu i nigdy nie przekierowuje. Endpoint tokenów odpowiada błędami JSON, takimi jak invalid_client, invalid_grant i unsupported_grant_type.

Wylogowanie

Aby kogoś wylogować, skieruj tę osobę do endpointu wylogowania z id_token_hint (lub client_id), opcjonalnym post_logout_redirect_uri zarejestrowanym w ustawieniach aplikacji i state. Dazr zapyta, czy chce też wylogować się z Dazr Identity na tym urządzeniu, a potem odeśle ją z powrotem. Niepotrzebne refresh tokeny unieważniaj w endpoincie unieważniania.

Testy i uruchomienie

Nowa aplikacja jest w trybie testowym: logować się mogą tylko członkowie Twojej organizacji, a ekran zgody o tym informuje. Aby ją uruchomić, zweryfikuj organizację w Dazr Identity, dodaj URI przekierowania https:// i poproś w konsoli o weryfikację. Zmiana nazwy, celu, polityki prywatności lub danych działającej aplikacji wymaga nowej weryfikacji.

Przycisk

Używaj tekstu „Sign in with Dazr Identity” i logo Dazr jak poniżej. Możesz zmieniać rozmiar, ale nie logo, kolory ani tekst. Podlinkuj go do własnej ścieżki, która rozpoczyna przepływ.

<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>

Minimalny przykład w Node.js

Node 18 lub nowszy, bez zależności. Aplikacja serwerowa z sekretem klienta; w przypadku klienta publicznego usuń nagłówek Authorization i wyślij client_id w treści.

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;
}

Limity

Endpointy autoryzacji i tokenów mają limity zapytań. Jeśli dostaniesz HTTP 429, odczekaj i spróbuj ponownie; nie ponawiaj w ciasnej pętli.

Twoje obowiązki

Twoja organizacja jest niezależnym administratorem otrzymanych danych. Warunki dla deweloperów Dazr Identity określają, co możesz z nimi robić, jak je chronić i jak zgłaszać naruszenia. Pytania: hello@dazr.eu.