Skip to content

Authentication

TCS uses three authentication methods depending on the endpoint you are calling:

  • API Key — A static key sent in the X-API-Key header. Use this for issuer and verifier admin operations.
  • Bearer JWT — An access token sent in the Authorization header. Use this for Trust Registry DID operations and credential requests.
  • DPoP (Demonstrating Proof-of-Possession) — A proof-of-possession mechanism (RFC 9449) that binds the access token to the client’s key pair, preventing token theft and replay. DPoP is per-token, not a deployment-wide flag — whether a given /credential request must use DPoP is decided by the issuer’s signing profile and the token type, not a global switch. Required by HAIP v1 (X.509 / ES256 issuers); optional on the EdDSA / DID path. See the endpoint mapping below.

Most discovery and protocol endpoints are public and require no authentication.

Rendering diagram...

Terminal window
curl -X POST https://issuer.turingspace.co/v1/offers \
-H "X-API-Key: tcs_production_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4" \
-H "Content-Type: application/json" \
-d '{
"credential": {
"config_id": "TuringCerts_Standard_Credential_v2_sd_jwt",
"claims": { "credentialName": "University Degree" }
},
"flow": "pre-authorized"
}'

Send your API key in the X-API-Key header. You receive an API key when your organization is onboarded into the Trust Registry by the TCS team — there is no self-service apply endpoint; contact us to register. The server hashes your key with SHA-256 before storing it — the plaintext key is never persisted.

DetailValue
HeaderX-API-Key: {your_api_key}
Formattcs_{environment}_{48-hex} (e.g., tcs_production_...)
Obtained fromTrust Registry onboarding (handled by the TCS team)
StorageSHA-256 hash only; plaintext is not stored
ScopeIssuer admin operations, verifier admin operations
MethodEndpointService
POST/v1/offersCredential Issuer
POST/v1/verifier/authorization-requestVerifier Service
GET/v1/verifier/result/:sessionIdVerifier Service
StatusMessageCause
401API Key is requiredMissing X-API-Key header
401Invalid API KeyKey not found or hash mismatch
401Account is not approvedKey is valid but the account is pending approval

Terminal window
# Trust Registry DID operation (token from login)
curl -X POST https://trust-registry.turingspace.co/v1/did \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." \
-H "Content-Type: application/json" \
-d '{ "did": "did:key:z6Mk..." }'
Terminal window
# Credential request (token from token endpoint)
curl -X POST https://issuer.turingspace.co/credential \
-H "Authorization: Bearer eyJhbGciOiJFUzI1NiJ9..." \
-H "Content-Type: application/json" \
-d '{
"credential_configuration_id": "EmployeeCredential_sd_jwt",
"proofs": {
"jwt": ["eyJhbGciOiJFZERTQSIs...proof-jwt"]
}
}'

Send the access token in the Authorization: Bearer {token} header. There are two distinct JWT sources depending on the operation:

PurposeToken SourceHow to Obtain
Trust Registry DID operationsLogin tokenPOST /v1/auth/login with uuid and password
Credential requestsAccess tokenPOST /v1/token with pre-authorized code
MethodEndpointServiceToken Source
POST/v1/didTrust RegistryLogin token
POST/v1/did/importTrust RegistryLogin token
POST/credentialCredential IssuerToken endpoint
StatusMessageCause
401invalid_tokenAccess token is invalid or expired
403ForbiddenAccount not approved for this operation

3. DPoP (Demonstrating Proof-of-Possession)

Section titled “3. DPoP (Demonstrating Proof-of-Possession)”
Terminal window
# Step 1: Create a DPoP proof JWT
# Header
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": { /* sender's public key */ }
}
# Payload
{
"jti": "unique-id-abc123",
"htm": "POST",
"htu": "https://issuer.turingspace.co/credential",
"iat": 1711411200,
"ath": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo"
}
# Step 2: Send both headers
curl -X POST https://issuer.turingspace.co/credential \
-H "Authorization: DPoP eyJhbGciOiJFUzI1NiJ9..." \
-H "DPoP: eyJ0eXAiOiJkcG9wK2p3dCJ9..." \
-H "Content-Type: application/json" \
-d '{
"credential_configuration_id": "EmployeeCredential_sd_jwt",
"proofs": {
"jwt": ["eyJhbGciOiJFZERTQSIs...proof-jwt"]
}
}'

