Log ind med Dazr Identity: udviklerdokumentation

Folk kan allerede bruge deres Google- eller Microsoft-konto i Dazr Identity, så din app behøver ikke separate Google- og Microsoft-knapper ved siden af: én knap i stedet for tre.

Discovery og endpoints

Alt er beskrevet i discovery-dokumentet. Peg dit bibliotek mod udstederen, så finder det resten.

HvadAdresse
Udsteder (issuer)https://identity.dazr.eu
Discoveryhttps://identity.dazr.eu/.well-known/openid-configuration
Autorisationhttps://identity.dazr.eu/oauth/authorize
Tokenhttps://identity.dazr.eu/oauth/token
Brugeroplysningerhttps://identity.dazr.eu/oauth/userinfo
Offentlige nøgler (JWKS)https://identity.dazr.eu/oauth/jwks
Tilbagekaldelse (RFC 7009)https://identity.dazr.eu/oauth/revoke
Introspektion (RFC 7662), kun webserverappshttps://identity.dazr.eu/oauth/introspect
Log ud (RP-initiated logout)https://identity.dazr.eu/oauth/logout
Verifikationsrapporter, kun webserverappshttps://identity.dazr.eu/oauth/verification-report

Registrér en app

En app i udviklerkonsollen for Dazr Identity: klient-id, discovery-URL, omdirigerings-URI'er, scopes og trinene til at forbinde den

Integrationsguides

Hver stack nedenfor bruger sin egen standardunderstøttelse af OpenID Connect. Du skal bruge tre værdier fra konsollen: klient-id'et, klienthemmeligheden (kun webserverapps) og den omdirigerings-URI, du registrerede. Resten kommer fra discovery-dokumentet.

Auth.js / NextAuth

Auth.js (NextAuth.js v5) tager et brugerdefineret OpenID Connect-providerobjekt. Registrér en webserverapp, sæt AUTH_SECRET, AUTH_DAZR_ID og AUTH_DAZR_SECRET, og brug denne callback-URL som omdirigerings-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 bruger samme idé med type: "oauth" og discovery-URL'en:

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

Med filen auth.ts ovenfor tilføjer du route-handleren og en login-knap, der kører en 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>
  )
}

Style knappen som vist under Knappen. I callbacks er Dazrs sub lig med account.providerAccountId.

WordPress

Brug et generisk OpenID Connect-klientplugin, f.eks. OpenID Connect Generic Client. Registrér en webserverapp, og udfyld pluginets indstillinger med værdierne nedenfor. Pluginet viser den omdirigerings-URI, der skal registreres, på sin indstillingsside; som standard er den https://example.com/wp-admin/admin-ajax.php?action=openid-connect-authorize.

IndstillingVærdi
Klient-iddit klient-id
Klienthemmeligheddin klienthemmelighed
Scopeopenid profile email
Login-endpoint (autorisation)https://identity.dazr.eu/oauth/authorize
Token-endpointhttps://identity.dazr.eu/oauth/token
Endpoint for brugeroplysningerhttps://identity.dazr.eu/oauth/userinfo
Endpoint for afslutning af sessionhttps://identity.dazr.eu/oauth/logout
Identitetsnøglesub
PKCEtil (S256)

Navnene på indstillingerne varierer lidt mellem plugins. Har et plugin ingen PKCE-mulighed, så vælg et andet: Dazr Identity afviser logins uden PKCE.

Laravel

Laravel Socialite har ingen indbygget generisk OpenID Connect-driver, så tilføj en lille brugerdefineret driver. Den sender folk til Dazr med PKCE og læser brugeren fra endpointet for brugeroplysninger. Socialite læser ikke discovery-dokumentet, så endpoints er skrevet ud. Registrér en webserverapp med omdirigerings-URI'en 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 har ingen generisk OpenID Connect-provider, du kan pege mod en hvilken som helst udsteder: dens loginproviders og integrationer med tredjeparts-auth er faste lister. Så du kan ikke tilføje Dazr Identity i Supabase-dashboardet i dag. Hvis Supabase tilføjer generisk understøttelse af OpenID Connect, så brug værdierne fra tjeklisten nedenfor.

