Class AgentRuleTokenService

java.lang.Object
com.xebialabs.xlrelease.agentrules.AgentRuleTokenService

public class AgentRuleTokenService extends Object
The rules engine's OAuth2 identities, configured under xl.features.ai.agent-rules.oauth:
  • Service identity (serviceToken()): short-lived access tokens via the client-credentials grant (RFC 6749 section 4.4) against the platform IdP. Dispatches to assistant-resolved agents authenticate as this service principal — the agent's permissions become the service account's roles, reviewable in one place, and tokens are auto-refreshed rather than pasted and left to expire.
  • Approver on-behalf-of (exchangeApproverToken(java.lang.String, java.lang.String)): RFC 8693 token exchange of an approver's own access token, so an action a human approved runs on the approver's identity (sub = approver) with the exchange client as the acting party — the audit trail an approval should leave. Configured separately under ...oauth.exchange because the exchanging client is usually Release's own login client (the IdP requires the subject token's audience to include the requesting client).

Built on Spring Security's OAuth2 client, but entirely module-local: the registrations and authorized-client manager are private to this service, deliberately NOT the OIDC auth plugin's beans — these identities must exist whatever auth profile Release runs. The AuthorizedClientServiceOAuth2AuthorizedClientManager is the servlet-context-free variant made for background threads; its providers cache tokens per principal and re-authorize inside a 60s pre-expiry clock-skew window. The one plugin bean this service looks up (never requires) is the host's request-bound OAuth2AuthorizedClientManager, used only to read the approver's current token on their own request thread.

Every method returns null instead of throwing when unconfigured or when the IdP is unreachable, so callers degrade gracefully (approver identity → service identity → no token). Service-token failures are negative-cached briefly so an unreachable IdP costs one request per cooldown, not one per event.

  • Constructor Summary

    Constructors
    Constructor
    Description
    AgentRuleTokenService(com.xebialabs.xlrelease.config.XlrConfig config, org.springframework.beans.factory.ObjectProvider<org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager> hostManagerProvider)
     
  • Method Summary

    Modifier and Type
    Method
    Description
    currentUserToken(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response)
    The approver's current access token, read from the host's request-bound authorized-client manager (the OIDC auth plugin's bean; absent on other auth profiles).
    exchangeApproverToken(String subjectToken, String approverName)
    Exchanges an approver's access token (RFC 8693) so a delegated agent call carries the approver's identity.
    boolean
    True when the client-credentials service identity is configured.
    boolean
    True when the RFC 8693 approver on-behalf-of exchange is configured.
    The current service access token, minted or refreshed by the authorized-client manager as needed.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • AgentRuleTokenService

      public AgentRuleTokenService(com.xebialabs.xlrelease.config.XlrConfig config, org.springframework.beans.factory.ObjectProvider<org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager> hostManagerProvider)
  • Method Details

    • isConfigured

      public boolean isConfigured()
      True when the client-credentials service identity is configured.
    • isExchangeConfigured

      public boolean isExchangeConfigured()
      True when the RFC 8693 approver on-behalf-of exchange is configured.
    • serviceToken

      public String serviceToken()
      The current service access token, minted or refreshed by the authorized-client manager as needed. Returns null when the identity is not configured or the IdP cannot be reached — never throws, so a broken IdP degrades to token-less dispatch instead of killing it. Blocking (one bounded HTTP round trip on mint/refresh): call it from the agent-rules executor only, never from an event-bus handler thread.
    • currentUserToken

      public String currentUserToken(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response)
      The approver's current access token, read from the host's request-bound authorized-client manager (the OIDC auth plugin's bean; absent on other auth profiles). Must be called on the approver's own HTTP request thread — this is the subject-token acquisition step of the on-behalf-of flow, and the session-bound token exists nowhere else.
    • exchangeApproverToken

      public String exchangeApproverToken(String subjectToken, String approverName)
      Exchanges an approver's access token (RFC 8693) so a delegated agent call carries the approver's identity. Cached per approver by the manager. Returns null when the exchange is not configured, the subject token is absent, or the IdP refuses — callers fall back to the service identity. Blocking (one bounded IdP round trip); called on the approver's own request thread so the subject token is exchanged while fresh.