DPoP (RFC 9449) binds the access token to the client’s key pair, so a stolen token cannot be used by another party. It is required by HAIP v1 and recommended for all credential endpoint interactions. Two headers are required: Authorization: DPoP {access_token} and DPoP: {proof_jwt}.

Header:

FieldValueDescription
typdpop+jwtFixed type for DPoP proofs
algES256 or EdDSASigning algorithm
jwk{ ... }Sender’s public key in JWK format

Payload:

FieldRequiredDescription
jtiYesUnique identifier for replay protection
htmYesHTTP method of the request (e.g., POST)
htuYesHTTP URL of the request (without query/fragment)
iatYesIssued-at timestamp (seconds since epoch)
athYesBase64url-encoded SHA-256 hash of the access token
nonceConditionalServer-provided nonce, required after receiving DPoP-Nonce header
ParameterValue
Supported algorithmsEdDSA, ES256
Nonce expiry300 seconds
JTI expiry300 seconds
IAT max age300 seconds
Clock skew tolerance60 seconds (future)

When the server requires a nonce, it follows this exchange:

Rendering diagram...
  1. Send your request with a DPoP proof that has no nonce field.
  2. The server returns HTTP 400 with error use_dpop_nonce and a DPoP-Nonce response header.
  3. Rebuild your DPoP proof JWT with the nonce field set to the value from the DPoP-Nonce header.
  4. Retry the request with the updated DPoP proof.
StatusError CodeCause
400invalid_dpop_proofDPoP proof structure, signature, or ath validation failed
400use_dpop_nonceServer requires a DPoP nonce — check the DPoP-Nonce response header

These endpoints require no authentication. They serve discovery metadata, protocol exchanges, and public registration.

MethodEndpointService
GET/v1/offers/:offerIdCredential Issuer
GET/.well-known/openid-credential-issuer/:tenantCredential Issuer
GET/.well-known/oauth-authorization-serverAuthorization Server
POST/v1/tokenAuthorization Server
POST/v1/nonceCredential Issuer
POST/v1/parAuthorization Server
GET/v1/authorizeAuthorization Server
POST/v1/authorize/submitAuthorization Server
GET/v1/verifier/request/:requestUriIdVerifier Service
POST/v1/verifier/presentationVerifier Service
GET/schemas/:type/:versionSchema Registry
POST/v1/auth/loginTrust Registry
GET/.well-known/did-configuration.json/:tenantTrust Registry

MethodPathAuth TypeDPoP required whenService
POST/v1/offersAPI KeyCredential Issuer
GET/v1/offers/:offerIdPublicCredential Issuer
POST/credentialBearer JWT or DPoP-boundIssuer registered on the HAIP / X.509 / ES256 path; optional on the EdDSA / DID pathCredential Issuer
GET/.well-known/openid-credential-issuer/:tenantPublicCredential Issuer
GET/.well-known/oauth-authorization-serverPublicAuthorization Server
POST/v1/tokenPublicDPoP proof required to mint a DPoP-bound access token (HAIP path)Authorization Server
POST/v1/noncePublicCredential Issuer
POST/v1/parPublicAuthorization Server
GET/v1/authorizePublicAuthorization Server
POST/v1/authorize/submitPublicAuthorization Server
POST/v1/verifier/authorization-requestAPI KeyVerifier Service
GET/v1/verifier/result/:sessionIdAPI KeyVerifier Service
GET/v1/verifier/request/:requestUriIdPublicVerifier Service
POST/v1/verifier/presentationPublicVerifier Service
GET/schemas/:type/:versionPublicSchema Registry
POST/v1/auth/loginPublicTrust Registry
POST/v1/didBearer JWTTrust Registry
POST/v1/did/importBearer JWTTrust Registry
GET/.well-known/did-configuration.json/:tenantPublicTrust Registry

StatusErrorDescription
400invalid_grantPre-authorized code is expired or has already been used
400invalid_dpop_proofDPoP proof structure, signature, or ath validation failed
400use_dpop_nonceServer requires a DPoP nonce — check the DPoP-Nonce response header
401invalid_tokenAccess token is invalid or expired
401invalid_token (DPoP path)Token is structurally valid but the endpoint requires DPoP binding — the issuer is registered on the HAIP / X.509 / ES256 path. Re-request with Authorization: DPoP <token> plus a DPoP: proof header (see §3).
401API Key is requiredMissing X-API-Key header
401Invalid API KeyAPI key not found or hash mismatch
401Account is not approvedAPI key is valid but the account is pending approval
403ForbiddenAccount not approved for this operation