Web Services: BFF
apps/bff is a small Spring Boot Backend-for-Frontend service that sits between Dialogue Branch Studio (apps/studio) and the Web Service. It exists so that the browser never has to hold an OAuth2 access or refresh token: the token lives server-side, in the BFF's HTTP session, and the browser only ever holds a JSESSIONID session cookie. See Studio Authentication (via the BFF) for the full request flow, with diagrams.
Sessions are persisted to a dedicated MariaDB database (dlb_bff_sessions by default) rather than kept in the JVM's own memory, so a redeploy of the BFF doesn't silently drop every signed-in user. This is a separate database from the Web Service's own — the BFF otherwise owns no persistent data of its own.
Unlike the Web Service, the BFF is deployed as a plain executable JAR (not a WAR), listening on port 8082 by default. It can also be built as a Docker image using the provided Dockerfile at apps/bff/Dockerfile (built from the repository root, since the Docker build context spans both apps/bff/ and the rest of the monorepo).
cd apps/bff
./gradlew build # compile and build the executable JAREnd-Points
The BFF exposes the following end-points to the browser:
GET /login— the generic login trigger Studio navigates to; redirects to this BFF's own OAuth2 authorization endpoint, which performs the Authorization Code + PKCE exchange against Keycloak and stores the resulting token in the session. Studio only ever needs this path, not the actual registration id behind it, so a differently-structured BFF (e.g. one resolving a registration per request rather than having exactly one fixed client) can satisfy the same contract however it needs to.GET /whoami— returns the current session's username and Dialogue Branch Web Service roles as JSON, decoded from the session's access token./api/**— proxies every call through to the Web Service, attaching the session's access token as theAuthorization: Bearerheader.GET /api/v1/info/allis reachable without a session, matching that end-point's public status on the Web Service itself.POST /logout— RP-initiated logout: ends both the BFF's session and the underlying Keycloak SSO session.GET /actuator/health,/actuator/info— Spring Boot Actuator health/info endpoints.
Building Your Own Proxy
If you write your own BFF or reverse proxy in front of the Web Service — rather than deploying this one — watch out for one easy-to-hit pitfall: double-encoding the forwarded query string.
An incoming request's query string arrives already percent-encoded (e.g. a client correctly sends timeZone=Europe%2FAmsterdam for the value Europe/Amsterdam). Many HTTP client libraries' URL builders assume the string you hand them still needs encoding, and will encode it again — turning that %2F into %252F. The Web Service then decodes it once, is left with the literal string Europe%2FAmsterdam, and rejects it as an invalid time zone. Any query parameter whose value itself needs escaping (a time zone, a name with &/=/spaces, …) can trigger this — not just timeZone.
Concretely, in Spring: RestClient's default UriBuilderFactory re-encodes whatever string you pass to .query(...). If your proxy's whole job is forwarding an already-encoded request verbatim, configure it not to re-encode — a DefaultUriBuilderFactory with EncodingMode.NONE. This apps/bff's own ProxyConfig does exactly this, as a concrete reference if you're proxying in Spring/Kotlin too.
Configuration
The BFF is configured through dlb.bff.* properties (see apps/bff/src/main/resources/application.yml), each overridable at runtime via a matching DLB_BFF_* environment variable:
dlb.bff.oauth2-client-id/dlb.bff.oauth2-client-secret— this service's own Keycloak client credentials (dlb-bffby default).dlb.bff.keycloak-base-url— the internal Keycloak address, used for token exchange and signature verification.dlb.bff.keycloak-browser-base-url— the browser-facing Keycloak address, used to redirect the browser for login and logout. Only differs from the internal address when the BFF runs inside a container network with its own internal Keycloak hostname.dlb.bff.keycloak-realm— the Keycloak realm (dialoguebranchby default).dlb.bff.api-base-url— the internal address of the Web Service that/api/**calls are proxied to.dlb.bff.web-service-client-id— the Keycloak client id (dlb-web-service) whoseresource_accessroles/whoamireads for the current user.dlb.bff.post-login-redirect-url— where the browser is sent after a successful login or logout; the web client's own origin (e.g. its Vite dev server address in local development, or "/" behind a same-origin reverse proxy in production).dlb.bff.session-timeout— how long an inactive session (and its cookie) stays valid, e.g.7d(the default). Drives both the server-side session timeout and theJSESSIONIDcookie'smax-age, so they can't drift apart.dlb.bff.mariadb.host/dlb.bff.mariadb.port/dlb.bff.mariadb.user/dlb.bff.mariadb.password/dlb.bff.mariadb.database— connection details for the MariaDB database sessions are persisted to (dlb_bff_sessionsby default; see Local Development below).
Local Development
Running docker compose --profile api up (see the repository's top-level infrastructure/docker/compose.yml) builds and starts the BFF alongside the Web Service, since developing Studio locally needs both. A --profile studio setup also exists, containerizing Studio and the BFF themselves behind a proxy — see Studio's local development setups for all three.
Note
If you found errors or have questions about this page, please consider reporting an issue at https://github.com/dialoguebranch/platform or sending an email to info@dialoguebranch.com.
