Iniciar sessão com Dazr Identity: documentação para programadores
OpenID Connect 1.0 padrão com o fluxo de código de autorização e PKCE. Não precisa de SDK: qualquer biblioteca OpenID Connect certificada funciona, e código simples também.
As pessoas já podem usar a sua conta Google ou Microsoft dentro do Dazr Identity, pelo que a sua aplicação não precisa de botões Google e Microsoft separados ao lado: um botão em vez de três.
Descoberta e endpoints
Tudo está descrito no documento de descoberta. Aponte a sua biblioteca para o emissor e ela encontra o resto.
| O quê | Endereço |
|---|---|
| Emissor | https://identity.dazr.eu |
| Discovery | https://identity.dazr.eu/.well-known/openid-configuration |
| Autorização | https://identity.dazr.eu/oauth/authorize |
| Token | https://identity.dazr.eu/oauth/token |
| Informações do utilizador | https://identity.dazr.eu/oauth/userinfo |
| Chaves públicas (JWKS) | https://identity.dazr.eu/oauth/jwks |
| Revogação (RFC 7009) | https://identity.dazr.eu/oauth/revoke |
| Introspeção (RFC 7662), só aplicações de servidor web | https://identity.dazr.eu/oauth/introspect |
| Terminar sessão (logout iniciado pela RP) | https://identity.dazr.eu/oauth/logout |
| Relatórios de verificação, só aplicações de servidor web | https://identity.dazr.eu/oauth/verification-report |
Registar uma aplicação
- As aplicações pertencem a uma organização. Os proprietários e administradores da organização registam-nas em Programadores.
- As aplicações de servidor web (clientes confidenciais) recebem um segredo de cliente, mostrado uma vez. Autentique-se no endpoint de token com HTTP Basic (
client_secret_basic) ou no corpo do formulário (client_secret_post). - As aplicações de página única e móveis (clientes públicos) não recebem segredo. Enviam
client_ide dependem do PKCE. - Os URIs de redirecionamento têm de corresponder exatamente. Têm de ser
https://. Enquanto a aplicação está em teste,http://localhostehttp://127.0.0.1em qualquer porta também funcionam. - São obrigatórios um endereço da política de privacidade, um e-mail de suporte e uma finalidade numa linha: as pessoas veem-nos no ecrã de consentimento.