Alternativet: log folk ind med Dazr Identity på din egen server (med en af guiderne på denne side), gem Dazrs sub i din brugertabel, og tal med Supabase fra den server med service role-nøglen, mens du tjekker adgang i din egen kode. Send aldrig service role-nøglen til en browser.

Django

Brug mozilla-django-oidc. Registrér en webserverapp med omdirigerings-URI'en https://app.example.eu/oidc/callback/. Biblioteket læser ikke discovery-dokumentet, så endpoints er skrevet ud.

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

Som standard matcher backenden brugere efter e-mail. For at bruge sub som kontonøgle laver du en underklasse af OIDCAuthenticationBackend og overskriver filter_users_by_claims og create_user. Brug en aktuel version: ældre versioner kan ikke verificere ES256-signaturer.

Node.js (openid-client)

openid-client (version 6) læser discovery-dokumentet og tjekker PKCE, state, nonce og ID-tokenet for dig.

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

Ethvert OpenID Connect-bibliotek

Står din stack ikke på listen, virker enhver OpenID Connect-klient. Tjek disse indstillinger:

Forløbet

  1. Opret en tilfældig code_verifier (43 til 128 tegn) og dens code_challenge = base64url(SHA-256(verifier)). Opret en tilfældig state og nonce. Gem alle tre i brugerens session.
  2. Omdirigér til authorization-endpointet med response_type=code, client_id, redirect_uri, scope (altid inklusive openid), state, nonce, code_challenge og code_challenge_method=S256.
  3. Personen logger ind, hvis det er nødvendigt, og ser samtykkeskærmen med de faktiske data. Når personen tillader det, kommer vedkommende tilbage til din omdirigerings-URI med code, state og iss. Tjek, at state matcher, og at iss er https://identity.dazr.eu.
  4. På din server laver du en POST til token-endpointet med grant_type=authorization_code, code, den samme redirect_uri og code_verifier.
  5. Verificér ID-tokenet: ES256-signatur med en nøgle fra JWKS (match kid), iss, aud = dit klient-id, exp og din nonce. Brug sub som kontonøgle.
Samtykkeskærmen i Dazr Identity for en app kaldet Ledgerline: den viser navnet, e-mailadressen og den verificerede organisation, brugeren valgte at dele

Parameteren prompt accepterer none (svar med det samme med login_required eller consent_required, hvis personen skulle handle), login (bed personen logge ind igen) og consent (vis samtykkeskærmen, selv når den er godkendt før). max_age understøttes. Husket samtykke: hvis personen har godkendt de samme scopes før, går vedkommende direkte tilbage til din app. Beder du om mere senere, viser samtykkeskærmen kun det nye.

Scopes og claims

Bed kun om det, du har brug for. Intet uden for denne tabel findes: ingen kontakter, ingen filer, ingen adgangskoder.

