Sign in with Dazr Identity: documentación para desarrolladores
OpenID Connect 1.0 estándar con el flujo authorization code y PKCE. Sin SDK: funciona cualquier biblioteca OpenID Connect certificada, y también el código propio.
La gente ya puede usar su cuenta de Google o Microsoft dentro de Dazr Identity, así que tu app no necesita botones de Google y Microsoft aparte: un botón en lugar de tres.
Discovery y endpoints
Todo está descrito en el documento de discovery. Indica el emisor a tu biblioteca y encontrará el resto.
| Qué | Dirección |
|---|---|
| Emisor | https://identity.dazr.eu |
| Discovery | https://identity.dazr.eu/.well-known/openid-configuration |
| Autorización | https://identity.dazr.eu/oauth/authorize |
| Token | https://identity.dazr.eu/oauth/token |
| Información del usuario | https://identity.dazr.eu/oauth/userinfo |
| Claves públicas (JWKS) | https://identity.dazr.eu/oauth/jwks |
| Revocación (RFC 7009) | https://identity.dazr.eu/oauth/revoke |
| Introspección (RFC 7662), solo apps de servidor web | https://identity.dazr.eu/oauth/introspect |
| Cierre de sesión (RP-initiated logout) | https://identity.dazr.eu/oauth/logout |
Registrar una app
- Las apps pertenecen a una organización. Los propietarios y administradores de la organización las registran en Desarrolladores.
- Las apps de servidor web (clientes confidenciales) reciben un secreto de cliente, que se muestra una sola vez. Autentícate en el endpoint de tokens con HTTP Basic (
client_secret_basic) o en el cuerpo del formulario (client_secret_post). - Las apps de una sola página y móviles (clientes públicos) no reciben secreto. Envían
client_idy se apoyan en PKCE. - Las URI de redirección deben coincidir exactamente y empezar por
https://. Mientras la app está en pruebas, también funcionanhttp://localhostyhttp://127.0.0.1en cualquier puerto. - Son obligatorios la dirección de una política de privacidad, un correo de soporte y una finalidad de una línea: las personas los ven en la pantalla de consentimiento.
Guías de integración
Cada stack de abajo usa su propio soporte estándar de OpenID Connect. Necesitas tres valores de la consola: el client ID, el client secret (solo apps de servidor web) y la URI de redirección que registraste. El resto sale del documento de discovery.
- Auth.js / NextAuth
- Next.js App Router
- WordPress
- Laravel
- Supabase
- Django
- Node.js (openid-client)
- Cualquier biblioteca OpenID Connect
sub es pairwise: el mismo para todas las apps de tu organización y distinto para cualquier otra organización. Nunca cambia para una persona, mientras que un correo sí puede cambiar. Guarda sub como clave de la cuenta y trata email como dato de contacto.
Auth.js / NextAuth
Auth.js (NextAuth.js v5) acepta un objeto de proveedor OpenID Connect personalizado. Registra una app de servidor web, define AUTH_SECRET, AUTH_DAZR_ID y AUTH_DAZR_SECRET y usa esta URL de callback como URI de redirección: 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 funciona igual, con type: "oauth" y la URL de 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
Con el archivo auth.ts de arriba, añade el route handler y un botón de inicio de sesión que ejecuta una 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>
)
}
Da al botón el estilo de El botón. En los callbacks, el sub de Dazr es account.providerAccountId.
WordPress
Usa un plugin cliente OpenID Connect genérico, como OpenID Connect Generic Client. Registra una app de servidor web y rellena los ajustes del plugin con los valores de abajo. El plugin muestra en su página de ajustes la URI de redirección que debes registrar; por defecto es https://example.com/wp-admin/admin-ajax.php?action=openid-connect-authorize.
| Ajuste | Valor |
|---|---|
| Client ID | tu client ID |
| Client secret | tu client secret |
| Scope | openid profile email |
| Endpoint de inicio de sesión (autorización) | https://identity.dazr.eu/oauth/authorize |
| Endpoint de tokens | https://identity.dazr.eu/oauth/token |
| Endpoint de userinfo | https://identity.dazr.eu/oauth/userinfo |
| Endpoint de cierre de sesión | https://identity.dazr.eu/oauth/logout |
| Identity key (clave de identidad) | sub |
| PKCE | activado (S256) |
Los nombres de los ajustes cambian un poco según el plugin. Si un plugin no tiene opción PKCE, elige otro: Dazr Identity rechaza los inicios de sesión sin PKCE.
Laravel
Laravel Socialite no trae un driver OpenID Connect genérico, así que añade un pequeño driver propio. Envía a las personas a Dazr con PKCE y lee el usuario del endpoint de userinfo. Socialite no lee el documento de discovery, por eso los endpoints están escritos. Registra una app de servidor web con la URI de redirección 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 no tiene un proveedor OpenID Connect genérico que puedas apuntar a cualquier emisor: sus proveedores de inicio de sesión y sus integraciones de autenticación de terceros son listas fijas. Por eso hoy no puedes añadir Dazr Identity en el panel de Supabase. Si Supabase añade soporte genérico de OpenID Connect, usa los valores de la lista de abajo.
La alternativa: inicia la sesión de las personas con Dazr Identity en tu propio servidor (con una de las guías de esta página), guarda el sub de Dazr en tu tabla de usuarios y habla con Supabase desde ese servidor con la service role key, comprobando el acceso en tu propio código. Nunca envíes la service role key a un navegador.
Django
Usa mozilla-django-oidc. Registra una app de servidor web con la URI de redirección https://app.example.eu/oidc/callback/. La biblioteca no lee el documento de discovery, por eso los endpoints están escritos.
# 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>
Por defecto, el backend asocia a los usuarios por correo. Para vincular las cuentas a sub, crea una subclase de OIDCAuthenticationBackend y sobrescribe filter_users_by_claims y create_user. Usa una versión actual: las versiones antiguas no verifican firmas ES256.
Node.js (openid-client)
openid-client (versión 6) lee el documento de discovery y comprueba por ti PKCE, state, nonce y el ID token.
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 };
}
Cualquier biblioteca OpenID Connect
Si tu stack no aparece, funciona cualquier cliente OpenID Connect. Revisa estos ajustes:
- Emisor
https://identity.dazr.eu, URL de discoveryhttps://identity.dazr.eu/.well-known/openid-configuration. - Response type
code(flujo authorization code). Sin flujo implicit ni hybrid. - PKCE es obligatorio, con
code_challenge_method=S256. - Scopes:
openidy solo lo que necesites deprofile,email,address,organisationsyoffline_access. Tu app debe estar registrada para cada scope que pida. - URI de redirección: exactamente como se registró en la consola, con la ruta y la query string si la hay.
- Autenticación del cliente:
client_secret_basicoclient_secret_postpara apps de servidor web,nonepara apps de una sola página y móviles. - Firma del ID token: ES256. Algunas bibliotecas suponen RS256: configura el algoritmo como ES256.
- Usa
subcomo clave de la cuenta. Es pairwise, por organización. - Con
offline_access, guarda el nuevo refresh token tras cada refresh: rotan.
El flujo
- Crea un
code_verifieraleatorio (de 43 a 128 caracteres) y sucode_challenge= base64url(SHA-256(verifier)). Crea unstatey unnoncealeatorios. Guarda los tres en la sesión del usuario. - Redirige al endpoint de autorización con
response_type=code,client_id,redirect_uri,scope(siempre conopenid),state,nonce,code_challengeycode_challenge_method=S256. - La persona inicia sesión si hace falta y ve la pantalla de consentimiento con los datos reales. Si lo permite, vuelve a tu URI de redirección con
code,stateeiss. Comprueba questatecoincide y queisseshttps://identity.dazr.eu. - En tu servidor, haz un POST al endpoint de tokens con
grant_type=authorization_code,code, el mismoredirect_uriy elcode_verifier. - Verifica el ID token: firma ES256 con una clave del JWKS (según el
kid),iss,aud= tu ID de cliente,expy tunonce. Usasubcomo clave de la cuenta.
El parámetro prompt acepta none (respuesta inmediata con login_required o consent_required si la persona tuviera que actuar), login (pedir a la persona que vuelva a iniciar sesión) y consent (mostrar la pantalla de consentimiento aunque ya se hubiera aprobado). Se admite max_age. Consentimiento recordado: si la persona ya aprobó los mismos scopes, vuelve directamente a tu app. Si más adelante pides más, la pantalla de consentimiento muestra solo lo nuevo.
Scopes y claims
Pide solo lo que necesitas. Fuera de esta tabla no existe nada: ni contactos, ni archivos, ni contraseñas.
| Scope | Claims |
|---|---|
openid (obligatorio) | sub: un ID aleatorio para la persona, igual para todas las apps de tu organización y distinto para cualquier otra organización |
profile | name (si está definido) y locale |
email | email y email_verified (siempre true) |
address | address (formatted, street_address, locality, region, postal_code, country), solo si la persona guardó una |
organisations | organisations: las organizaciones que la persona marca en la pantalla de consentimiento, cada una con id, name, country, register_number, vat, vat_verified (true solo si el servicio VIES de la UE ha confirmado el número de IVA), verified, verification (ver Organizaciones verificadas) y role |
offline_access | sin claims; además recibes un refresh token |
Los claims aparecen en el ID token para los scopes que la persona aprobó. El endpoint de información del usuario devuelve el mismo conjunto, con los valores actuales.
Organizaciones verificadas
Dazr verifica una organización cuando su administrador, u otra persona que puede firmar en su nombre, firma una breve declaración con una firma electrónica cualificada (QES). Es instantáneo y funciona en todos los países de la UE. Sin firma cualificada, el representante puede enviar un extracto del registro mercantil y un documento de identidad; Dazr los revisa a mano. Cada organización de la claim organisations lleva su verificación actual:
| verification.status | Significado |
|---|---|
unverified | No verificada, o una verificación falló, caducó o se revocó (por ejemplo tras cambiar la razón social o el número de registro). |
pending | La verificación está en curso: se ha pedido al representante que firme, o Dazr está revisando documentos. |
verified | Verificada. method es qes, qes_org (el propio certificado indica la organización, por número de IVA o de registro), documents o extract, y verified_at es un tiempo Unix. |
El booleano verified se mantiene por compatibilidad y equivale a verification.status === 'verified'. Cada organización tiene también un id, el mismo que usan los webhooks.
"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"
}]
Exigir una organización verificada
En la consola, pon Exigir una organización verificada en Verificación iniciada o Verificada, o pídelo por solicitud con acr_values=urn:dazr:org:verification-started o acr_values=urn:dazr:org:verified (se aplica la más estricta). Tu app también debe pedir el scope organisations.
Quien no tiene una organización válida la completa en el mismo inicio de sesión: su nombre, la organización (con el número de IVA comprobado en VIES) y luego la verificación. Puede firmar en el acto, recibir la declaración por correo o indicar a otra persona que firme. Después marca la organización en la pantalla de consentimiento (solo se pueden marcar organizaciones válidas) y vuelve a tu URI de redirección.
Si otra persona aún tiene que firmar, la persona vuelve con estado pending, también si pediste Verificada: recibes un código, la claim dice pending y el acr del ID token es urn:dazr:org:verification-started en lugar de urn:dazr:org:verified. Decide siempre según verification.status, nunca por el hecho de que el inicio de sesión funcionó. Con prompt=none recibes interaction_required si no se cumple el requisito.
Los cambios del ajuste se aplican desde el próximo inicio de sesión. Quien ya ha iniciado sesión no se desconecta: las respuestas de refresh, el endpoint userinfo y los webhooks llevan siempre el estado actual, así que úsalos para las sesiones existentes.
Webhooks
Añade una URL de webhook (https) en la consola. Ves el secreto de firma una sola vez; puedes renovarlo. Dazr envía organisation.verification.updated cuando una organización que uno de tus usuarios compartió con tu app pasa a pending, verified, failed o unverified. Las apps que nunca recibieron una organización mediante consentimiento nunca se enteran.
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 es pending, verified, failed o unverified. subjects son los valores sub, en tu app, de las personas que le compartieron esta organización. Comprueba la firma antes de fiarte del contenido:
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]));
}
Responde con un 2xx en menos de 8 segundos. Si no, Dazr reintenta tras unos 5 minutos, 30 minutos, 2, 6, 12 y 24 horas, y después marca la entrega como fallida. Cada intento lleva el mismo ID Dazr-Delivery, así que ignora los duplicados. La consola muestra las entregas recientes, puede reenviar una y enviar un evento de prueba (webhook.test). Consultar periódicamente el endpoint userinfo también funciona.
Tokens y duración
| Token | Duración y reglas |
|---|---|
| Código de autorización | 60 segundos, de un solo uso, vinculado a tu cliente, a la URI de redirección y al challenge PKCE. Usar un código dos veces revoca los tokens que emitió. |
| Access token | 10 minutos. Un JWT firmado (ES256, typ at+jwt) con aud = tu ID de cliente. Envíalo como Authorization: Bearer. |
| ID token | 10 minutos. ES256, con iss, sub, aud, exp, iat, auth_time, nonce y amr cuando se conoce. |
| Refresh token | Solo con offline_access. Cambia con cada uso: guarda siempre el nuevo. Volver a enviar un refresh token antiguo revoca todo el inicio de sesión. Caduca tras 30 días sin uso y como máximo a los 180 días. |
Las claves de firma se renuevan. Elige siempre la clave por su kid en el JWKS y vuelve a cargar el JWKS cuando veas un kid desconocido.
Errores
Mientras tu ID de cliente y tu URI de redirección sean válidos, los errores vuelven a tu URI de redirección como error, error_description, state e iss: por ejemplo invalid_request (como PKCE ausente o plain), invalid_scope, unsupported_response_type, access_denied (la persona canceló, o la app está en pruebas y la persona no es miembro), login_required, consent_required e interaction_required (con prompt=none, cuando falta una organización verificada exigida). Con un cliente desconocido o una URI de redirección no registrada, Dazr muestra una página de error y nunca redirige. El endpoint de tokens responde con errores JSON como invalid_client, invalid_grant y unsupported_grant_type.
Cierre de sesión
Para cerrar la sesión de alguien, envía a la persona al endpoint de cierre de sesión con id_token_hint (o client_id), un post_logout_redirect_uri opcional registrado en los ajustes de la app y state. Dazr le pregunta si quiere cerrar también la sesión de Dazr Identity en ese dispositivo y después la devuelve. Revoca en el endpoint de revocación los refresh tokens que ya no necesites.
Pruebas y puesta en marcha
Una app nueva está en pruebas: solo pueden iniciar sesión los miembros de tu organización, y la pantalla de consentimiento lo indica. Para publicarla, haz verificar tu organización en Dazr Identity, añade una URI de redirección https:// y solicita una revisión en la consola. Cambiar el nombre, la finalidad, la política de privacidad o los datos de una app publicada requiere una nueva revisión.
El botón
Usa el texto «Sign in with Dazr Identity» y el logotipo de Dazr como se muestra abajo. Puedes cambiar el tamaño, pero no el logotipo, los colores ni el texto. Enlázalo a tu propia ruta que inicia el flujo.
<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>
Un ejemplo mínimo en Node.js
Node 18 o posterior, sin dependencias. Una app de servidor web con secreto de cliente; para un cliente público, quita la cabecera Authorization y envía client_id en el cuerpo.
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;
}
Límites
Los endpoints de autorización y de tokens tienen límite de peticiones. Si recibes HTTP 429, espera y vuelve a intentarlo; no reintentes en un bucle rápido.
Tus obligaciones
Tu organización es responsable independiente del tratamiento de los datos que recibe. Las Condiciones para desarrolladores de Dazr Identity establecen qué puedes hacer con ellos, cómo protegerlos y cómo notificar las brechas. Preguntas: hello@dazr.eu.