Guias de integração
Cada stack abaixo usa o seu próprio suporte padrão de OpenID Connect. Precisa de três valores da consola: o ID de cliente, o segredo de cliente (só aplicações de servidor web) e o URI de redirecionamento que registou. O resto vem do documento de descoberta.
- Auth.js / NextAuth
- Next.js App Router
- WordPress
- Laravel
- Supabase
- Django
- Node.js (openid-client)
- Qualquer biblioteca OpenID Connect
sub é pairwise: igual para todas as aplicações da sua organização, diferente para qualquer outra organização. Nunca muda para uma pessoa, ao passo que um endereço de e-mail pode mudar. Guarde sub como chave da conta e trate email como dado de contacto.
Auth.js / NextAuth
O Auth.js (NextAuth.js v5) aceita um objeto de fornecedor OpenID Connect personalizado. Registe uma aplicação de servidor web, defina AUTH_SECRET, AUTH_DAZR_ID e AUTH_DAZR_SECRET, e use este URL de callback como URI de redirecionamento: 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" },
},
],
})
O NextAuth.js v4 usa a mesma ideia com type: "oauth" e o URL de descoberta:
// 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
Com o ficheiro auth.ts acima, adicione o route handler e um botão de início de sessão que executa uma 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>
)
}
Formate o botão como mostrado em O botão. Nos callbacks, o sub do Dazr é account.providerAccountId.
WordPress
Use um plugin genérico de cliente OpenID Connect, como o OpenID Connect Generic Client. Registe uma aplicação de servidor web e preencha as definições do plugin com os valores abaixo. O plugin mostra o URI de redirecionamento a registar na sua página de definições; por defeito é https://example.com/wp-admin/admin-ajax.php?action=openid-connect-authorize.
| Definição | Valor |
|---|---|
| ID de cliente | o seu ID de cliente |
| Segredo de cliente | o seu segredo de cliente |
| Scope | openid profile email |
| Endpoint de início de sessão (autorização) | https://identity.dazr.eu/oauth/authorize |
| Endpoint de token | https://identity.dazr.eu/oauth/token |
| Endpoint de informações do utilizador | https://identity.dazr.eu/oauth/userinfo |
| Endpoint de fim de sessão | https://identity.dazr.eu/oauth/logout |
| Chave de identidade | sub |
| PKCE | ativo (S256) |
Os nomes das definições variam um pouco entre plugins. Se um plugin não tiver opção de PKCE, escolha outro: o Dazr Identity rejeita inícios de sessão sem PKCE.
Laravel
O Laravel Socialite não tem um driver OpenID Connect genérico incluído, por isso adicione um pequeno driver personalizado. Envia as pessoas para o Dazr com PKCE e lê o utilizador a partir do endpoint de informações do utilizador. O Socialite não lê o documento de descoberta, por isso os endpoints estão escritos por extenso. Registe uma aplicação de servidor web com o URI de redirecionamento 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
O Supabase Auth não tem um fornecedor OpenID Connect genérico que possa apontar para qualquer emissor: os seus fornecedores de início de sessão e integrações de autenticação de terceiros são listas fixas. Por isso, hoje não pode adicionar o Dazr Identity no painel do Supabase. Se o Supabase acrescentar suporte genérico de OpenID Connect, use os valores da lista de verificação abaixo.
A alternativa: faça o início de sessão com o Dazr Identity no seu próprio servidor (com um dos guias desta página), guarde o sub do Dazr na sua tabela de utilizadores e comunique com o Supabase a partir desse servidor com a chave service role, verificando o acesso no seu próprio código. Nunca envie a chave service role para um navegador.
Django
Use o mozilla-django-oidc. Registe uma aplicação de servidor web com o URI de redirecionamento https://app.example.eu/oidc/callback/. A biblioteca não lê o documento de descoberta, por isso os endpoints estão escritos por extenso.
# 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 defeito, o backend associa os utilizadores pelo e-mail. Para associar as contas ao sub, crie uma subclasse de OIDCAuthenticationBackend e substitua filter_users_by_claims e create_user. Use uma versão atual: as versões antigas não conseguem verificar assinaturas ES256.
Node.js (openid-client)
O openid-client (versão 6) lê o documento de descoberta e verifica por si o PKCE, o state, o nonce e o token de 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 };
}
Qualquer biblioteca OpenID Connect
Se a sua stack não estiver na lista, qualquer cliente OpenID Connect funciona. Verifique estas definições:
- Emissor
https://identity.dazr.eu, URL de descobertahttps://identity.dazr.eu/.well-known/openid-configuration. - Tipo de resposta
code(fluxo de código de autorização). Sem fluxo implícito nem híbrido. - O PKCE é obrigatório, com
code_challenge_method=S256. - Âmbitos:
openide apenas o que precisar deprofile,email,address,organisationseoffline_access. A sua aplicação tem de estar registada para cada âmbito que pede. - URI de redirecionamento: exatamente como registado na consola, incluindo o caminho e qualquer query string.
- Autenticação do cliente:
client_secret_basicouclient_secret_postpara aplicações de servidor web,nonepara aplicações de página única e móveis. - Assinatura do token de ID: ES256. Algumas bibliotecas assumem RS256: defina o algoritmo como ES256.
- Use
subcomo chave da conta. É pairwise, por organização. - Com
offline_access, guarde o novo refresh token após cada renovação: são rotativos.
O fluxo
- Crie um
code_verifieraleatório (43 a 128 caracteres) e o respetivocode_challenge= base64url(SHA-256(verifier)). Crie umstatee umnoncealeatórios. Guarde os três na sessão do utilizador. - Redirecione para o endpoint de autorização com
response_type=code,client_id,redirect_uri,scope(incluindo sempreopenid),state,nonce,code_challengeecode_challenge_method=S256. - A pessoa inicia sessão se necessário e vê o ecrã de consentimento com os dados reais. Quando autoriza, volta ao seu URI de redirecionamento com
code,stateeiss. Verifique se ostatecorresponde e seisséhttps://identity.dazr.eu. - No seu servidor, faça um POST para o endpoint de token com
grant_type=authorization_code,code, o mesmoredirect_urie ocode_verifier. - Verifique o token de ID: assinatura ES256 com uma chave do JWKS (corresponda o
kid),iss,aud= o seu ID de cliente,expe o seunonce. Usesubcomo chave da conta.