ScopeClaims
openid (påkrævet)sub: et tilfældigt id for personen, det samme for alle apps i din organisation og forskelligt for alle andre organisationer
profilename (hvis angivet) og locale
emailemail og email_verified (altid true)
addressaddress (formatted, street_address, locality, region, postal_code, country), kun hvis personen har gemt en
organisationsorganisations: de organisationer, personen markerer på samtykkeskærmen, hver med id, name, display_name, website, country, register_number, vat, vat_verified (kun true, når EU's VIES-tjeneste har bekræftet momsnummeret), verified, verification (se Verificerede organisationer) og role (owner, admin eller member)
offline_accessingen claims; du modtager også et refresh token

Claims vises i ID-tokenet for de scopes, personen godkendte. Endpointet for brugeroplysninger returnerer det samme sæt med aktuelle værdier.

Verificerede organisationer

Dazr verificerer kun en organisation, når officielle kilder beviser to ting: hvem personen er, og at personen repræsenterer denne organisation, med dens navn, registreringsnummer og momsnummer. Typisk underskriver direktøren en kort erklæring med en kvalificeret elektronisk signatur (QES) og tilføjer udskriften fra virksomhedsregistret, der nævner vedkommende; med en udskrift forseglet af registret, som den italienske visura eller en hollandsk KvK-udskrift, sker det øjeblikkeligt, ellers gennemgår Dazr den. En kvalificeret signatur alene gør aldrig en organisation verificeret. Hver organisation i claimet organisations har sin aktuelle verifikation:

Verifikation af en organisation i Dazr Identity: direktøren underskriver erklæringen med en kvalificeret signatur nu eller senere via e-mail, eller uploader virksomhedsudskriften og et ID
verification.statusBetydning
unverifiedIkke verificeret, eller en verifikation mislykkedes, udløb eller blev tilbagekaldt (f.eks. efter at det juridiske navn eller virksomhedsnummeret ændrede sig).
pendingVerifikationen er i gang: repræsentanten er blevet bedt om at underskrive, udskriften fra virksomhedsregistret mangler stadig, eller Dazr gennemgår dokumenter. method angiver, hvilken vej der blev startet, f.eks. qes (repræsentanten underskriver erklæringen), qseal, pec, letter, video eller documents.
verifiedVerificeret. method er qes_org (kvalificeret signatur, certifikatet nævner organisationen), extract_sealed (kvalificeret signatur eller gennemgået ID plus en udskrift forseglet af virksomhedsregistret), qes_reviewed (kvalificeret signatur, udskrift tjekket af Dazr), qseal (organisationens kvalificerede elektroniske segl), documents, pec (en kode sendt til organisationens PEC-adresse, Italien), letter (en kode sendt med posten til det registrerede hjemsted), video (et videoopkald med Dazr) eller extract. authority angiver, hvad der beviste, at personen repræsenterer organisationen (se nedenfor), og verified_at er et Unix-tidspunkt.

Kun når status er verified, angiver verification.authority (og authority i webhooks), hvordan forbindelsen mellem personen og organisationen blev bevist:

verification.authorityBetydning
certificateUnderskriverens kvalificerede certifikat eller organisationens kvalificerede segl nævner organisationen med dens registreringsnummer. Med kun et momsnummer i certifikatet beviste en registerudskrift registreringsnummeret.
register_extractEn udskrift forseglet af virksomhedsregistret, højst 3 måneder gammel, nævner organisationen med dens registrerings- og momsnummer og angiver underskriveren som direktør eller juridisk repræsentant.
reviewedDazr tjekkede en udskrift, der nævner underskriveren, eller, uden kvalificeret signatur, udskriften og et ID.
pecEn kode sendt til PEC-adressen på visuraen forseglet af registret (Italien).
letterEn kode sendt med posten til det registrerede hjemsted, efter at Dazr havde tjekket oplysningerne mod registret.
videoEt videoopkald med Dazr: et ID og en officiel udskrift, der nævner personen.

Booleanen verified bevares af kompatibilitetshensyn og er lig med verification.status === 'verified'. Hver organisation har også et id, det samme, som webhooks bruger.

Når status er pending eller verified, er verification.report_url adressen på dens verifikationsrapport.

"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 og medlemskab læses igen ved hver fornyelse af token og hver anmodning om brugeroplysninger: efter en rolleændring har næste svar den nye rolle, og en organisation, personen har forladt eller er fjernet fra, er ikke længere med. Når en administrator af organisationen afbryder din app, udelades organisationen af tidligere udstedte tokens, indtil et medlem deler den med din app igen ved et nyt login. display_name og website er null, når organisationen ikke har angivet dem; name er altid det juridiske navn.

Kræv en verificeret organisation

Sæt i konsollen Kræv en verificeret organisation til Verifikation startet eller Verificeret, eller bed om det pr. anmodning med acr_values=urn:dazr:org:verification-started eller acr_values=urn:dazr:org:verified (den strengeste af de to vinder). Din app skal også anmode om scopet organisations.

Indstillingen Kræv en verificeret organisation i udviklerkonsollen: ikke påkrævet, verifikation startet eller verificeret

En person uden en kvalificerende organisation tages gennem det i samme login: navnet, organisationen (med momsnummeret tjekket i VIES) og derefter verifikationen. Personen kan underskrive med det samme, få erklæringen sendt til sig selv via e-mail eller angive en anden, der underskriver. Derefter markerer personen organisationen på samtykkeskærmen (kun kvalificerende organisationer kan markeres) og vender tilbage til din omdirigerings-URI.

Skal en anden stadig underskrive, kommer personen tilbage med status pending, også når du bad om Verificeret: du modtager en kode, claimet siger pending, og ID-tokenets acr er urn:dazr:org:verification-started i stedet for urn:dazr:org:verified. Styr altid adgang efter verification.status, aldrig efter, at login lykkedes. Med prompt=none får du interaction_required, når kravet ikke er opfyldt.

Ændringer af indstillingen gælder fra næste login. Folk, der allerede er logget ind, logges ikke ud: svar på fornyelser, endpointet for brugeroplysninger og webhooks har altid den aktuelle status, så brug dem til at reagere for eksisterende sessioner.

Webhooks

Tilføj en webhook-URL (https) i konsollen. Du ser signeringshemmeligheden én gang; du kan rotere den. Dazr sender organisation.verification.updated, når en organisation, som en af dine brugere har delt med din app, bliver pending, verified, failed eller unverified. Apps, der aldrig har modtaget en organisation via samtykke, hører aldrig om det.

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 er pending, verified, failed eller unverified. subjects er sub-værdierne i din app for de personer, der har delt denne organisation med den. Tjek signaturen, før du stoler på body'en:

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

Svar med en hvilken som helst 2xx inden for 8 sekunder. Ellers prøver Dazr igen efter cirka 5 minutter, 30 minutter, 2, 6, 12 og 24 timer og markerer derefter leveringen som mislykket. Hvert forsøg har samme Dazr-Delivery-id, så ignorér dubletter. Konsollen viser de seneste leveringer, kan sende en igen og kan sende en testhændelse (webhook.test). Polling af endpointet for brugeroplysninger virker også.

Verifikationsrapporter til revisioner

For hver organisation, der er delt med din app, kan du hente en rapport om, hvordan og hvornår Dazr verificerede den, til din revisionssag. Den findes, mens organisationen er verificeret, og mens verifikationen er i gang, og viser så det, der er gjort indtil videre.

En rapport indeholder aldrig billeder eller numre fra identitetsdokumenter, engangskoder, PEC-adressen, e-mailadresser eller andre adresser end det registrerede hjemsted.

Hent en rapport

Godkend med dit klient-id og din klienthemmelighed, som ved token-endpointet: HTTP Basic (client_secret_basic) eller formularens body i en POST (client_secret_post). Sæt aldrig hemmeligheden i URL'en. Brug organisationens id fra claims eller webhooks, eller blot den verification.report_url, de indeholder.

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"
ParameterBetydning
orgOrganisationens id.
formatpdf (standard) eller jws, de samme fakta som signeret JSON.
langPDF'ens sprog: en (standard), nl, it, de, fr, es eller pl.
docI stedet for rapporten: declaration, direktørens signerede erklæring (kun kvalificeret signatur eller segl), eller extract, udskriften fra virksomhedsregistret.

Kun organisationer, som mindst én af dine brugere har delt med din app via samtykke, kan hentes. Enhver anden organisation svarer 404, ligesom en, der ikke findes eller ikke har nogen verifikation; en forkert klienthemmelighed svarer 401. Apps uden klienthemmelighed kan ikke hente rapporter. Downloads er hastighedsbegrænsede, og hver download vises i organisationens aktivitet for dens administratorer med din apps navn. I konsollen viser din apps side de forbundne organisationer med de samme downloads.

Verificér den signerede JSON

Med format=jws modtager du en kompakt JWS (ES256, header typ dazr-verification-report+jwt), signeret med de samme nøgler som ID-tokens. Dens claims er iss, iat, jti (dokument-id'et), sub (organisationens id), aud (dit klient-id) og report, fakta. PDF'en indeholder den samme JWS på sidste side og i sine metadata (DazrVerificationReport), så en PDF også kan tjekkes 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);

Lad en revisor tjekke signaturen

For en kvalificeret signatur eller et kvalificeret segl returnerer doc=declaration den signerede erklæring præcis, som den blev uploadet: en signeret PDF (PAdES) eller en .p7m-fil (CAdES). En revisor kan validere den uafhængigt med Europa-Kommissionens DSS-demovalidator: upload filen, behold standardpolitikken for validering, og kør den. Resultatet viser, om signaturen er kvalificeret (QESig, eller QESeal for et segl), hvem der underskrev, og tillidstjenesteudbyderen fra EU's tillidsliste. doc=extract returnerer udskriften fra virksomhedsregistret; en registerforseglet udskrift kan valideres på samme måde.

Rapporten er dokumentation til din revisionssag. Regulerede virksomheder er fortsat ansvarlige for deres egne kundekendskabsprocedurer.

Dazr opbevarer den signerede erklæring, udskriften og rapportdataene, mens organisationen er verificeret, og i 5 år efter at dens verifikation ophører, eller organisationen slettes. Opbevar dine egne kopier, så længe dine regler kræver det.

Tokens og levetider

TokenLevetid og regler
Autorisationskode60 sekunder, engangsbrug, bundet til din klient, omdirigerings-URI og PKCE-challenge. Bruges en kode to gange, tilbagekaldes de tokens, den udstedte.
Access token10 minutter. En signeret JWT (ES256, typ at+jwt) med aud = dit klient-id. Send den som Authorization: Bearer.
ID-token10 minutter. ES256, med iss, sub, aud, exp, iat, auth_time, nonce og amr, hvor kendt.
Refresh tokenKun med offline_access. Roterer ved hver brug: gem altid det nye. Sendes et gammelt refresh token igen, tilbagekaldes hele loginet. Udløber efter 30 dage uden brug og højst efter 180 dage.

Signeringsnøgler roterer. Vælg altid nøglen efter kid fra JWKS, og opdatér JWKS, når du ser et ukendt kid.

Fejl

Så længe dit klient-id og din omdirigerings-URI er gyldige, kommer fejl tilbage til din omdirigerings-URI som error, error_description, state og iss: f.eks. invalid_request (som manglende eller plain PKCE), invalid_scope, unsupported_response_type, access_denied (personen annullerede, eller appen er i test, og personen er ikke medlem), login_required, consent_required og interaction_required (med prompt=none, når en påkrævet verificeret organisation mangler). Med en ukendt klient eller en ikke-registreret omdirigerings-URI viser Dazr en fejlside og omdirigerer aldrig. Token-endpointet svarer med JSON-fejl som invalid_client, invalid_grant og unsupported_grant_type.

Log ud

For at logge en person ud sender du vedkommende til logout-endpointet med id_token_hint (eller client_id), en valgfri post_logout_redirect_uri, der er registreret i din apps indstillinger, og state. Dazr spørger, om personen også vil logge ud af Dazr Identity på den enhed, og sender vedkommende derefter tilbage. Tilbagekald refresh tokens, du ikke længere har brug for, ved revocation-endpointet.

Test og go-live

En ny app er i test: kun medlemmer af din organisation kan logge ind, og det står på samtykkeskærmen. For at gå live skal du få din organisation verificeret i Dazr Identity, tilføje en https://-omdirigerings-URI og anmode om en live-gennemgang i konsollen. Ændring af navn, formål, privatlivspolitik eller data for en live app kræver en ny gennemgang.

Overfør en app til en anden organisation

En app kan flyttes til en anden organisation, der er registreret hos Dazr, f.eks. efter et salg eller en omorganisering. En ejer eller administrator starter overførslen under Overfør app i appens indstillinger, i Dazr Identity eller i Sign-portalen. En ejer eller administrator af den anden organisation accepterer eller afviser den inden for 14 dage. Indtil da kan afsenderen annullere, og intet ændres.

Knappen

Brug formuleringen "Log ind med Dazr Identity" og Dazr-logoet som nedenfor. Du må ændre størrelsen, ikke logoet, farverne eller formuleringen. Link den til din egen route, der starter forløbet.

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

Dazr-skrifttypen. Knappen bruger Dazr-skrifttypen, som er gratis at bruge under SIL Open Font License. Hvor Dazr ikke er indlæst, bruges systemskrifttypen i stedet. Download Dazr-skrifttypen (ZIP med TTF- og WOFF2-filer) for at hoste den på dit eget website:

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

Et minimalt Node.js-eksempel

Node 18 eller nyere, ingen afhængigheder. En webserverapp med klienthemmelighed; for en offentlig klient fjerner du Authorization-headeren og sender client_id i body'en.

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

Grænser

Authorization- og token-endpointene er hastighedsbegrænsede. Får du HTTP 429, så vent og prøv igen; prøv ikke igen i en tæt løkke.

Dine forpligtelser

Din organisation er selvstændig dataansvarlig for de data, den modtager. Udviklervilkårene for Dazr Identity fastlægger, hvad du må gøre med dem, hvordan du holder dem sikre, og hvordan du indberetter brud. Spørgsmål: hello@dazr.eu.