Tvarka ID developer guide

Add Lithuanian eID login to your application with OpenID Connect. Your application redirects the person to Tvarka ID, then receives an authorization code and exchanges it for tokens.

All Tvarka APIs · Discovery · Public signing keys

1. Register your application

Request access through Tvarka's contact page. Provide the application name, billing company, exact HTTPS redirect URIs, logout redirect URIs, requested signing-key algorithm, login methods and scopes. Choose your client authentication method below. Send only public JWKS for key-based authentication; keep private keys in your own system.

Registration is operator-managed. You receive a client ID, the agreed configuration and, where applicable, a one-time client secret. Use these OIDC credentials; Sign, Due Diligence and ATK API keys belong to separate products. Request lt_personal_code only when your written legal basis has been approved. Register test redirect URIs explicitly; localhost HTTP is accepted for registered development callbacks, while ordinary redirects use HTTPS.

2. Configure your OIDC client

SettingValue
Issuerhttps://id.tvarka.pro
Discoveryhttps://id.tvarka.pro/.well-known/openid-configuration
FlowAuthorization code; response_type=code
PKCES256, mandatory for public and confidential clients
Request bindingFresh state and nonce, both required
Scopesopenid, optional profile; approved lt_personal_code
ID token algorithmES256 by default; RS256 when registered
Code lifetime120 seconds; single use
ID/access token lifetime300 seconds; access token is opaque

Use a maintained OIDC client library and the discovered endpoints. Generate new state, nonce and PKCE verifier for every authorization. Persist them with the initiating browser session until the callback is consumed. Keep confidential credentials on your backend.

Authorization request example

import base64
import hashlib
import secrets
from urllib.parse import urlencode


def new_login(client_id, redirect_uri):
    verifier = secrets.token_urlsafe(48)
    challenge = base64.urlsafe_b64encode(
        hashlib.sha256(verifier.encode('ascii')).digest()
    ).decode('ascii').rstrip('=')
    pending = {'state': secrets.token_urlsafe(32),
               'nonce': secrets.token_urlsafe(32), 'verifier': verifier}
    query = {'client_id': client_id, 'redirect_uri': redirect_uri,
             'response_type': 'code', 'scope': 'openid profile',
             'state': pending['state'], 'nonce': pending['nonce'],
             'code_challenge': challenge, 'code_challenge_method': 'S256'}
    # Store pending in the initiating browser's server-side session. Do not
    # expose the verifier in the authorization URL or in application logs.
    return 'https://id.tvarka.pro/authorize?' + urlencode(query), pending

Redirect the browser to the returned URL. The login page offers the methods enabled for your client: Smart-ID, Mobile-ID and/or the Lithuanian ID card. Each authorization requires a fresh eID act; a remembered browser helps select the method but does not silently authenticate. Card-on-phone availability depends on the enabled app integration and a compatible published client.

3. Handle the callback and exchange the code

First compare the returned state with the pending session value using a constant-time comparison, then consume that pending state once. Handle an error response before using code. Send the code to the discovered token endpoint with the same exact redirect URI and original PKCE verifier.

POST /token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64(client_id:client_secret)>

grant_type=authorization_code&code=RETURNED_CODE&redirect_uri=REGISTERED_URI&code_verifier=ORIGINAL_VERIFIER

The header above is for client_secret_basic. A public client instead sends client_id in the form without a secret. For private_key_jwt, send client_id, client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer and the assertion. Its iss and sub identify the client; aud is the token endpoint or issuer, exp is short-lived and jti is fresh. Sign with the algorithm/key registered for this client. Never retry a consumed code as a new login; start a fresh authorization after a failed or expired exchange.

4. Validate tokens and use claims

Before starting your application session, validate the ID token with the issuer's JWKS and your registered algorithm. Check its signature, exact iss, your client in aud, expiry, issuance time and the original nonce; apply the standard authorized-party checks if multiple audiences are present. Select keys by kid; refresh the trusted issuer's JWKS on an unknown key, then fail if validation still fails. Never accept unsigned tokens or token-supplied key URLs. Follow OIDC ID-token validation.

Call GET /userinfo with Authorization: Bearer ACCESS_TOKEN for the permitted profile. Verify that its sub matches the ID token. The access token is for UserInfo, not other Tvarka APIs. This flow issues no refresh token: start another authorization when you need fresh authentication.

Logout and revocation

End your own application session on logout. You can send the access token to POST /revoke with the client's registered authentication method and token_type_hint=access_token. RP-initiated /logout accepts an ID-token hint and a registered post_logout_redirect_uri; an unregistered return target is refused. Tvarka ID has no reusable IdP login session to terminate. Browser recognition and your application's own session are separate.

Pricing and access problems

Successful Smart-ID/Mobile-ID login costs €0.12 + VAT; ID-card login costs €0.08 + VAT. The registered billing entity pays. Failed or cancelled logins are free. Funding is checked before the PIN flow.

For support, provide the time, client ID, endpoint and error code. Keep secrets, authorization codes, access/ID tokens and personal identifiers out of support messages.

Production acceptance includes a registered relying party and a real login with each required method. Local protocol tests and public discovery do not establish OpenID Foundation conformance certification. PKCE reference: RFC 7636.