O parâmetro prompt aceita none (responde de imediato com login_required ou consent_required se a pessoa tiver de agir), login (pede à pessoa que volte a iniciar sessão) e consent (mostra o ecrã de consentimento mesmo que já tenha sido aprovado). max_age é suportado. Consentimento memorizado: se a pessoa já aprovou os mesmos âmbitos, volta diretamente à sua aplicação. Se pedir mais depois, o ecrã de consentimento mostra apenas o que é novo.
Âmbitos e claims
Peça apenas o que precisa. Nada fora desta tabela existe: sem contactos, sem ficheiros, sem palavras-passe.
| Scope | Claims |
|---|---|
openid (obrigatório) | sub: um ID aleatório para a pessoa, igual para todas as aplicações da sua organização e diferente para qualquer outra organização |
profile | name (se definido) e locale |
email | email e email_verified (sempre true) |
address | address (formatted, street_address, locality, region, postal_code, country), só se a pessoa tiver guardado uma morada |
organisations | organisations: as organizações que a pessoa assinala no ecrã de consentimento, cada uma com id, name, display_name, website, country, register_number, vat, vat_verified (true apenas quando o serviço VIES da UE confirmou o número de IVA), verified, verification (ver Organizações verificadas) e role (owner, admin ou member) |
offline_access | nenhuma claim; recebe também um refresh token |
As claims aparecem no token de ID para os âmbitos que a pessoa aprovou. O endpoint de informações do utilizador devolve o mesmo conjunto, com os valores atuais.
Organizações verificadas
O Dazr só verifica uma organização quando fontes oficiais provam duas coisas: quem é a pessoa e que representa esta organização, com o seu nome, número de registo e número de IVA. Normalmente, o administrador assina uma breve declaração com uma assinatura eletrónica qualificada (QES) e junta a certidão do registo comercial em que consta; com uma certidão selada pelo registo, como a visura italiana ou um extrato KvK neerlandês, é imediato; caso contrário, o Dazr analisa-a. Uma assinatura qualificada, por si só, nunca torna uma organização verificada. Cada organização na claim organisations traz a sua verificação atual:

