OpenFGA
Authorize an Exchange against an OpenFGA relationship graph, and maintain the relationship tuples it is authorized against.
What’s inside
-
OpenFGA component, URI syntax:
openfga:operation
Please refer to the above links for usage and configuration details.
Maven coordinates
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-openfga-starter</artifactId>
</dependency> Spring Boot Auto-Configuration
The starter supports 32 options, which are listed below.
| Name | Description | Default | Type |
|---|---|---|---|
camel.component.openfga.api-audience | The audience to request the access token for in the client-credentials flow. | String | |
camel.component.openfga.api-token | Pre-shared token sent to OpenFGA in the Authorization header, for a server started with \{code --authn-method preshared}. | String | |
camel.component.openfga.api-token-issuer | The token endpoint the client-credentials flow exchanges its credentials at. | String | |
camel.component.openfga.api-url | The base URL of the OpenFGA HTTP API, without a trailing path. The default assumes OpenFGA running as a sidecar on its standard HTTP port. | String | |
camel.component.openfga.authorization-model-id | The identifier of the authorization model revision to evaluate against. Leave it empty to use whichever model the store considers latest. Pin it in production. A store keeps every model it was ever given and latest moves the moment somebody writes a new one, so an unpinned endpoint can start answering a different question than the one it was reviewed with - without any change to the route. Pinning also makes a model rollout a deliberate, reviewable configuration change. | String | |
camel.component.openfga.autowired-enabled | Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc. | true | Boolean |
camel.component.openfga.client-id | Client identifier for the OAuth 2.0 client-credentials flow, for a server that authenticates through an OIDC provider. Setting it selects that flow, so clientSecret, apiTokenIssuer and apiAudience are then required too. | String | |
camel.component.openfga.client-secret | Client secret for the OAuth 2.0 client-credentials flow. | String | |
camel.component.openfga.condition-context | Context passed to the CEL expressions of any conditioned relation the check touches, as a map resolved from the registry - conditionContext=#myContext. A map rather than an expression on purpose: OpenFGA types every condition parameter in the authorization model (int, bool, timestamp, ipaddress), and a map lets the route author supply values of the right Java type instead of strings that the server would then reject. A mistyped value is refused with an HTTP 400, which this component treats as a denial rather than as an unavailable decision point, so the failure direction is safe either way. Like contextualTuples this is endpoint-only. A condition can decide a relation, so letting a message choose the values it is evaluated with would hand the caller the decision. | Object> | |
camel.component.openfga.configuration | The component configuration. The option is a org.apache.camel.component.openfga.OpenFgaConfiguration type. | OpenFgaConfiguration | |
camel.component.openfga.connect-timeout | How long to wait for the connection to OpenFGA to be established. The component applies this itself rather than through the SDK’s own connectTimeout setting, which as of openfga-sdk 0.10.1 is accepted and then never read, leaving the connect phase bounded only by the operating system. The option is a long type. | 10000 | Long |
camel.component.openfga.consistency | The consistency the query is answered with. OpenFGA’s default, MINIMIZE_LATENCY, may answer from a replica that has not caught up yet, which right after a revoke means a tuple that was deleted can still grant access for a moment. Set HIGHER_CONSISTENCY on the paths where that window matters, at the cost of latency. Left unset, OpenFGA’s own default applies. | String | |
camel.component.openfga.contextual-tuples | Relationship tuples supplied for the duration of one check and never stored, as semicolon-separated user,relation,object triples - for example user:$\{exchangeProperty.authenticatedSubject},member,team:eng. Each part is evaluated as a Simple expression against the exchange, exactly as user and object are, and applies to check, batchCheck, listObjects, listRelations and listUsers. This is how a route hands OpenFGA a relationship the stored graph does not hold - a group membership that lives in the token rather than in the store, or a fact about the request such as which network it arrived on. A contextual tuple grants. It is read exactly like a stored tuple, so user:anne,owner,document:secret makes \{code check(user:anne, owner, document:secret)} answer true whatever the store contains. That is why this option is endpoint-only and is never taken from the message: a tuple the caller could choose would let it assert the very relationship being checked. By the same token, an expression here that reads an inbound header hands the caller that power anyway - keep these literal, or derive them from something the route established rather than from what it received. A part that resolves to blank denies the exchange rather than being dropped: the route asked for a tuple it did not get, and continuing without it would answer a different question than the one configured. | String | |
camel.component.openfga.enabled | Whether to enable auto configuration of the openfga component. This is enabled by default. | Boolean | |
camel.component.openfga.fail-open | Whether to let the exchange proceed when OpenFGA could not be asked at all, for example because the server is unreachable. Disabled by default so that an unavailable decision point denies rather than grants access. Do not enable this in production. It applies to the check operation and to OpenFgaSecurityPolicy, the two places where proceed has a meaning, and it covers only a failure to obtain a verdict. An exchange that was denied, and an exchange that carried no usable subject or object, are decisions rather than failures and are never turned into an allow by this flag. The other operations ignore it. A batchCheck or listObjects that failed has no safe way to proceed - returning the objects it never managed to filter would be the leak the filtering was there to prevent - so a failure there is reported as an error for the route’s own error handling to deal with. | false | Boolean |
camel.component.openfga.health-check-consumer-enabled | Used for enabling or disabling all consumer based health checks from this component | true | Boolean |
camel.component.openfga.health-check-producer-enabled | Used for enabling or disabling all producer based health checks from this component. Notice: Camel has by default disabled all producer based health-checks. You can turn on producer checks globally by setting camel.health.producersEnabled=true. | true | Boolean |
camel.component.openfga.lazy-start-producer | Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel’s routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing. | false | Boolean |
camel.component.openfga.max-parallel-requests | How many of a batchCheck’s checks may be in flight at once. The batch is issued as one request per object, so this bounds the load one exchange puts on the server. | 10 | Integer |
camel.component.openfga.max-retries | How many times the SDK retries a request that failed in a way worth retrying, such as a rate limit or a 5xx. Set it to 0 to disable retries; the overall wait a routing thread can spend on one exchange grows with it. | 3 | Integer |
camel.component.openfga.object | The object being accessed, as an OpenFGA object identifier such as \{code document:budget}. Evaluated as a Simple expression against each exchange, so document:$\{header.documentId} names the resource the message is about. Unlike the subject, taking the object from a header is normal and safe: the caller is entitled to say which resource it wants, and the check is what decides whether it may have it. | String | |
camel.component.openfga.open-fga-client | An existing OpenFgaClient to use. When set, every option describing how to reach the server - apiUrl, storeId, the credentials, the timeouts and sslContextParameters - is ignored, because they are baked into the client that was handed over. The option is a dev.openfga.sdk.api.client.OpenFgaClient type. | OpenFgaClient | |
camel.component.openfga.read-timeout | How long to wait for one request to OpenFGA to complete once connected. A request that times out is a failure to obtain a verdict rather than a deny, so it fails closed - or proceeds when failOpen is set. The option is a long type. | 10000 | Long |
camel.component.openfga.relation | The relation to demand, such as reader or owner. Evaluated as a Simple expression against each exchange, though a literal is what you usually want. The relation is the permission being demanded, so resolving it from an inbound header lets the caller pick the weakest one the model defines. Keep it literal, or derive it from something the route controls such as $\{header.CamelHttpMethod}. | String | |
camel.component.openfga.relations | Comma-separated list of relations the listRelations operation asks about, for example reader,writer,owner. Only the ones the subject actually holds come back. | String | |
camel.component.openfga.scopes | Space-separated scopes to request in the client-credentials flow. | String | |
camel.component.openfga.ssl-context-parameters | TLS configuration for the connection to OpenFGA. Needed to trust a server whose certificate comes from a private CA, and to present a client certificate to a server that requires mutual TLS - a SPIFFE X.509-SVID obtained with \{code camel-spiffe}, for instance, so the workload authenticates to the decision point as itself. The option is a org.apache.camel.support.jsse.SSLContextParameters type. | SSLContextParameters | |
camel.component.openfga.store-id | The identifier of the OpenFGA store holding the relationship tuples and the authorization model, as returned by \{code fga store create}. The store is the relationship graph that judges the exchange, so it comes from the endpoint only and is never taken from a message header. | String | |
camel.component.openfga.type | The object type to enumerate for the listObjects operation, for example document. This is a type name from the authorization model, so it is taken literally rather than evaluated. | String | |
camel.component.openfga.use-global-ssl-context-parameters | Enable usage of global SSL context parameters. | false | Boolean |
camel.component.openfga.user | The subject to authorize, as an OpenFGA user identifier such as \{code user:anne}. Evaluated as a Simple expression against each exchange, so a literal is used as-is and user:$\{exchangeProperty.CamelKeycloakTokenSubject} resolves whatever an earlier step established. Read it from an exchange property rather than a header wherever you can. An exchange property is set by the route itself - by the step that verified the caller - and nothing outside the route can set one. A header, by contrast, is often whatever the caller sent, and an endpoint configured as user:$\{header.userId} lets the caller choose who to be. \{code camel-keycloak}'s KeycloakSecurityPolicy already follows that reasoning: it reads the subject from the CamelKeycloakTokenSubject exchange property in preference to the header of the same name, its preferPropertyOverHeader option defaulting to true. Nothing in Camel sets the property for you, so the step that validates the token has to record it - but recording it under that name lets one identity serve both. An expression that resolves to blank, or to a bare \{code user:} prefix, denies the exchange: an exchange carrying no identity is not authorized, and failOpen does not apply to it. | String | |
camel.component.openfga.user-filters | Comma-separated list of user filters for the listUsers operation, naming which kinds of subject to return. An entry is either a type, user, or a type and a relation, team#member, to return the usersets holding the relation rather than the individual subjects. Defaults to user. | String |