EidKit SSO Integration
EidKit SSO is a standard OIDC identity provider. You integrate it exactly like Google Sign-In or Apple Sign-In — a Client ID, a Client Secret, a redirect URI.
Getting credentials
Credentials are issued instantly via the Developer Portal — no forms, no email.
What you need:
- A Romanian electronic identity card (CEI) with NFC chip
- The EidKit app installed on your phone
Steps:
- Go to dashboard.eidkit.ro
- Click "Înregistrează aplicația cu buletinul electronic"
- Scan the QR code with the EidKit app and tap your card
- Copy your
client_idandclient_secret— the secret is shown only once
CEI authentication is required both to register in the portal and to test your integration. If you don't have an electronic ID card yet, contact hello@eidkit.ro.
What you get
- Client ID and Client Secret — generated instantly
- Issuer URL:
https://idp.eidkit.ro - OIDC autodiscovery at
https://idp.eidkit.ro/.well-known/openid-configuration - Cryptographically verified data from MAI — not self-reported by the user
Available scopes
| Scope | Data returned |
|---|---|
openid | sub — stable pseudonym (SHA-256 of CNP, not the raw CNP) |
profile | name, given_name, family_name, birthdate |
address | address.formatted — MAI-verified address, not self-declared |
cei:document | Number, series, expiry date, issuing authority |
cei:cnp | CNP extracted server-side from verified DG1 — not from the app payload |
email | email, email_verified: true — address verified via OTP in the EidKit app |
cei:picture | Face photo (JPEG base64, ~33KB) |
cei:signature | Handwritten signature image (JPEG base64, ~3.5KB) |
The openid and profile scopes require no PIN. The address scope requires the authentication PIN — the user enters it in the EidKit app before tapping their card. Images are opt-in for specific use cases (e.g. insurance, HR).
The email scope
When you request the email scope, the user is prompted in the EidKit app to enter their email address during authentication. EidKit sends a 6-digit OTP to that address and confirms receipt before issuing the token.
Behaviour on subsequent logins:
- Same email → no new OTP — authentication continues automatically
- Changed email → new OTP flow
The user can opt to remember the address for future logins (skips the prompt) or delete it at any time from the "Saved Data" section of the app.
Important: the email address is user-declared and OTP-verified — it is not extracted from the card.
Example JWT with the email scope:
{
"sub": "a3f7bc9d...",
"name": "CĂTĂLIN TOMA",
"email": "catalin@example.com",
"email_verified": true,
"iss": "https://idp.eidkit.ro",
"aud": "your-client-id"
}
Registration and login — the same flow
There is no difference between login and registration. The sub in the JWT is a stable, unique pseudonym per person. The first card tap creates the account automatically.
const { sub, name, given_name, family_name, address } = jwtPayload;
let user = await db.users.findOne({ eidkitSub: sub });
if (!user) {
// First authentication = automatic registration
user = await db.users.create({
eidkitSub: sub,
name: name,
givenName: given_name,
familyName: family_name,
address: address?.formatted,
createdAt: new Date(),
});
}
// Subsequent authentications = login with the same sub
await session.create({ userId: user.id });
JWT verification without network calls
After initial setup, token signatures can be verified locally using the public JWKS — no call to EidKit per request:
GET https://idp.eidkit.ro/.well-known/jwks.json
Cache the public keys and verify the JWT signature locally on every request.
Example JWT — typical scopes (openid profile address)
{
"sub": "a3f7bc9d...",
"name": "CĂTĂLIN TOMA",
"given_name": "CĂTĂLIN",
"family_name": "TOMA",
"birthdate": "1985-03-15",
"address": {
"formatted": "Str. Exemplu Nr. 1, Timișoara, Timiș"
},
"iss": "https://idp.eidkit.ro",
"aud": "your-client-id"
}
Session management
EidKit issues an ID token once — there are no refresh tokens in the standard configuration. You manage the user session with your own cookie or server-side session. If you need re-verification (high-security flows), the user taps their card again.
Use the ID token — it contains everything you need. The access token has no EidKit API to use it against.
Security guarantees
Unlike a traditional OIDC provider that issues tokens on the basis of a password, EidKit SSO will not issue any token unless all of the following verifications pass:
| Check | What it proves |
|---|---|
| DSC chain → MAI CSCA | The identity card was issued by the Romanian state |
| DG1 hash from SOD | The identity data (including CNP) has not been modified since MAI signed it |
| DG14 hash from SOD | The chip's public key (Q_chip) was signed by MAI — cannot be substituted |
| Chip ECDSA signature | The physical card was present — cannot be forged without the chip |
| Server-side challenge | The signature is fresh — cannot be replayed |
| CE81 chain → MAI GenPKI Sub-CA | The chip's authentication key was issued by MAI |
| CA binding (ECDH, BSI TR-03110) | The chip that signed CE81 holds exactly the Q_chip key from the identity — split-proof attack with a different name is impossible |
PIN proof: Active Authentication on the CEI chip requires verification of the 4-digit auth PIN before CE81 can sign the challenge. A valid AA signature implies the user knows the PIN of the physical card.
The server extracts the CNP directly from verified DG1 bytes — it does not accept the CNP from the app payload.
The residual same-name split-proof attack requires physical access to the victim's card, knowledge of their CAN, and the same legal name. Every authentication permanently logs the CE81 serial number (hashed with a server secret) — MAI holds the SERIALNUMBER→CNP mapping, enabling forensic identification of any attacker. This is a detectable insider threat, not a scalable attack vector.