| verification.status | Significado |
|---|---|
unverified | Não verificada, ou uma verificação falhou, expirou ou foi revogada (por exemplo, depois de mudar a denominação ou o número de registo). |
pending | A verificação está em curso: foi pedido ao representante que assinasse, a certidão do registo comercial ainda falta ou o Dazr está a analisar documentos. method indica a via iniciada, por exemplo qes (o representante assina a declaração), qseal, pec, letter, video ou documents. |
verified | Verificada. method é qes_org (assinatura qualificada, o certificado indica a organização), extract_sealed (assinatura qualificada ou documento de identificação analisado, mais uma certidão selada pelo registo comercial), qes_reviewed (assinatura qualificada, certidão verificada pelo Dazr), qseal (o selo eletrónico qualificado da organização), documents, pec (um código enviado para o endereço PEC da organização, Itália), letter (um código enviado por correio para a sede), video (uma videochamada com o Dazr) ou extract. authority indica o que provou que a pessoa representa a organização (ver abaixo), e verified_at é um tempo Unix. |
Apenas quando o estado é verified, verification.authority (e authority nos webhooks) indica como foi provada a ligação entre a pessoa e a organização:
| verification.authority | Significado |
|---|---|
certificate | O certificado qualificado do signatário, ou o selo qualificado da organização, indica a organização com o seu número de registo. Se o certificado tiver apenas um número de IVA, uma certidão do registo provou o número de registo. |
register_extract | Uma certidão selada pelo registo comercial, com no máximo 3 meses, indica a organização com os seus números de registo e de IVA e inclui o signatário como administrador ou representante legal. |
reviewed | O Dazr verificou uma certidão que inclui o signatário ou, sem assinatura qualificada, a certidão e um documento de identificação. |
pec | Um código enviado para o endereço PEC indicado na visura selada pelo registo (Itália). |
letter | Um código enviado por correio para a sede, depois de o Dazr ter verificado os dados no registo. |
video | Uma videochamada com o Dazr: um documento de identificação e uma certidão oficial em que a pessoa consta. |
O booleano verified mantém-se por compatibilidade e é igual a verification.status === 'verified'. Cada organização tem também um id, o mesmo usado pelos webhooks.
Quando o estado é pending ou verified, verification.report_url é o endereço do respetivo relatório de verificação.
"organisations": [{
"id": "org_3k9x2m0q8w1v5t7a", "name": "Conti Logistica S.r.l.", "country": "IT",
"register_number": "REA MI1234567", "vat": "IT12345678903", "vat_verified": true,
"display_name": "Conti", "website": "https://conti.example",
"verified": false, "verification": { "status": "pending", "method": "qes" }, "role": "owner"
}]
role e a pertença são lidas de novo em cada renovação de token e pedido de informações do utilizador: após uma mudança de função, a resposta seguinte traz a nova função, e uma organização que a pessoa deixou ou da qual foi removida deixa de ser incluída. Quando um administrador da organização desliga a sua aplicação, a organização é omitida dos tokens emitidos antes, até um membro a voltar a partilhar com a sua aplicação num novo início de sessão. display_name e website são null quando a organização não os definiu; name é sempre a denominação legal.
Exigir uma organização verificada
Na consola, defina Exigir uma organização verificada como Verificação iniciada ou Verificada, ou peça-o por pedido com acr_values=urn:dazr:org:verification-started ou acr_values=urn:dazr:org:verified (prevalece o mais rigoroso dos dois). A sua aplicação tem também de pedir o âmbito organisations.

