Skip to main content

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:

  1. Go to dashboard.eidkit.ro
  2. Click "Înregistrează aplicația cu buletinul electronic"
  3. Scan the QR code with the EidKit app and tap your card
  4. Copy your client_id and client_secret — the secret is shown only once
Electronic ID card required

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

ScopeData returned
openidsub — stable pseudonym (SHA-256 of CNP, not the raw CNP)
profilename, given_name, family_name, birthdate
addressaddress.formatted — MAI-verified address, not self-declared
cei:documentNumber, series, expiry date, issuing authority
cei:cnpCNP extracted server-side from verified DG1 — not from the app payload
emailemail, email_verified: true — address verified via OTP in the EidKit app
cei:pictureFace photo (JPEG base64, ~33KB)
cei:signatureHandwritten 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.

Tokens

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:

CheckWhat it proves
DSC chain → MAI CSCAThe identity card was issued by the Romanian state
DG1 hash from SODThe identity data (including CNP) has not been modified since MAI signed it
DG14 hash from SODThe chip's public key (Q_chip) was signed by MAI — cannot be substituted
Chip ECDSA signatureThe physical card was present — cannot be forged without the chip
Server-side challengeThe signature is fresh — cannot be replayed
CE81 chain → MAI GenPKI Sub-CAThe 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.

Threat model note

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.