openapi: 3.2.0
info:
title: Tvarka Atk Auth API
version: 1.3.0
description: 'Operations tagged Auth across 2 of this provider''s published API definitions: tvarka-atk-api-auth-openapi.yaml, tvarka-atk-api-openapi-original.json. Each path carries the servers of the definition it was published in.'
servers:
- url: https://atk.tvarka.pro/v1
description: 'Production. Sandbox runs on the same host - provider credentials issued with
environment=sandbox get the identical API against test-card material and are never billed.
'
security:
- basicAuth: []
tags:
- name: Auth
description: The eID authentication ceremony (`/v1/auth/*`).
paths:
/auth/requests:
servers:
- url: https://atk.tvarka.pro/v1
description: 'Production. Sandbox runs on the same host - provider credentials issued with
environment=sandbox get the identical API against test-card material and are never billed.
'
post:
tags:
- Auth
operationId: createAuthRequest
summary: Create an authentication request (server-to-server)
description: 'Called by the provider''s backend with HTTP Basic (`keyId:keySecret`). `audience` must be an
exact origin on the provider''s allow-list. Physical requests return a short-lived `clientToken`;
NFC requests return a pairing hint; Smart-ID/Mobile-ID requests remain entirely server-side.
'
x-codeSamples:
- lang: curl
label: Smart-ID
source: "curl -u \"$ATK_KEY_ID:$ATK_KEY_SECRET\" https://atk.tvarka.pro/v1/auth/requests \\\n -H 'Content-Type: application/json' \\\n -d '{\"audience\":\"https://app.example\",\"method\":\"smart_id\",\"personalCode\":\"39001010000\"}'\n"
- lang: curl
label: Mobile-ID
source: "curl -u \"$ATK_KEY_ID:$ATK_KEY_SECRET\" https://atk.tvarka.pro/v1/auth/requests \\\n -H 'Content-Type: application/json' \\\n -d '{\"audience\":\"https://app.example\",\"method\":\"mobile_id\",\"personalCode\":\"39001010000\",\"phoneNumber\":\"+37061234567\"}'\n"
security:
- basicAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AuthRequestCreate'
examples:
physicalAtk:
summary: Primary ATK desktop-reader method
value:
audience: https://app.example
method: physical
smartId:
summary: Optional Smart-ID method
value:
audience: https://app.example
method: smart_id
personalCode: '39001010000'
mobileId:
summary: Optional Mobile-ID method
value:
audience: https://app.example
method: mobile_id
personalCode: '39001010000'
phoneNumber: '+37061234567'
responses:
'202':
description: Request created; awaiting the ATK client or remote user authorization.
content:
application/json:
schema:
$ref: '#/components/schemas/AuthRequestCreated'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/RateLimited'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/auth/{requestId}/certificate:
servers:
- url: https://atk.tvarka.pro/v1
description: 'Production. Sandbox runs on the same host - provider credentials issued with
environment=sandbox get the identical API against test-card material and are never billed.
'
post:
tags:
- Auth
operationId: submitAuthCertificate
summary: Submit the card's authentication certificate; receive the DTBS + challenge
description: 'Called by the client with `Authorization: Bearer <clientToken>`. The API validates the
certificate (chain, revocation, purpose = authentication, key usage), builds the canonical
challenge, and returns the exact `dtbs` the card must sign plus a single-use `operationToken`.
Physical/NFC only; server-side methods do not call this endpoint.
'
security:
- clientToken: []
parameters:
- $ref: '#/components/parameters/RequestId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CertificateSubmit'
responses:
'200':
description: DTBS + challenge + operation token.
content:
application/json:
schema:
$ref: '#/components/schemas/CertificateChallenge'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'422':
$ref: '#/components/responses/Unprocessable'
/auth/{requestId}/complete:
servers:
- url: https://atk.tvarka.pro/v1
description: 'Production. Sandbox runs on the same host - provider credentials issued with
environment=sandbox get the identical API against test-card material and are never billed.
'
post:
tags:
- Auth
operationId: completeAuth
summary: Submit the raw card signature; receive the verified identity
description: 'Called by the client with `Authorization: Bearer <clientToken>` and the single-use
`operationToken` in the body. The API verifies the signature against the stored `dtbs` and the
certificate''s public key, enforces the identity-swap guard, and returns the verified identity
(and the optional `assertion` JWT). Retry-safe: a repeat with the same tuple returns the same
result; a different signature/cert after the token is claimed is rejected.
Physical/NFC only; server-side methods complete asynchronously.
'
security:
- clientToken: []
parameters:
- $ref: '#/components/parameters/RequestId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CompleteSubmit'
responses:
'200':
description: Authenticated - verified identity.
content:
application/json:
schema:
$ref: '#/components/schemas/AuthResult'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'422':
$ref: '#/components/responses/Unprocessable'
/auth/{requestId}:
servers:
- url: https://atk.tvarka.pro/v1
description: 'Production. Sandbox runs on the same host - provider credentials issued with
environment=sandbox get the identical API against test-card material and are never billed.
'
get:
tags:
- Auth
operationId: getAuthRequest
summary: Poll request status / fetch the result
description: 'Readable with the provider''s Basic credentials for every method, or with the request''s
`clientToken` for physical/NFC requests. Smart-ID/Mobile-ID providers poll this endpoint or
consume the terminal webhook.
'
security:
- basicAuth: []
- clientToken: []
parameters:
- $ref: '#/components/parameters/RequestId'
responses:
'200':
description: Current state.
content:
application/json:
schema:
$ref: '#/components/schemas/AuthRequestState'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
/auth/{requestId}/cancel:
servers:
- url: https://atk.tvarka.pro/v1
description: 'Production. Sandbox runs on the same host - provider credentials issued with
environment=sandbox get the identical API against test-card material and are never billed.
'
post:
tags:
- Auth
operationId: cancelAuthRequest
summary: Cancel a non-terminal request
security:
- basicAuth: []
- clientToken: []
parameters:
- $ref: '#/components/parameters/RequestId'
responses:
'200':
description: Cancelled.
content:
application/json:
schema:
$ref: '#/components/schemas/AuthRequestState'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
components:
schemas:
CertificateSubmit:
type: object
required:
- certificate
properties:
certificate:
type: string
format: byte
description: Base64 DER of the card's **authentication** certificate.
ErrorBody:
type: object
required:
- code
- message
properties:
code:
$ref: '#/components/schemas/ErrorCode'
message:
type: string
requestId:
type: string
format: uuid
retryable:
type: boolean
Method:
type: string
enum:
- physical
- nfc
- smart_id
- mobile_id
example: physical
description: physical = desktop reader; nfc = phone tap; smart_id/mobile_id = server-side remote eID.
AuthResult:
type: object
required:
- status
- method
- sig
- cert
properties:
status:
type: string
const: done
method:
$ref: '#/components/schemas/Method'
sig:
type: string
format: byte
description: 'The raw card signature over the audience-bound challenge, returned so the relying party can
verify identity independently of `assertion`. Audience-binding prevents cross-RP relay.
'
cert:
type: object
properties:
subject:
$ref: '#/components/schemas/CertSubject'
der:
type: string
format: byte
assertion:
type: string
description: 'Optional short-lived Tvarka-signed JWT (ES256), verifiable against the JWKS endpoint. Claims:
iss=atk.tvarka.pro, sub=<personal code>, aud=<audience>, iat, exp, jti, request_id
(binds the assertion to this ceremony), given_name, family_name, cert_fp,
authentication_method, amr. NOT OIDC.
Optional by contract - integrators may ignore it and verify the raw `sig` themselves.
'
Error:
type: object
required:
- error
properties:
error:
$ref: '#/components/schemas/ErrorBody'
SignatureAlgorithm:
type: string
description: Card-reported JWS-style algorithm.
enum:
- ES256
- ES384
AuthRequestCreated:
type: object
required:
- requestId
- method
- verificationCode
- status
- expiresAt
description: Physical requests return `clientToken`; NFC requests return `pairing`; remote methods return neither.
properties:
requestId:
type: string
format: uuid
method:
$ref: '#/components/schemas/Method'
verificationCode:
type: string
description: NFC anti-relay code, or the user verification code for Smart-ID/Mobile-ID.
clientToken:
type: string
description: Scoped bearer JWT for physical-card `/certificate` + `/complete`; absent for remote methods.
status:
$ref: '#/components/schemas/Status'
expiresAt:
type: string
format: date-time
challengeProfile:
type: string
const: atk-auth-v1
nonce:
type: string
description: 'Server-minted challenge nonce (see `Challenge`), so the provider backend can
re-derive `dtbs` and verify `sig` independently - no device relay needed.
'
pairing:
allOf:
- $ref: '#/components/schemas/PairingHint'
description: Present only when method == nfc.
AuthRequestState:
type: object
required:
- requestId
- method
- status
properties:
requestId:
type: string
format: uuid
method:
$ref: '#/components/schemas/Method'
status:
$ref: '#/components/schemas/Status'
verificationCode:
type: string
description: Present while a Smart-ID/Mobile-ID request is non-terminal.
nonce:
type: string
description: Server-minted challenge nonce (see `Challenge`) - enables independent `sig` verification from polling alone.
cert:
type: object
properties:
subject:
$ref: '#/components/schemas/CertSubject'
der:
type: string
format: byte
description: Base64 DER, present once status == done.
sig:
type: string
format: byte
assertion:
type: string
error:
$ref: '#/components/schemas/ErrorBody'
ErrorCode:
type: string
description: 'The full stable error-code catalog of the ATK API family (auth + sign + services);
each endpoint returns the subset that applies to it. By HTTP status:
400 invalid_json, missing_field, invalid_field, invalid_webhook_url, invalid_document_ref.
401 invalid_basic_credentials, invalid_client_token, client_token_expired,
operation_token_expired, invalid_download_token.
403 origin_not_allowed, ip_not_allowed, format_not_allowed, document_ref_host_not_allowed,
method_not_allowed, tenant_suspended, tier_forbidden.
404 request_not_found, pairing_not_found, document_not_ready.
409 request_terminal, operation_token_spent, identity_swap, idempotency_conflict,
service_in_progress, pairing_not_available, pairing_already_claimed.
410 pairing_expired. 413 document_too_large.
422 cert_invalid, untrusted_chain, cert_revoked, cert_expired, not_qualified,
cert_purpose_mismatch, signature_invalid, document_hash_mismatch, document_size_mismatch.
429 rate_limited. 500 assembly_failed. 502 document_ref_fetch_failed.
503 erasure_failed, pairing_service_unavailable, service_unavailable.
The catalog can grow in minor versions - fall back to HTTP status semantics
for codes you do not recognize.
'
enum:
- invalid_json
- missing_field
- invalid_field
- invalid_webhook_url
- invalid_document_ref
- invalid_basic_credentials
- invalid_client_token
- client_token_expired
- operation_token_expired
- invalid_download_token
- origin_not_allowed
- ip_not_allowed
- format_not_allowed
- document_ref_host_not_allowed
- method_not_allowed
- tenant_suspended
- tier_forbidden
- request_not_found
- pairing_not_found
- document_not_ready
- request_terminal
- operation_token_spent
- identity_swap
- idempotency_conflict
- service_in_progress
- pairing_not_available
- pairing_already_claimed
- pairing_expired
- document_too_large
- cert_invalid
- untrusted_chain
- cert_revoked
- cert_expired
- not_qualified
- cert_purpose_mismatch
- signature_invalid
- document_hash_mismatch
- document_size_mismatch
- rate_limited
- assembly_failed
- document_ref_fetch_failed
- erasure_failed
- pairing_service_unavailable
- service_unavailable
PairingHint:
type: object
properties:
pairingUrl:
type: string
format: uri
example: https://sign.tvarka.pro/atk-login/#PAIRING_TOKEN
deepLink:
type: string
example: tvarkasign://atk/pair/PAIRING_TOKEN
qrPayload:
type: string
example: https://sign.tvarka.pro/atk-login/#PAIRING_TOKEN
HashAlgOid:
type: string
description: Digest algorithm OID, curve-matched. SHA-256 = 2.16.840.1.101.3.4.2.1; SHA-384 = 2.16.840.1.101.3.4.2.2.
enum:
- 2.16.840.1.101.3.4.2.1
- 2.16.840.1.101.3.4.2.2
AuthRequestCreate:
type: object
required:
- audience
- method
additionalProperties: false
oneOf:
- title: Physical ATK
properties:
method:
const: physical
not:
anyOf:
- required:
- personalCode
properties:
personalCode: {}
- required:
- phoneNumber
properties:
phoneNumber: {}
- title: ATK over NFC
properties:
method:
const: nfc
not:
anyOf:
- required:
- personalCode
properties:
personalCode: {}
- required:
- phoneNumber
properties:
phoneNumber: {}
- title: Smart-ID
required:
- personalCode
properties:
method:
const: smart_id
not:
required:
- phoneNumber
properties:
phoneNumber: {}
- title: Mobile-ID
required:
- personalCode
- phoneNumber
properties:
method:
const: mobile_id
properties:
audience:
type: string
description: The relying-party origin (must be exactly on the provider's allow-list), e.g. https://www.epaslaugos.lt.
example: https://www.epaslaugos.lt
method:
$ref: '#/components/schemas/Method'
personalCode:
type: string
pattern: ^[0-9]{11}$
example: '39001010000'
description: Required for Smart-ID and Mobile-ID; rejected for physical/NFC.
phoneNumber:
type: string
pattern: ^\+3706[0-9]{7}$
example: '+37061234567'
description: Required for Mobile-ID, rejected for every other method.
message:
type: string
description: Optional short message the client may display to the user.
externalId:
type: string
description: Provider's own correlation id (echoed back, deduped).
webhookUrl:
type: string
format: uri
description: Optional public HTTPS webhook for the terminal status; private/non-global destinations are rejected.
CompleteSubmit:
type: object
required:
- signature
- signatureAlgorithm
- operationToken
properties:
signature:
type: string
format: byte
description: Base64 raw card signature over `dtbs` (auth key).
signatureAlgorithm:
$ref: '#/components/schemas/SignatureAlgorithm'
signatureEncoding:
$ref: '#/components/schemas/SignatureEncoding'
operationToken:
type: string
CertSubject:
type: object
description: Selected subject fields from the certificate. `serialNumber` holds `PNOLT-<personal code>`.
properties:
serialNumber:
type: string
example: PNOLT-39001010000
givenName:
type: string
sn:
type: string
description: Surname.
cn:
type: string
c:
type: string
example: LT
CertificateChallenge:
type: object
required:
- requestId
- dtbs
- dtbsHashAlgOid
- operationToken
- challenge
- cert
properties:
requestId:
type: string
format: uuid
dtbs:
type: string
format: byte
description: 'Base64 of the exact digest the card must sign (ECDSA prehashed). Built as the `atk-auth-v1`
construction - see `Challenge`.
'
dtbsHashAlgOid:
$ref: '#/components/schemas/HashAlgOid'
operationToken:
type: string
description: Single-use JWT bound to {tenant, requestId, purpose, certFp, dtbsHash}; the nonce is bound indirectly via dtbs. Returned at /complete.
verificationCode:
type: string
challenge:
$ref: '#/components/schemas/Challenge'
cert:
type: object
required:
- subject
description: 'Echoes only the parsed `subject` (the client itself just submitted the DER).
The certificate DER is returned at `/complete` and on GET once done.
'
properties:
subject:
$ref: '#/components/schemas/CertSubject'
Status:
type: string
description: 'Flat request status. Non-terminal - `pending`, `awaitingCard`, `awaitingCredentials`,
`finalizing`. Terminal success - `done`. Terminal failure -
`cancelled`/`timeout`/`deviceError`/`pinBlocked`/`cardRemoved`/`certInvalid`.
`awaitingCredentials` is SDK-reported (CAN/PIN entry is out-of-band).
'
enum:
- pending
- awaitingCard
- awaitingCredentials
- finalizing
- done
- cancelled
- timeout
- deviceError
- pinBlocked
- cardRemoved
- certInvalid
SignatureEncoding:
type: string
enum:
- P1363
- DER
default: P1363
description: Raw ECDSA signature encoding. P1363 = fixed-width r||s; DER = SEQUENCE(r, s). Informational - the server auto-detects and accepts either encoding regardless of this field.
Challenge:
type: object
description: 'Canonical `atk-auth-v1` anti-relay challenge. The server issues `nonce`; `dtbs` returned above =
`HASH( HASH(utf8(audience_origin)) || HASH(utf8(nonce)) )` with `HASH` matched to the auth-key
curve (P-256 -> SHA-256, P-384 -> SHA-384). Versioned - future profiles never reinterpret an
existing `dtbs`. This binding is what a relying party (e.g. ePaslaugos) audits.
'
required:
- profile
- audience
- nonce
properties:
profile:
type: string
const: atk-auth-v1
audience:
type: string
nonce:
type: string
description: Server-issued base64url challenge string (44-128 chars).
responses:
Conflict:
description: State/idempotency conflict (spent operation token, identity swap, terminal request).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
NotFound:
description: Unknown request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Forbidden:
description: Authorized but not permitted (origin/format/tenant).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
RateLimited:
description: Too many requests.
headers:
Retry-After:
schema:
type: integer
description: Seconds.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unprocessable:
description: Semantic reject (cert purpose/chain/revocation, dtbs mismatch, bad signature).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
BadRequest:
description: Malformed input.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Missing/invalid credentials or client token.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
ServiceUnavailable:
description: A required remote or erasure service is temporarily unavailable.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
parameters:
RequestId:
name: requestId
in: path
required: true
description: The `requestId` returned by `POST /auth/requests`.
schema:
type: string
format: uuid
securitySchemes:
basicAuth:
type: http
scheme: basic
description: 'Provider server-to-server credentials `keyId:keySecret`. Server-to-server only, also gated by the
provider''s server IP allow-list. Never placed in a browser/mobile client.
'
clientToken:
type: http
scheme: bearer
bearerFormat: JWT
description: 'Short-lived, per-request JWT (`ES256`) minted at request creation, scoped
`{tenant, requestId, purpose, method, allowedOrigins}`. Safe to hand to a browser/mobile client:
it cannot create requests, read tenant data, or touch other requests. For browser clients the API
enforces `Origin` against `allowedOrigins` (rejects cross-origin replay).
'
downloadToken:
type: apiKey
in: query
name: downloadToken
description: One-off token returned with a completed sign result, for fetching the signed document.
externalDocs:
description: Quickstart, SDKs, test data, pricing and lifecycle policy
url: https://atk.tvarka.pro/docs/
x-refined-from:
- tvarka-atk-api-auth-openapi.yaml
- tvarka-atk-api-openapi-original.json