Quem não tem uma organização que cumpra o requisito é conduzido pelo processo no mesmo início de sessão: o nome, a organização (com o número de IVA verificado no VIES) e depois a verificação. Pode assinar de imediato, receber a declaração por e-mail ou indicar outra pessoa que assine. Depois assinala a organização no ecrã de consentimento (só se podem assinalar organizações que cumpram o requisito) e volta ao seu URI de redirecionamento.
Se outra pessoa ainda tiver de assinar, a pessoa volta com o estado pending, mesmo que tenha pedido Verificada: recebe um código, a claim indica pending e o acr do token de ID é urn:dazr:org:verification-started em vez de urn:dazr:org:verified. Decida sempre com base em verification.status, nunca no facto de o início de sessão ter sido bem-sucedido. Com prompt=none recebe interaction_required quando o requisito não é cumprido.
As alterações à definição aplicam-se a partir do próximo início de sessão. As pessoas com sessão já iniciada não são desligadas: as respostas de renovação, o endpoint de informações do utilizador e os webhooks trazem sempre o estado atual, por isso use-os para reagir nas sessões existentes.
Webhooks
Adicione um URL de webhook (https) na consola. Vê o segredo de assinatura uma vez; pode rodá-lo. O Dazr envia organisation.verification.updated quando uma organização que um dos seus utilizadores partilhou com a sua aplicação passa a pendente, verificada, falhada ou não verificada. As aplicações que nunca receberam uma organização através de consentimento nunca são notificadas.
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", "authority": "certificate", "verified_at": 1791021598,
"report_url": "https://identity.dazr.eu/oauth/verification-report?org=org_3k9x2m0q8w1v5t7a",
"subjects": ["Xo1c…"] } }
status é pending, verified, failed ou unverified. subjects são os valores sub, na sua aplicação, das pessoas que partilharam esta organização com ela. Verifique a assinatura antes de confiar no corpo:
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]));
}
Responda com qualquer 2xx em 8 segundos. Caso contrário, o Dazr tenta de novo após cerca de 5 minutos, 30 minutos, 2, 6, 12 e 24 horas e depois marca a entrega como falhada. Todas as tentativas têm o mesmo ID Dazr-Delivery, por isso ignore duplicados. A consola mostra as entregas recentes, pode reenviar uma e pode enviar um evento de teste (webhook.test). Consultar periodicamente o endpoint de informações do utilizador também funciona.
Relatórios de verificação para auditorias
Para cada organização partilhada com a sua aplicação, pode obter um relatório de como e quando o Dazr a verificou, para o seu dossiê de auditoria. Existe enquanto a organização estiver verificada e enquanto a verificação estiver em curso, mostrando o que já foi feito.
- A organização: nome, país, número de registo, número de IVA e sede.
- A verificação: estado, método, autoridade e quando foi pedida, assinada e verificada.
- As provas: para uma assinatura ou selo qualificado, o nome do signatário tal como consta do certificado, o emissor do certificado, o número de série, a validade e a impressão digital SHA-256, o seu estatuto qualificado (declarações QC, prestador e serviço de confiança da lista de confiança da UE, com o número de sequência e a data de emissão da lista), a hora da assinatura e a verificação de revogação. Para uma certidão selada pelo registo, quem a selou, quando e o que coincidiu: nome, número de registo, número de IVA, administrador. O resultado do VIES. Para um código PEC, uma carta ou uma videochamada, as datas e as verificações assinaladas por um revisor do Dazr, apresentado como «Revisor do Dazr» com um ID interno.
- Para os seus registos: a impressão digital SHA-256 de cada ficheiro de prova, um ID de documento, quando o relatório foi gerado e uma breve explicação de cada método e do que significa a autoridade.
Um relatório nunca contém imagens nem números de documentos de identificação, códigos de utilização única, o endereço PEC, endereços de e-mail nem qualquer morada que não seja a sede.
Obter um relatório
Autentique-se com o seu ID de cliente e segredo de cliente, como no endpoint de token: HTTP Basic (client_secret_basic) ou o corpo do formulário de um POST (client_secret_post). Nunca coloque o segredo no URL. Use o id da organização das claims ou dos webhooks, ou simplesmente o verification.report_url que trazem.
curl -u "$DAZR_CLIENT_ID:$DAZR_CLIENT_SECRET" -o report.pdf \
"https://identity.dazr.eu/oauth/verification-report?org=org_3k9x2m0q8w1v5t7a&format=pdf&lang=en"
| Parâmetro | Significado |
|---|---|
org | O id da organização. |
format | pdf (predefinição) ou jws, os mesmos factos em JSON assinado. |
lang | Idioma do PDF: en (predefinição), nl, it, de, fr, es ou pl. |
doc | Em vez do relatório: declaration, a declaração assinada pelo administrador (só assinatura ou selo qualificado), ou extract, a certidão do registo comercial. |
Só é possível obter organizações que pelo menos um dos seus utilizadores partilhou com a sua aplicação através de consentimento. Qualquer outra organização responde 404, tal como uma que não existe ou não tem verificação; um segredo de cliente errado responde 401. As aplicações sem segredo de cliente não podem obter relatórios. As transferências têm limite de frequência, e cada uma aparece na atividade da organização para os seus administradores, com o nome da sua aplicação. Na consola, a página da sua aplicação mostra as organizações ligadas com as mesmas transferências.
Verificar o JSON assinado
Com format=jws recebe um JWS compacto (ES256, cabeçalho typ dazr-verification-report+jwt), assinado com as mesmas chaves que os tokens de ID. As suas claims são iss, iat, jti (o ID do documento), sub (o id da organização), aud (o seu ID de cliente) e report, os factos. O PDF traz o mesmo JWS na última página e nos metadados (DazrVerificationReport), pelo que um PDF também pode ser verificado offline.
import { createRemoteJWKSet, jwtVerify } from 'jose';
const jwks = createRemoteJWKSet(new URL('https://identity.dazr.eu/oauth/jwks'));
// jws = the response body of format=jws, or the text from the PDF metadata
const { payload } = await jwtVerify(jws, jwks, {
issuer: 'https://identity.dazr.eu',
audience: process.env.DAZR_CLIENT_ID,
typ: 'dazr-verification-report+jwt',
});
console.log(payload.report.verification.status, payload.report.verification.authority);
Deixar um auditor verificar a assinatura
Para uma assinatura ou selo qualificado, doc=declaration devolve a declaração assinada exatamente como foi carregada: um PDF assinado (PAdES) ou um ficheiro .p7m (CAdES). Um auditor pode validá-la de forma independente com o validador de demonstração DSS da Comissão Europeia: carregue o ficheiro, mantenha a política de validação predefinida e execute-a. O resultado mostra se a assinatura é qualificada (QESig, ou QESeal para um selo), quem assinou e o prestador de serviços de confiança da lista de confiança da UE. doc=extract devolve a certidão do registo comercial; uma certidão selada pelo registo pode ser validada da mesma forma.
O relatório é uma prova para o seu dossiê de auditoria. As entidades reguladas continuam responsáveis pela sua própria diligência devida quanto aos clientes.
O Dazr conserva a declaração assinada, a certidão e os dados do relatório enquanto a organização estiver verificada e durante 5 anos após o fim da verificação ou a eliminação da organização. Guarde as suas próprias cópias durante o tempo que as suas regras exigirem.
Tokens e validades
| Token | Validade e regras |
|---|---|
| Código de autorização | 60 segundos, utilização única, associado ao seu cliente, URI de redirecionamento e desafio PKCE. Usar um código duas vezes revoga os tokens que emitiu. |
| Token de acesso | 10 minutos. Um JWT assinado (ES256, typ at+jwt) com aud = o seu ID de cliente. Envie-o como Authorization: Bearer. |
| Token de ID | 10 minutos. ES256, com iss, sub, aud, exp, iat, auth_time, nonce e amr quando conhecido. |
| Refresh token | Só com offline_access. Roda a cada utilização: guarde sempre o novo. Enviar novamente um refresh token antigo revoga todo o início de sessão. Expira após 30 dias sem utilização e, no máximo, após 180 dias. |
As chaves de assinatura são rotativas. Escolha sempre a chave pelo kid do JWKS e atualize o JWKS quando vir um kid desconhecido.
Erros
Desde que o seu ID de cliente e o URI de redirecionamento sejam válidos, os erros regressam ao seu URI de redirecionamento como error, error_description, state e iss: por exemplo invalid_request (como PKCE em falta ou simples), invalid_scope, unsupported_response_type, access_denied (a pessoa cancelou, ou a aplicação está em teste e a pessoa não é membro), login_required, consent_required e interaction_required (com prompt=none, quando falta uma organização verificada exigida). Com um cliente desconhecido ou um URI de redirecionamento não registado, o Dazr mostra uma página de erro e nunca redireciona. O endpoint de token responde com erros JSON como invalid_client, invalid_grant e unsupported_grant_type.
Terminar sessão
Para terminar a sessão de alguém, envie a pessoa para o endpoint de fim de sessão com id_token_hint (ou client_id), um post_logout_redirect_uri opcional registado nas definições da sua aplicação e state. O Dazr pergunta se também quer terminar a sessão do Dazr Identity nesse dispositivo e depois envia-a de volta. Revogue os refresh tokens de que já não precisa no endpoint de revogação.
Testar e entrar em produção
Uma aplicação nova está em teste: só os membros da sua organização podem iniciar sessão, e o ecrã de consentimento indica-o. Para entrar em produção, verifique a sua organização no Dazr Identity, adicione um URI de redirecionamento https:// e peça uma revisão para produção na consola. Alterar o nome, a finalidade, a política de privacidade ou os dados de uma aplicação em produção exige uma nova revisão.
Transferir uma aplicação para outra organização
Uma aplicação pode passar para outra organização registada na Dazr, por exemplo após uma venda ou uma reorganização. Um proprietário ou administrador inicia a transferência em Transferir aplicação, nas definições da aplicação, no Dazr Identity ou no portal do Sign. Um proprietário ou administrador da outra organização aceita-a ou recusa-a no prazo de 14 dias. Até lá, o remetente pode cancelar e nada muda.
- Quem a pode receber. Apenas uma organização registada na Dazr, identificada pelo respetivo ID de organização. Os proprietários e administradores encontram o ID nos dados legais da organização no Dazr Identity. Uma aplicação publicada, uma aplicação em análise, uma aplicação que exige uma organização verificada e uma aplicação com a API do Dazr Sign ativada só podem passar para uma organização verificada. Esta condição é verificada novamente quando a transferência é aceite.
- Um início de sessão recente. Iniciar e aceitar uma transferência exige um início de sessão nos últimos 10 minutos.
- O que é transferido. O ID de cliente, as definições, o logótipo, os URI de redirecionamento, as API ativadas e os URL de webhooks. O administrador que aceita também aceita os Termos para programadores em nome da nova organização.
- O segredo de cliente. Uma aplicação de servidor web recebe um novo segredo de cliente, mostrado uma única vez ao administrador que aceita. O segredo anterior continua a funcionar nos endpoints de token, revogação e introspeção durante o período de transição escolhido ao iniciar a transferência: nenhum, 24 horas (predefinição) ou 7 dias. Os relatórios de verificação exigem o novo segredo.
- Webhooks. Os URL mantêm-se. Os respetivos segredos de assinatura, do Dazr Identity e do Dazr Sign, são renovados e mostrados uma única vez ao administrador que aceita. As entregas anteriores à transferência não são reenviadas.
- As pessoas e o respetivo
sub. Cada pessoa mantém o mesmosubna aplicação, pelo que as contas na sua aplicação continuam a funcionar. É diferente dosubdessa pessoa nas outras aplicações da nova organização. As pessoas aprovam novamente a aplicação no início de sessão seguinte, porque o ecrã de consentimento passa a indicar a nova organização. Os tokens emitidos antes da transferência deixam de funcionar. - O que fica com o proprietário anterior. As contagens de inícios de sessão e de consentimentos e as entregas de webhooks anteriores à transferência. Os documentos criados através da API do Dazr Sign ficam com as pessoas a quem pertencem, e o novo proprietário da aplicação não os pode ver.
O botão
Use o texto «Iniciar sessão com Dazr Identity» e o logótipo do Dazr como abaixo. Pode alterar o tamanho, mas não o logótipo, as cores nem o texto. Ligue-o à sua própria rota que inicia o fluxo.
<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 'Dazr', 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>
Tipo de letra Dazr. O botão usa o tipo de letra Dazr, de utilização gratuita ao abrigo da SIL Open Font License. Onde o Dazr não estiver carregado, é usado o tipo de letra do sistema. Transfira o tipo de letra Dazr (ZIP com ficheiros TTF e WOFF2) para o alojar no seu próprio site:
/* woff2 files from the ZIP, copied to /fonts/ on your site */
@font-face { font-family: 'Dazr'; font-weight: 400; font-display: swap;
src: url('/fonts/dazr-regular-latin.woff2') format('woff2'); }
@font-face { font-family: 'Dazr'; font-weight: 700; font-display: swap;
src: url('/fonts/dazr-bold-latin.woff2') format('woff2'); }
.dazr-signin { font: 600 15px/1 'Dazr', system-ui, sans-serif; }
Um exemplo mínimo em Node.js
Node 18 ou posterior, sem dependências. Uma aplicação de servidor web com segredo de cliente; para um cliente público, retire o cabeçalho Authorization e envie client_id no corpo.
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;
}
Limites
Os endpoints de autorização e de token têm limite de frequência. Se receber HTTP 429, aguarde e tente novamente; não repita num ciclo apertado.
As suas obrigações
A sua organização é responsável pelo tratamento, de forma independente, dos dados que recebe. Os Termos para Programadores do Dazr Identity definem o que pode fazer com eles, como os manter seguros e como comunicar violações. Dúvidas: hello@dazr.eu.