Skip to content

Authentication

The platform identity provider is Keycloak 26, served at https://tfs-auth.karyabilisim.com with the tfs realm. APIs read identity from the Authorization: Bearer <token> header:

GET /api/v1/contracts/ctr_01J8ZK4T2M HTTP/1.1
Host: tfs-api.karyabilisim.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Endpoint URL
Discovery https://tfs-auth.karyabilisim.com/realms/tfs/.well-known/openid-configuration
authorization_endpoint https://tfs-auth.karyabilisim.com/realms/tfs/protocol/openid-connect/auth
token_endpoint https://tfs-auth.karyabilisim.com/realms/tfs/protocol/openid-connect/token

Prefer resolving endpoints from the discovery document instead of hard-coding them.

Single-page applications (e.g. the ops console’s tfs-ops public client) carry no client secret; they use Authorization Code + PKCE (S256):

  1. The app generates a random code_verifier and derives the code_challenge from its SHA-256 hash.
  2. The user is redirected to the authorization_endpoint (response_type=code, code_challenge_method=S256).
  3. After login, Keycloak returns a short-lived code to the app.
  4. The app posts code + code_verifier to the token_endpoint to obtain the access token; Keycloak verifies that the verifier hashes to the challenge.

Access tokens are issued with the tfs-core-api audience. Realm roles are the platform permission codes (e.g. party:create, contract:approve); APIs read permissions from the token’s realm_access.roles claim:

{
"aud": "tfs-core-api",
"azp": "tfs-ops",
"preferred_username": "ops-user-1",
"realm_access": {
"roles": ["party:create", "contract:approve", "report:prepare"]
}
}

Endpoints are permission-gated; e.g. contract approval requires contract:approve. On four-eyes endpoints the preparer cannot also approve — otherwise 409 FOUR_EYES_SAME_USER.

For integration tests you can obtain a token without the browser flow:

Terminal window
curl -s "https://tfs-auth.karyabilisim.com/realms/tfs/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "client_id=tfs-ops" \
-d "username=ops-user-1" \
-d "password=<password>"

Put the access_token from the response into the Authorization: Bearer header.

Developer headers — refused in any deployed environment

Section titled “Developer headers — refused in any deployed environment”

x-dev-user, x-dev-permissions, x-dev-party and x-dev-partner are for local development only. Both API surfaces refuse them whenever an identity provider is configured (OIDC_ISSUER), and that includes tfs-api.karyabilisim.com.

This is not tidiness. x-dev-permissions is believed verbatim: a caller receives exactly the permissions it names. With no identity provider around and no real data behind the API that is harmless. With one configured it is a complete authentication and authorization bypass — anyone able to reach the URL could call themselves an approver and read, approve or disburse against the whole book, or name any partyId and read that customer’s contracts through /api/v1/my/*. The deployed platform accepted exactly that until 27 August 2026.

So a deployed environment needs a real token: the password grant above for scripts, PKCE for browsers. Any request sending dev headers gets MSG-G-12.