jimmyjames / fga-spring-examples

simple example demonstrating using FGA in a spring boot application
15 stars 4 forks source link

Spring Security Examples with OpenFGA

Repository demonstrating use cases and possible solutions to integrate FGA with Spring Security.

Goals

The goals of this repository are to:

Samples

Prerequisites

Usage

Simple no-auth sample

To run the simple-auth sample, see the README.

Client credentials sample

This sample comprises of two parts:

Create Auth0 application and API

Start OpenFGA and create a store and authorization model

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 run

Create a store:

fga store create --name "Example Store" --api-url http://localhost:4000

You 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.json

You should receive a response like this. Note the authorization_model_id:

{
  "authorization_model_id":"01HPSDPTTC209FQ0P4AMK3AZPE"
}

Configure resource server

Configure the application properties:

cd resource-server
cp src/main/resources/application.yml.example src/main/resources/application.yml

In 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.

Run resource server

./gradlew bootRun

This will start the server on port 8082.

Configure the client

Configure the application properties:

cd client-restclient
cp src/main/resources/application.yml.example src/main/resources/application.yml

Replace the oauth2 values and auth0-audience with the values of your Auth0 application and API identifier.

Start the application

./gradlew bootRun

This will start the application, execute the client credentials grant to obtain a JWT, and then makes calls to the resource server:

You can see the results of these calls in the application logs.

Implementation details

The samples demonstrate the following:

Auto-configuration of OpenFgaClient

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.

Simple FGA check bean definition

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.

Custom FgaCheck annotation and aspect

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.