Skip to main content
Proxy service extensions let you customize how the Orchestrator handles requests to your protected web applications. Use these hooks to control authentication, enforce authorization policies, enrich requests with user attributes, inject headers, modify requests and responses, and automate login to upstream applications.

Request Lifecycle

The following diagram shows the Orchestrator’s request processing pipeline for proxy apps. Each service extension hook is shown at the point where it executes in the flow. When a request arrives, the Orchestrator matches it against configured policies. For protected policies, the flow proceeds through authentication, attribute loading, authorization, header injection, optional upstream login, and request/response modification before the response is returned to the user. The authorization step evaluates isAuthorizedSE if configured, along with any declarative authorization rules defined on the policy.
Policy decisions (authorization result and headers) are cached in the user’s session. On subsequent requests, cached decisions skip directly to the upstream login check, bypassing the authentication, attribute loading, authorization, and header building steps.

Hooks

isAuthenticatedSE

Determine whether the current user is already authenticated. Return true to skip the login flow, or false to send the user through authentication. Use this when you need custom logic to check authentication status — for example, validating an external session token or checking a cookie from another system. Signature:
Config location: apps[].policies[].authentication.isAuthenticatedSE Parameters: Returns: booltrue if the user is authenticated, false otherwise. Examples:
See the full example under authenticateSE for a combined implementation that uses both isAuthenticatedSE and authenticateSE to let users choose which identity provider to authenticate with.
See the full example under authenticateSE for a combined implementation that uses both isAuthenticatedSE and authenticateSE to authenticate users against an LDAP directory over TLS.

authenticateSE

Handle authentication when the user has not yet logged in. Use this to redirect to an external login page, validate credentials directly, or start a custom authentication flow. Signature:
Config location: apps[].policies[].authentication.authenticateSE Parameters: Examples:
When multiple identity providers are available, you can give users a choice of which one to authenticate with. This extension pairs isAuthenticatedSE and authenticateSE to implement an IdP selector. The IsAuthenticated function checks the session to determine if the user has already authenticated with any configured IdP, while Authenticate renders a selector form and delegates login to the chosen provider.
idp-selector.go
Some applications require the user to actively re-authenticate at the identity provider on every login — for example, when returning after logging out of a sensitive application — rather than silently reusing an existing IdP session. This extension pairs isAuthenticatedSE and authenticateSE: IsAuthenticated checks whether the SAML connector has already authenticated the user, and Authenticate starts login with the WithForceAuthentication option, which sets ForceAuthn="true" on the SAML AuthnRequest so the IdP prompts for credentials again.
Forced re-authentication is driven entirely by the service extension. Call idp.Login with idfabric.WithForceAuthentication() on every login to always re-prompt, or apply it conditionally — for example, only for step-up scenarios — to decide per request.
force-authn.go
When an application relies on HTTP Basic Auth, this extension intercepts the credentials and authenticates the user against an LDAP directory over TLS. The IsAuthenticated function checks the session for a previous successful bind, while Authenticate extracts the Basic Auth credentials, connects to LDAP with StartTLS, and performs a bind to verify them.
ldap-tls-auth.go

isAuthorizedSE

Decide whether an authenticated user is allowed to access the requested resource. Return true to allow the request or false to deny it. Use this to call an external policy engine, enforce attribute-based access control (ABAC), or apply custom business rules beyond what declarative authorization rules support. Signature:
Config location: apps[].policies[].authorization.isAuthorizedSE Parameters: Returns: booltrue if the user is authorized, false otherwise.

handleUnauthorizedSE

Customize the response when a user fails authorization. Use this to redirect to a custom error page, return a specific error code, or log additional context about the denied request. Signature:
Config location: apps[].handleUnauthorizedSE Parameters:

loadAttrsSE

Enrich the user’s session with additional attributes before the request is processed. Use this to pull in user details from external sources — such as an LDAP directory, a database, or a REST API — transform attribute values, or merge attributes from multiple identity providers. Signature:
Config location: apps[].loadAttrsSE Parameters: Returns: error — return nil on success, or an error to indicate attribute loading failed. Examples:
When authorization decisions depend on group memberships stored in an LDAP directory, this extension queries LDAP for the authenticated user’s groups and stores them in the session. Downstream hooks like isAuthorizedSE or createHeaderSE can then read the groups from the session without repeating the LDAP lookup.
load-ldap-groups.go

createHeaderSE

Build custom HTTP headers to send to the upstream application along with the proxied request. Use this when header values need to be computed dynamically — for example, constructing a header from session attributes, looking up a value from an external service, or encoding user information for the upstream app. Signature:
Config location: apps[].headers[].createHeaderSE Parameters: Returns:
  • http.Header — a map of header names to values to inject into the upstream request
  • error — return nil on success, or an error if header creation fails
On open routes (allowUnauthenticated + allowAll), this extension does not run, and the header names it would emit are not known when configuration loads. As a result, a client-supplied copy of a header this extension normally sets is not stripped and is forwarded to the upstream. Do not mark a route open if the upstream trusts an identity header produced by a createHeaderSE.
Examples:
When an upstream application expects user identity in specific HTTP headers, this extension reads attributes from the user’s session and constructs the required headers. Each createHeaderSE entry handles one header, allowing you to transform or combine attribute values as needed.
create-headers.go

modifyRequestSE

Modify the HTTP request before it is forwarded to the upstream application. Use this to add authentication headers, rewrite paths, inject tracing headers, or transform the request body. Signature:
Config location: apps[].modifyRequestSE Parameters:
This hook runs on every proxied request. Keep it lightweight to avoid adding latency. Use api.Cache() to avoid repeating expensive lookups.
This hook runs on every request, including open routes, and it runs after the Orchestrator strips managed headers. If your extension reads a client-supplied header and forwards it to the upstream, it can reintroduce attacker-controlled input that stripping would otherwise have removed. Do not pass through client-supplied identity headers on routes that reach a header-trusting upstream.

modifyResponseSE

Modify the response from the upstream application before it reaches the user’s browser. Use this to add security headers, transform response content, adjust status codes, or inject additional content. Signature:
Config location: apps[].modifyResponseSE Parameters:
This hook runs on every proxied response. Keep it lightweight to avoid adding latency. Use api.Cache() to avoid repeating expensive lookups.
Examples:
Some applications lack single logout functionality. When the Orchestrator sits in front of such an application, there is no built-in way for users to trigger a federated logout because the upstream UI has no logout control that integrates with the Orchestrator.This service extension solves the problem by injecting a fixed-position “Single Logout” button into every HTML page returned by the upstream application. The button links to the Orchestrator’s /single-logout endpoint, giving users a way to initiate federated logout without any changes to the upstream application’s source code.
inject-slo-button.go

isLoggedInSE

Check whether the user is already logged in to the upstream application. Return true if the upstream session is active, or false to trigger the login flow. Use this to inspect upstream cookies, session tokens, or other indicators of an active upstream session. Signature:
Config location: apps[].upstreamLogin.isLoggedInSE Parameters: Returns: booltrue if the user is logged in to the upstream application, false otherwise.

loginSE

Perform the login to the upstream application. Use this to submit credentials to the upstream login page, exchange tokens, set upstream cookies, or perform any steps required to establish an upstream session. Signature:
Config location: apps[].upstreamLogin.loginSE Parameters: Returns: error — return nil on success, or an error if the upstream login fails.

Service Extensions Overview

Configuration, SDK reference, and best practices

Proxy Applications

Proxy application configuration and upstream settings