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.1Host: tfs-api.karyabilisim.comAuthorization: Bearer eyJhbGciOiJSUzI1NiIs...OIDC endpoints
Section titled “OIDC endpoints”| 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.
SPAs: Authorization Code + PKCE (S256)
Section titled “SPAs: Authorization Code + PKCE (S256)”Single-page applications (e.g. the ops console’s tfs-ops public client) carry no
client secret; they use Authorization Code + PKCE (S256):
- The app generates a random
code_verifierand derives thecode_challengefrom its SHA-256 hash. - The user is redirected to the
authorization_endpoint(response_type=code,code_challenge_method=S256). - After login, Keycloak returns a short-lived
codeto the app. - The app posts
code+code_verifierto thetoken_endpointto obtain the access token; Keycloak verifies that the verifier hashes to the challenge.
Token contents: permission codes
Section titled “Token contents: permission codes”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.
Password grant for testing
Section titled “Password grant for testing”For integration tests you can obtain a token without the browser flow:
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.