Repository demonstrating use cases and possible solutions to integrate FGA with Spring Security.
The goals of this repository are to:
- Show how OpenFGA can be integrated with Spring today
- Give insight into possible DX improvements, either through an FGA-owned starter/library, possible direct Spring Security integration, or customer guidance
simple-authis a sample FGA integration that has a basic Spring security configured. It is a simple example that makes assumptions about users and principals.resource-serverandclient-restclientdemonstrate a resource server with JWT authorization using theokta-spring-boot-starterand a client credentials flow to obtain a JWT to make API calls. The API's inresource-serverare protected both by JWT and FGA checks, and are called byclient-restclient.
- Docker
- Java 17
- OpenFGA CLI
To run the simple-auth sample, see the README.
This sample comprises of two parts:
- A resource server configured with the
okta-spring-boot-starterto secure endpoints with JWTs issued by Auth0. It protects APIs with JWT authorization and uses FGA to protect endpoints and write authorization data. - A client that uses the client credentials flow to obtain a JWT to call the resource server.
- Create a new Auth0 API and note the API identifier
- Create a new Auth0 machine-to-machine application, and note the client ID and secret
This will start an in-memory database OpenFGA server:
docker pull openfga/openfga:latest
docker run --rm -e OPENFGA_HTTP_ADDR=0.0.0.0:4000 -p 4000:4000 -p 8081:8081 -p 3000:3000 openfga/openfga runCreate a store:
fga store create --name "Example Store" --api-url http://localhost:4000You should receive a response like this. Note the store ID value:
{
"store": {
"created_at":"2024-02-16T16:56:21.162910175Z",
"id":"01HPSDHYXAD9HS906YFG9CQM02",
"name":"Test Store",
"updated_at":"2024-02-16T16:56:21.162910175Z"
}
}Create an authorization model:
fga model write --api-url http://localhost:4000 --store-id STORE-ID-FROM-ABOVE --file ./example-auth-model.jsonYou should receive a response like this. Note the authorization_model_id:
{
"authorization_model_id":"01HPSDPTTC209FQ0P4AMK3AZPE"
}Configure the application properties:
cd resource-server
cp src/main/resources/application.yml.example src/main/resources/application.ymlIn application.yml, replace the oauth2 properties with the values from your Auth0 application and API.
Also replace the values for fga-store-id and fga-authorization-model-id with the values created above.
./gradlew bootRunThis will start the server on port 8082.
Configure the application properties:
cd client-restclient
cp src/main/resources/application.yml.example src/main/resources/application.ymlReplace the oauth2 values and auth0-audience with the values of your Auth0 application and API identifier.
./gradlew bootRun
This will start the application, execute the client credentials grant to obtain a JWT, and then makes calls to the resource server:
- Attempt to
GETa "document" for which the current principal does not have an FGA relation to. This request should fail with a403. - A call to create a "document", which will create an FGA relationship associated with the principal.
- Another attempt to get the document, which should now return successfully as there is a
readerrelationship between the principal and the document.
You can see the results of these calls in the application logs.
The samples demonstrate the following:
Uses custom application property values to create and make available to components an OpenFgaClient. This can be used by applications to interact with the FGA API directly, e.g., to write authorization data.
An application can configure the client in application properties for their usage:
openfga.fgaApiUrl=FGA_API_URL
openfga.fgaStoreId=FGA_STORE_ID
openfga.fgaAuthorizationModelId=FGA_AUTHORIZATION_MODEL_ID
openfga.fgaApiAudience=FGA_API_AUDIENCE
openfga.fgaClientId=FGA_CLIENT_ID
openfga.fgaClientSecret=FGA_CLIENT_SECRET
...Note that for simplicity purposes, this sample does not support FGA authorization, thus is NOT suitable for production use.
A simple bean is defined to perform an authorization check:
@PreAuthorize("@openFga.check('#id', 'document', 'reader', 'user')")
public String getDocumentWithSimpleFgaBean(@PathVariable String id) {
return "You have access!";
}In the example above, the currently authenticated principal's name is used as the user ID by default. It can also be explicitly passed.
A custom @FgaCheck annotation was created to demonstrate using an explicit FGA annotation and aspect to execute an FGA check prior to the method execution:
@FgaCheck(userType="user", relation="reader", objectType="document", object="#id")
public String customAnnotation(@PathVariable String id) {
return "You have access!";
}Similar to the bean definition, it uses the currently authenticated principal by default for the user ID.