Client erhalten
Nennen Sie uns Ihre Redirect-URI(s), etwa https://your-app.example/callback. Sie erhalten client_id und client_secret; Ihre Relying-Party-Angaben kommen ins Registrierungszertifikat.
Entwickler · OpenID Connect · EU-Wallet
Das CodeB Relying Party Gateway ist für Ihre Anwendung ein normaler OpenID-Connect-Anbieter. Sie fügen einen Parameter hinzu und prüfen einen Claim – die Wallet-Protokolle bleiben außerhalb Ihres Codes.
acr_values=urn:codeb:acr:eudi-wallet im Authorization-Request mit. 2. Akzeptieren Sie nur ein geprüftes id_token, dessen acr urn:codeb:acr:eudi-wallet oder urn:codeb:acr:eudi-wallet-linked ist.Authorization code + PKCE (S256) · acr / amr · RS256 id_token · JWKS · RP-initiated logout
Geschlossene Beta, im Test. Nicht zertifiziert.
https://phone.aloaha.com (Discovery unter /.well-known/openid-configuration)state, nonceacr_values=urn:codeb:acr:eudi-wallet (eID-Ausweis: urn:codeb:acr:eidcard)acr im id_token; amr enthält vc (Wallet) bzw. sc + pop (Ausweis)openid, profile, email (weitere: phone, address)client_secret_basic, client_secret_post oder öffentlicher Client mit PKCENennen Sie uns Ihre Redirect-URI(s), etwa https://your-app.example/callback. Sie erhalten client_id und client_secret; Ihre Relying-Party-Angaben kommen ins Registrierungszertifikat.
Starten Sie einen Authorization-Code-Ablauf mit PKCE (S256), state und nonce und ergänzen Sie acr_values=urn:codeb:acr:eudi-wallet.
Tauschen Sie auf Ihrem Server den Code am Token-Endpunkt gegen Tokens und prüfen Sie das id_token: Signatur, Aussteller, Empfänger, Ablauf, Nonce.
Akzeptieren Sie die Anmeldung nur, wenn acr gleich urn:codeb:acr:eudi-wallet oder urn:codeb:acr:eudi-wallet-linked ist. Fertig.
Kompakte Beispiele; vollständige, getestete Apps für sechs Umgebungen (auch Java und Go) stehen im Integrations-Kochbuch. Ersetzen Sie YOUR_CLIENT_ID, your-app.example usw. durch Ihre Werte.
Alle Endpunkte und Schlüssel stehen in einem Dokument; jede OpenID-Connect-Bibliothek liest es.
shell# Every endpoint comes from the discovery document.
curl -s https://phone.aloaha.com/.well-known/openid-configuration
# The parts you need:
# authorization_endpoint https://phone.aloaha.com/oauth2/v1/authorize
# token_endpoint https://phone.aloaha.com/oauth2/v1/token
# userinfo_endpoint https://phone.aloaha.com/oauth2/v1/userinfo
# end_session_endpoint https://phone.aloaha.com/oauth2/v1/logout
# jwks_uri (keys to check the id_token signature)
# code_challenge_methods_supported ["S256"]
# acr_values_supported ... urn:codeb:acr:eudi-wallet, urn:codeb:acr:eudi-wallet-linked, urn:codeb:acr:eidcardEin einfacher Link auf Ihren eigenen Server. Für das offizielle Aussehen gibt es das Button- und QR-Kit.
html<!-- The button only links to YOUR server; your server starts the sign-in. -->
<a href="/login" class="eudi-signin">Sign in with your EU Digital Identity Wallet</a>
<style>
.eudi-signin { display:inline-block; padding:.75rem 1.2rem; border-radius:10px;
background:#003399; color:#fff; font:600 1rem system-ui, sans-serif;
text-decoration:none; }
.eudi-signin:focus-visible { outline:3px solid #ffcc00; outline-offset:2px; }
</style>Zum Verstehen des Ablaufs oder für Umgebungen ohne OpenID-Connect-Bibliothek.
http + shell# 1. Send the browser to the authorization endpoint (one line, URL-encoded):
https://phone.aloaha.com/oauth2/v1/authorize
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https%3A%2F%2Fyour-app.example%2Fcallback
&scope=openid%20profile%20email
&acr_values=urn%3Acodeb%3Aacr%3Aeudi-wallet # straight to the wallet sign-in
&state=RANDOM_STATE
&nonce=RANDOM_NONCE
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256
# 2. The browser comes back to https://your-app.example/callback?code=...&state=...
# Check state, then exchange the code on your server:
curl -s https://phone.aloaha.com/oauth2/v1/token \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code=THE_CODE \
-d redirect_uri=https://your-app.example/callback \
-d code_verifier=THE_PKCE_VERIFIER
# 3. Validate the id_token (signature via jwks_uri, iss, aud, exp, nonce) and
# accept the sign-in only if acr is urn:codeb:acr:eudi-wallet
# or urn:codeb:acr:eudi-wallet-linked.// npm install express express-session openid-client@5
const express = require('express');
const session = require('express-session');
const { Issuer, generators } = require('openid-client');
const ISSUER = 'https://phone.aloaha.com';
const BASE = process.env.APP_BASE_URL; // e.g. https://your-app.example
const WALLET_ACRS = ['urn:codeb:acr:eudi-wallet', 'urn:codeb:acr:eudi-wallet-linked'];
const app = express();
app.use(session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false }));
let client;
async function codeb() {
if (!client) {
const issuer = await Issuer.discover(ISSUER);
client = new issuer.Client({ client_id: process.env.CODEB_CLIENT_ID,
client_secret: process.env.CODEB_CLIENT_SECRET, redirect_uris: [BASE + '/callback'], response_types: ['code'] });
}
return client;
}
app.get('/login', async (req, res, next) => {
try {
const c = await codeb(), verifier = generators.codeVerifier();
req.session.oidc = { verifier, state: generators.state(), nonce: generators.nonce() };
res.redirect(c.authorizationUrl({ scope: 'openid profile email', acr_values: WALLET_ACRS[0],
code_challenge: generators.codeChallenge(verifier), code_challenge_method: 'S256',
state: req.session.oidc.state, nonce: req.session.oidc.nonce }));
} catch (e) { next(e); }
});
app.get('/callback', async (req, res, next) => {
try {
const c = await codeb(), o = req.session.oidc || {};
const tokens = await c.callback(BASE + '/callback', c.callbackParams(req),
{ code_verifier: o.verifier, state: o.state, nonce: o.nonce }); // checks signature, iss, aud, exp, nonce
const claims = tokens.claims();
if (!WALLET_ACRS.includes(claims.acr)) return res.status(403).send('EU Digital Identity Wallet sign-in required');
req.session.regenerate(err => {
if (err) return next(err);
req.session.user = { sub: claims.sub, given_name: claims.given_name, family_name: claims.family_name };
res.redirect('/');
});
} catch (e) { next(e); }
});
app.listen(3000);# pip install flask authlib requests
import os
from flask import Flask, redirect, session
from authlib.integrations.flask_client import OAuth
WALLET_ACRS = ("urn:codeb:acr:eudi-wallet", "urn:codeb:acr:eudi-wallet-linked")
BASE = os.environ["APP_BASE_URL"] # e.g. https://your-app.example
app = Flask(__name__)
app.secret_key = os.environ["SESSION_SECRET"]
oauth = OAuth(app)
oauth.register("codeb",
client_id=os.environ["CODEB_CLIENT_ID"], client_secret=os.environ["CODEB_CLIENT_SECRET"],
server_metadata_url="https://phone.aloaha.com/.well-known/openid-configuration",
client_kwargs={"scope": "openid profile email", "code_challenge_method": "S256"})
@app.route("/login")
def login():
return oauth.codeb.authorize_redirect(BASE + "/callback", acr_values=WALLET_ACRS[0])
@app.route("/callback")
def callback():
token = oauth.codeb.authorize_access_token() # checks state, nonce, signature, iss, aud, exp
claims = token["userinfo"]
if claims.get("acr") not in WALLET_ACRS:
return "EU Digital Identity Wallet sign-in required", 403
session.clear()
session["user"] = {"sub": claims["sub"], "given_name": claims.get("given_name")}
return redirect("/")<?php
// composer require jumbojett/openid-connect-php
require __DIR__ . '/vendor/autoload.php';
use Jumbojett\OpenIDConnectClient;
session_start();
$oidc = new OpenIDConnectClient('https://phone.aloaha.com',
getenv('CODEB_CLIENT_ID'), getenv('CODEB_CLIENT_SECRET'));
$oidc->setRedirectURL(getenv('APP_BASE_URL') . '/callback.php');
$oidc->addScope(['openid', 'profile', 'email']);
$oidc->setCodeChallengeMethod('S256');
$oidc->addAuthParam(['acr_values' => 'urn:codeb:acr:eudi-wallet']);
$oidc->authenticate(); // redirects to CodeB, and on the way back validates the id_token
$acr = $oidc->getVerifiedClaims('acr');
if (!in_array($acr, ['urn:codeb:acr:eudi-wallet', 'urn:codeb:acr:eudi-wallet-linked'], true)) {
http_response_code(403);
exit('EU Digital Identity Wallet sign-in required');
}
session_regenerate_id(true);
$_SESSION['user'] = ['sub' => $oidc->getVerifiedClaims('sub'),
'given_name' => $oidc->getVerifiedClaims('given_name')];
header('Location: /');// ASP.NET Core 8: dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
var walletAcrs = new[] { "urn:codeb:acr:eudi-wallet", "urn:codeb:acr:eudi-wallet-linked" };
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(o =>
{
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(o =>
{
o.Authority = "https://phone.aloaha.com";
o.ClientId = builder.Configuration["CodeB:ClientId"];
o.ClientSecret = builder.Configuration["CodeB:ClientSecret"];
o.ResponseType = "code";
o.UsePkce = true;
o.CallbackPath = "/callback";
o.MapInboundClaims = false; // keep "acr", "sub", "given_name" as they are
o.Scope.Clear(); o.Scope.Add("openid"); o.Scope.Add("profile"); o.Scope.Add("email");
o.Events.OnRedirectToIdentityProvider = ctx =>
{
ctx.ProtocolMessage.AcrValues = walletAcrs[0]; // straight to the wallet sign-in
return Task.CompletedTask;
};
o.Events.OnTokenValidated = ctx =>
{
var acr = ctx.Principal?.FindFirst("acr")?.Value;
if (acr is null || !walletAcrs.Contains(acr)) ctx.Fail("EU Digital Identity Wallet sign-in required");
return Task.CompletedTask;
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", (HttpContext h) => $"Hello {h.User.FindFirst("given_name")?.Value}").RequireAuthorization();
app.Run();Dekodiertes id_token nach einer Wallet-Anmeldung (Beispielwerte). Welche persönlichen Angaben Sie erhalten, hängt von den Scopes ab und davon, was der Betreiber für Ihren Client freigibt.
json{
"iss": "https://phone.aloaha.com",
"aud": "YOUR_CLIENT_ID",
"sub": "eu_3f8a1d7e", // stable per person for your client's tenant
"acr": "urn:codeb:acr:eudi-wallet", // HOW the person signed in: check it
"amr": ["vc"], // vc = verifiable credential from the wallet
"auth_time": 1792490000,
"nonce": "RANDOM_NONCE",
"given_name": "Erika", // with scope profile, from the presented PID
"family_name": "Mustermann",
"birthdate": "1964-08-12",
"email": "erika@example.com" // with scope email, when the person has one
}# Same client, same code - only the acr changes:
acr_values=urn:codeb:acr:eidcard
# and accept the sign-in only if the id_token says
# "acr": "urn:codeb:acr:eidcard" (amr contains "sc" and "pop")
#
# The user signs in with the authentication key of a national eID card,
# read over NFC by the CodeB Wallet app.# After clearing your own session, sign the person out at CodeB too
# (RP-initiated logout); the return URL must be registered for your client:
https://phone.aloaha.com/oauth2/v1/logout
?client_id=YOUR_CLIENT_ID
&post_logout_redirect_uri=https%3A%2F%2Fyour-app.example%2FDer Endpunkt hinter dem Link „Löschung beantragen“, den Wallets zu einem Relying Party anzeigen.
shell# A person (or you, as data controller) asks CodeB to delete the wallet sign-in
# records about them. Public endpoint, no sign-in; reviewed by an operator.
curl -s -X POST 'https://phone.aloaha.com/oidc.ashx?action=data-deletion-request' \
-H 'Content-Type: application/json' \
-d '{
"sub": "eu_3f8a1d7e",
"requester_type": "controller",
"contact": "privacy@your-app.example",
"reason": "Customer closed the account"
}'
# -> 202 { "request_id": "...", "status": "queued", "eta_days": 30 }Jeder verbreitete OpenID-Connect-Client funktioniert: openid-client (Node.js), Authlib (Python), jumbojett/openid-connect-php, Microsoft.AspNetCore.Authentication.OpenIdConnect, Spring Security, go-oidc. Nichts CodeB-Spezifisches ist nötig.
Weil ein Browser von jeder Anmeldung zu Ihrem Callback zurückgeschickt werden kann. acr_values fordert die Wallet-Anmeldung an; der acr-Claim im geprüften id_token belegt, dass sie stattfand (OpenID Connect Core 3.1.2.1). Jedes Beispiel prüft ihn.
Ja, als öffentlicher Client mit PKCE (ohne Client-Secret), siehe das OIDC-Anmelde-Kochbuch. Für den Produktivbetrieb ist ein Backend mit Client-Secret die sicherere Wahl.
Die Person hat ihre Wallet vorgelegt und diese Vorlage wurde einem bestehenden Konto auf der Seite zugeordnet; sonst lautet der acr urn:codeb:acr:eudi-wallet. Behandeln Sie beide als Wallet-Anmeldung.
Mit der CodeB Wallet oder der EU-Referenz-Wallet und Testnachweisen, dazu der Verifier-Selbsttest, der zeigt, welche HAIP-Prüfungen eine Wallet besteht.
Ja: Testclients registrieren wir zuerst auf unserem Testmandanten. Fragen Sie uns mit Ihrer Redirect-URI nach einem Testclient.
Schicken Sie uns Ihre Redirect-URI und den Namen Ihres Dienstes; Sie erhalten Client-ID und Secret für den Testmandanten.