Authentication

The client supports two authentication methods, both configured at construction time via ADTClient. src/AdtClient.ts:315

Basic auth (username + password)

const client = new ADTClient(
  "https://my-sap-system:8000",
  "MYUSER",
  "password",
  "001",         // SAP client / mandant
  "EN"          // language
)
await client.login()

OAuth / Bearer token

Pass a BearerFetcher callback instead of a password string. It is called lazily before any request that needs a token and the result is cached until the next login. src/AdtHTTP.ts:339

const client = new ADTClient(
  "https://my-sap-system:8000",
  "MYUSER",
  () => fetchTokenFromIdp(),   // BearerFetcher — async () => string
  "001",
  "EN"
)

The SAP client number (001) and language are appended as sap-client and sap-language query parameters on every request. src/AdtHTTP.ts:225

Login flow

login() performs a single bootstrapping request that seeds both the cookie jar and the CSRF token in one shot. src/AdtHTTP.ts:214

  1. Sets the internal CSRF token to the sentinel value "fetch"
  2. Makes a GET to /sap/bc/adt/compatibility/graph with X-CSRF-Token: fetch and basic auth / bearer in the Authorization header
  3. SAP responds with Set-Cookie headers and X-CSRF-Token: <real-token>
  4. Cookies are stored in the internal cookie jar; the real token replaces "fetch" in commonHeaders

All subsequent requests automatically include the stored cookies as a Cookie header and the cached CSRF token as X-CSRF-Token — no further login calls are needed until the session expires.

Login is also called automatically on the first request if login() was never called explicitly. Concurrent calls are deduplicated — only one login request ever runs at a time. src/AdtHTTP.ts:215

What gets sent on every request

All four of these are assembled in AdtHTTP.request() before dispatch. src/AdtHTTP.ts:322

HeaderValuePurpose
Authorization Basic <base64> or Bearer <token> Credential proof on every request
X-CSRF-Token Opaque token returned at login SAP's CSRF protection — required on all mutating requests
X-sap-adt-sessiontype stateful, stateless, or "" Tells the server whether to maintain server-side session state
Cookie All cookies from the jar, semicolon-separated Carries the SAP session ID (SAP_SESSIONID_*) back to the server
The Cookie header is only set manually in Node.js environments (runningInNode check). In a browser the browser handles cookies automatically, so the client skips this step to avoid conflicts. src/AdtHTTP.ts:324

CSRF token lifecycle

SAP ADT requires a valid X-CSRF-Token on all POST, PUT, and DELETE requests. The client manages this automatically. src/AdtHTTP.ts:36

StateToken valueMeaning
Pre-login "fetch" Sentinel — triggers a login cycle on next request
Logged in Opaque string from SAP Sent as-is on every request
Token rejected (403) Reset to "fetch" Triggers re-login in stateless mode only
Session timeout (400) Reset to "fetch" Treated the same as a CSRF error

A CSRF error is identified by either a 403 with X-CSRF-Token: Required in the response, or a 400 with status text "Session timed out". src/AdtException.ts:166

In stateful mode the client does not auto-retry on CSRF or 401 errors — the lock state on the server would be lost. You must handle re-login and re-locking manually. src/AdtHTTP.ts:272

Cookie jar

The client maintains a simple Map<string, string> as a cookie jar. src/AdtHTTP.ts:238

The session cookie SAP sets is named SAP_SESSIONID_<SID>_<client>. The client exposes it via:

const id = client.sessionID   // ["SAP_SESSIONID_NPL_001", "<value>"] or ""
// src/AdtClient.ts:445 — parses the cookie jar for SAP_SESSIONID

Session types

The X-sap-adt-sessiontype header tells the SAP server whether to keep state between requests. The client exposes this as the stateful property. src/AdtHTTP.ts:44

ModeHeader sentServer behaviourWhen to use
session_types.stateless stateless Each request is independent; session state discarded after response Reads, object creation, anything that doesn't need a lock
session_types.stateful stateful Server ties all requests to the same session; locks are held between calls Any write that requires lock → write → unlock
session_types.keep ("") (empty string) Server preserves whatever session type was previously negotiated Mid-sequence requests where you don't want to change session type
isStateful returns true if mode is explicitly stateful, OR if mode is keep and the last negotiated session was stateful. This lets you check whether locks will actually be held without tracking mode changes manually. src/AdtHTTP.ts:159

The recommended pattern when writing — save and restore the previous mode:

const prevstate = c.stateful
try {
  c.stateful = session_types.stateful    // → X-sap-adt-sessiontype: stateful
  const handle = await c.lock(path)
  await c.setObjectSource(path, source, handle.LOCK_HANDLE)
  await c.unLock(path, handle.LOCK_HANDLE)
} finally {
  c.stateful = prevstate                 // restore whatever it was before
}

dropSession vs logout

These are distinct operations and should not be used interchangeably.

MethodWhat it doesWhen to use
dropSession() Sets mode to stateless, then makes a GET to /sap/bc/adt/compatibility/graph — this signals the server to release the stateful session slot without invalidating credentials. Cookies and CSRF token are kept. src/AdtHTTP.ts:253 After a lock/write/unlock cycle — you want to stay logged in but release the server-side session
logout() Sets mode to stateless, calls /sap/public/bc/icf/logoff, then clears credentials, cookies, and resets the CSRF token to "fetch". src/AdtHTTP.ts:242 End of session — full teardown

Stateless clone

Some operations (like createObject) leave the server in an inconsistent state if called on a stateful session. The statelessClone property provides a separate client instance using the same credentials but always in stateless mode. src/AdtClient.ts:371

// statelessClone is lazily created and cached
await c.statelessClone.createObject(options)

// clones cannot be set to stateful — throws AdtException
c.statelessClone.stateful = session_types.stateful  // ✗ throws

The clone logs in independently with its own cookie jar and CSRF token. If the main client used a BearerFetcher, the same fetcher callback is reused so token refresh is handled consistently.

Keep-alive

SAP systems time out idle sessions (typically after a few minutes). An optional keep-alive timer can be enabled at construction time. src/AdtHTTP.ts:210

const client = new ADTClient(url, user, pw, "001", "EN", {
  keepAlive: true   // fires a ping every 120 000 ms (2 minutes)
})

When active, the client sends a lightweight request every 2 minutes only if it has not made any other request in that window (needKeepalive flag). This prevents unnecessary traffic during active use. src/AdtHTTP.ts:283

Auth error handling

ErrorConditionStateless behaviourStateful behaviour
401 Unauthorized Bad or expired credentials Auto re-login, retry request once Throws — no retry
403 + X-CSRF-Token: Required Token expired or missing Auto re-login, retry request once Throws — no retry
400 Session timed out Stateful session slot expired on server Auto re-login, retry request once Throws — no retry

The auto-retry path calls login() to refresh cookies and the CSRF token, then replays the original request exactly once. If it fails again, the error is thrown. src/AdtHTTP.ts:263