fastapi-opa


Namefastapi-opa JSON
Version 2.0.0 PyPI version JSON
download
home_pagehttps://github.com/busykoala/fastapi-opa
SummaryFastapi OPA middleware incl. auth flow.
upload_time2024-02-07 22:34:15
maintainer
docs_urlNone
authorMatthias Osswald
requires_python>=3.8,<4.0
licenseGPL-3.0-or-later
keywords fastapi oidc authentication authorization saml
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Open Policy Agent middleware for FastAPI

## Table of contents
- [Contributors](#contributors)
- [What does fastapi-opa do](#about)
- [Installation](#installation)
- [How to get started](#getting-started)
- [Open Policy Agent](#opa)
- [Authentication flow](#auth-flow)
  - [API key authentication](#api-key-auth)
  - [OIDC authentication](#oidc-auth)
  - [SAML authentication](#saml-auth)
- [Custom payload enrichment](#custom-payload-enrichment)
  - [GraphQL enrichment](#gql-enrichment)

<a name="contributors"/>

## Contributors

Thanks to all the contributors below. Furthermore thanks for raising issues.

<a href="https://github.com/morestanna">
  <img src="https://avatars.githubusercontent.com/morestanna" width="60" height="60" />
</a>
<a href="https://github.com/busykoala">
  <img src="https://avatars.githubusercontent.com/busykoala" width="60" height="60" />
</a>
<a href="https://github.com/TracyWR">
  <img src="https://avatars.githubusercontent.com/TracyWR" width="60" height="60" />
</a>
<a href="https://github.com/loikki">
  <img src="https://avatars.githubusercontent.com/loikki" width="60" height="60" />
</a>
<a href="https://github.com/ejsyx">
  <img src="https://avatars.githubusercontent.com/ejsyx" width="60" height="60" />
</a>
<a href="https://github.com/JimFawkes">
  <img src="https://avatars.githubusercontent.com/JimFawkes" width="60" height="60" />
</a>
<a href="https://github.com/DiamondJoseph">
  <img src="https://avatars.githubusercontent.com/DiamondJoseph" width="60" height="60" />
</a>

<a name="about"/>

## What does fastapi-opa do

The FastAPI extension `fastapi-opa` allows to add login flows and integrates
Open Policy Agent to your app.

![Flow Diagram](https://raw.githubusercontent.com/busykoala/fastapi-opa/master/assets/diagram.png)

The middleware redirects the request to the identity provider. After the
authentication it validates the token. Using the token, Open Policy Agent
decides if the response has success or failure status.

<a name="installation"/>

## Installation

```bash
poetry add [--extras "graphql"] [--extras "saml"] fastapi-opa 
```

<a name="getting-started"/>

## How to get started

:bulb: checkout the wiki for an environment setup with Keycloak and Open Policy Agent:  
[Getting Started with FastAPI app with Authentication and Authorization](https://github.com/busykoala/fastapi-opa/wiki#dev-setup)

The package combines authentication and authorization with FastAPI. You can
customize the `OPAMiddleware` depending on your authentication flow.

Check out these examples for the most common flows:
- OIDC: `fastapi_opa.example_oidc.py`
- SAML: `fastapi_opa.example_saml.py`

## Open Policy Agent

The middleware sends the validated and authenticated user token to Open
Policy Agent. It adds the extra attributes `request_method` and
`request_path`.

```json
{
    "input": {
        "exp": 1617466243,
        "iat": 1617465943,
        "auth_time": 1617465663,
        "jti": "9aacb638-70c6-4f0a-b0c8-dbc67f92e3d1",
        "iss": "http://localhost:8080/auth/realms/example-realm",
        "aud": "example-client",
        "sub": "ccf78dc0-e1d6-4606-99d4-9009e74e3ab4",
        "typ": "ID",
        "azp": "david",
        "session_state": "41640fe7-39d2-44bc-818c-a3360b36fb87",
        "at_hash": "2IGw-B9f5910Sll1tnfQRg",
        "acr": "0",
        "email_verified": false,
        "hr": "true",
        "preferred_username": "david",
        "user": "david",
        "subordinates": [],
        "request_method": "GET",
        "request_path": ["finance", "salary", "david"]
    }
}
```

In Open Policy Agent you can create policies using user roles,
routes, request methods etc.

An example policy (from [the official Open Policy Agent
docs](https://www.openpolicyagent.org/docs/v0.11.0/http-api-authorization/))
for this setup could look like this:

```rego
package httpapi.authz

# bob is alice's manager, and betty is charlie's.
subordinates = {"alice": [], "charlie": [], "bob": ["alice"], "betty": ["charlie"]}

# HTTP API request
import input

default allow = false

# Allow users to get their own salaries.
allow {
  some username
  input.request_method == "GET"
  input.request_path = ["finance", "salary", username]
  input.user == username
}

# Allow managers to get their subordinates' salaries.
allow {
  some username
  input.request_method == "GET"
  input.request_path = ["finance", "salary", username]
  subordinates[input.user][_] == username
}
```

<a name="auth-flow"/>

## Authentication flow

Use the provided interface to set up your desired authentication flow. Then
insert it into `OPAMiddleware` (`fastapi_opa.auth.auth_interface.AuthInterface`).
Consider submitting a pull request with new flows.

You can also use these ready-to-go implementations:

<a name="api-key-auth"/>

### API key authentication

In the API key authentication a request header needs to match a given value.

```python
# Configure API keys
api_key_config = APIKeyConfig(
    header_key="test",
    api_key="1234"
)
api_key_auth = APIKeyAuthentication(api_key_config)
```

In the example the header `header["test"] = "1234"` authenticates the request.
For Open Policy Agent, set user to `APIKey` and the variable `client` to the
client address.

<a name="oidc-auth"/>

### OIDC authentication

The example in [How to get started](#getting-started) provides an example for
the implementation of the OIDC Authentication.

<a name="saml-auth"/>

### SAML authentication

For the saml implementation create your certs using
`openssl req -new -x509 -days 3652 -nodes -out sp.crt -keyout sp.key` and
add the keys to the sp section of your `settings.json`. Checkout the test
settings to get an idea (`tests/test_data/saml/*.json`).
Provide the path to your own `settings.json` and `advanced_settings.json`
in the `SAMLAuthConfig` like in the example below (don't use the test data in
production).

```python
from fastapi_opa import OPAConfig
from fastapi_opa.auth.auth_saml import SAMLAuthentication
from fastapi_opa.auth.auth_saml import SAMLConfig

opa_host = "http://localhost:8181"

saml_config = SAMLConfig(settings_directory="./tests/test_data/saml")
saml_auth = SAMLAuthentication(saml_config)

opa_config = OPAConfig(authentication=saml_auth, opa_host=opa_host,
                       accepted_methods=["id_token", "access_token"])
```

Upload the certificate to your identity provider. Using Keycloak as an
identity provider you need to configure `encrypt assertion`,
`client signature required`, `force POST bindings` on creating the client.
Also configure: `Client Scopes` -> `role_list (saml)` -> `Mappers tab` ->
`role list` -> `Single Role Attribute`

<a name="custom-payload-enrichment"/>

## Custom payload enrichment

Use the interface `fastapi_opa.opa.opa_config.Injectable` to add
more information to the payload sent to Open Policy Agent.

Configure the injectables in the `OPAConfig`:

```python
class FancyInjectable(Injectable):
    async def extract(self, request: Request) -> List:
        return ["some", "custom", "stuff"]

fancy_inj = FancyInjectable("fancy_key", skip_endpoints=["/health", "/api/[^/]*/test])

opa_config = OPAConfig(
    authentication=oidc_auth, opa_host=opa_host, injectables=[fancy_inj]
)
```

Use `skip_endpoints` to choose which endpoints the injectable shouldn't affect.
To define an endpoint, specify an exact string or a regular expression.

<a name="gql-enrichment"/>

### GraphQL enrichment

For GraphQL you can use the ready to go injectable:

```python
from fastapi_opa.opa.enrichment.graphql_enrichment import GraphQLInjectable`

graphql = GraphQLInjectable("gql_injectable")
opa_config = OPAConfig(authentication=oidc_auth, opa_host=opa_host, injectables=[graphql])
```

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/busykoala/fastapi-opa",
    "name": "fastapi-opa",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8,<4.0",
    "maintainer_email": "",
    "keywords": "fastapi,oidc,authentication,authorization,saml",
    "author": "Matthias Osswald",
    "author_email": "info@busykoala.io",
    "download_url": "https://files.pythonhosted.org/packages/12/48/1d6bc3d361398bee456aa380683cb8a5bfb205825148ea1e34e4bc72d246/fastapi_opa-2.0.0.tar.gz",
    "platform": null,
    "description": "# Open Policy Agent middleware for FastAPI\n\n## Table of contents\n- [Contributors](#contributors)\n- [What does fastapi-opa do](#about)\n- [Installation](#installation)\n- [How to get started](#getting-started)\n- [Open Policy Agent](#opa)\n- [Authentication flow](#auth-flow)\n  - [API key authentication](#api-key-auth)\n  - [OIDC authentication](#oidc-auth)\n  - [SAML authentication](#saml-auth)\n- [Custom payload enrichment](#custom-payload-enrichment)\n  - [GraphQL enrichment](#gql-enrichment)\n\n<a name=\"contributors\"/>\n\n## Contributors\n\nThanks to all the contributors below. Furthermore thanks for raising issues.\n\n<a href=\"https://github.com/morestanna\">\n  <img src=\"https://avatars.githubusercontent.com/morestanna\" width=\"60\" height=\"60\" />\n</a>\n<a href=\"https://github.com/busykoala\">\n  <img src=\"https://avatars.githubusercontent.com/busykoala\" width=\"60\" height=\"60\" />\n</a>\n<a href=\"https://github.com/TracyWR\">\n  <img src=\"https://avatars.githubusercontent.com/TracyWR\" width=\"60\" height=\"60\" />\n</a>\n<a href=\"https://github.com/loikki\">\n  <img src=\"https://avatars.githubusercontent.com/loikki\" width=\"60\" height=\"60\" />\n</a>\n<a href=\"https://github.com/ejsyx\">\n  <img src=\"https://avatars.githubusercontent.com/ejsyx\" width=\"60\" height=\"60\" />\n</a>\n<a href=\"https://github.com/JimFawkes\">\n  <img src=\"https://avatars.githubusercontent.com/JimFawkes\" width=\"60\" height=\"60\" />\n</a>\n<a href=\"https://github.com/DiamondJoseph\">\n  <img src=\"https://avatars.githubusercontent.com/DiamondJoseph\" width=\"60\" height=\"60\" />\n</a>\n\n<a name=\"about\"/>\n\n## What does fastapi-opa do\n\nThe FastAPI extension `fastapi-opa` allows to add login flows and integrates\nOpen Policy Agent to your app.\n\n![Flow Diagram](https://raw.githubusercontent.com/busykoala/fastapi-opa/master/assets/diagram.png)\n\nThe middleware redirects the request to the identity provider. After the\nauthentication it validates the token. Using the token, Open Policy Agent\ndecides if the response has success or failure status.\n\n<a name=\"installation\"/>\n\n## Installation\n\n```bash\npoetry add [--extras \"graphql\"] [--extras \"saml\"] fastapi-opa \n```\n\n<a name=\"getting-started\"/>\n\n## How to get started\n\n:bulb: checkout the wiki for an environment setup with Keycloak and Open Policy Agent:  \n[Getting Started with FastAPI app with Authentication and Authorization](https://github.com/busykoala/fastapi-opa/wiki#dev-setup)\n\nThe package combines authentication and authorization with FastAPI. You can\ncustomize the `OPAMiddleware` depending on your authentication flow.\n\nCheck out these examples for the most common flows:\n- OIDC: `fastapi_opa.example_oidc.py`\n- SAML: `fastapi_opa.example_saml.py`\n\n## Open Policy Agent\n\nThe middleware sends the validated and authenticated user token to Open\nPolicy Agent. It adds the extra attributes `request_method` and\n`request_path`.\n\n```json\n{\n    \"input\": {\n        \"exp\": 1617466243,\n        \"iat\": 1617465943,\n        \"auth_time\": 1617465663,\n        \"jti\": \"9aacb638-70c6-4f0a-b0c8-dbc67f92e3d1\",\n        \"iss\": \"http://localhost:8080/auth/realms/example-realm\",\n        \"aud\": \"example-client\",\n        \"sub\": \"ccf78dc0-e1d6-4606-99d4-9009e74e3ab4\",\n        \"typ\": \"ID\",\n        \"azp\": \"david\",\n        \"session_state\": \"41640fe7-39d2-44bc-818c-a3360b36fb87\",\n        \"at_hash\": \"2IGw-B9f5910Sll1tnfQRg\",\n        \"acr\": \"0\",\n        \"email_verified\": false,\n        \"hr\": \"true\",\n        \"preferred_username\": \"david\",\n        \"user\": \"david\",\n        \"subordinates\": [],\n        \"request_method\": \"GET\",\n        \"request_path\": [\"finance\", \"salary\", \"david\"]\n    }\n}\n```\n\nIn Open Policy Agent you can create policies using user roles,\nroutes, request methods etc.\n\nAn example policy (from [the official Open Policy Agent\ndocs](https://www.openpolicyagent.org/docs/v0.11.0/http-api-authorization/))\nfor this setup could look like this:\n\n```rego\npackage httpapi.authz\n\n# bob is alice's manager, and betty is charlie's.\nsubordinates = {\"alice\": [], \"charlie\": [], \"bob\": [\"alice\"], \"betty\": [\"charlie\"]}\n\n# HTTP API request\nimport input\n\ndefault allow = false\n\n# Allow users to get their own salaries.\nallow {\n  some username\n  input.request_method == \"GET\"\n  input.request_path = [\"finance\", \"salary\", username]\n  input.user == username\n}\n\n# Allow managers to get their subordinates' salaries.\nallow {\n  some username\n  input.request_method == \"GET\"\n  input.request_path = [\"finance\", \"salary\", username]\n  subordinates[input.user][_] == username\n}\n```\n\n<a name=\"auth-flow\"/>\n\n## Authentication flow\n\nUse the provided interface to set up your desired authentication flow. Then\ninsert it into `OPAMiddleware` (`fastapi_opa.auth.auth_interface.AuthInterface`).\nConsider submitting a pull request with new flows.\n\nYou can also use these ready-to-go implementations:\n\n<a name=\"api-key-auth\"/>\n\n### API key authentication\n\nIn the API key authentication a request header needs to match a given value.\n\n```python\n# Configure API keys\napi_key_config = APIKeyConfig(\n    header_key=\"test\",\n    api_key=\"1234\"\n)\napi_key_auth = APIKeyAuthentication(api_key_config)\n```\n\nIn the example the header `header[\"test\"] = \"1234\"` authenticates the request.\nFor Open Policy Agent, set user to `APIKey` and the variable `client` to the\nclient address.\n\n<a name=\"oidc-auth\"/>\n\n### OIDC authentication\n\nThe example in [How to get started](#getting-started) provides an example for\nthe implementation of the OIDC Authentication.\n\n<a name=\"saml-auth\"/>\n\n### SAML authentication\n\nFor the saml implementation create your certs using\n`openssl req -new -x509 -days 3652 -nodes -out sp.crt -keyout sp.key` and\nadd the keys to the sp section of your `settings.json`. Checkout the test\nsettings to get an idea (`tests/test_data/saml/*.json`).\nProvide the path to your own `settings.json` and `advanced_settings.json`\nin the `SAMLAuthConfig` like in the example below (don't use the test data in\nproduction).\n\n```python\nfrom fastapi_opa import OPAConfig\nfrom fastapi_opa.auth.auth_saml import SAMLAuthentication\nfrom fastapi_opa.auth.auth_saml import SAMLConfig\n\nopa_host = \"http://localhost:8181\"\n\nsaml_config = SAMLConfig(settings_directory=\"./tests/test_data/saml\")\nsaml_auth = SAMLAuthentication(saml_config)\n\nopa_config = OPAConfig(authentication=saml_auth, opa_host=opa_host,\n                       accepted_methods=[\"id_token\", \"access_token\"])\n```\n\nUpload the certificate to your identity provider. Using Keycloak as an\nidentity provider you need to configure `encrypt assertion`,\n`client signature required`, `force POST bindings` on creating the client.\nAlso configure: `Client Scopes` -> `role_list (saml)` -> `Mappers tab` ->\n`role list` -> `Single Role Attribute`\n\n<a name=\"custom-payload-enrichment\"/>\n\n## Custom payload enrichment\n\nUse the interface `fastapi_opa.opa.opa_config.Injectable` to add\nmore information to the payload sent to Open Policy Agent.\n\nConfigure the injectables in the `OPAConfig`:\n\n```python\nclass FancyInjectable(Injectable):\n    async def extract(self, request: Request) -> List:\n        return [\"some\", \"custom\", \"stuff\"]\n\nfancy_inj = FancyInjectable(\"fancy_key\", skip_endpoints=[\"/health\", \"/api/[^/]*/test])\n\nopa_config = OPAConfig(\n    authentication=oidc_auth, opa_host=opa_host, injectables=[fancy_inj]\n)\n```\n\nUse `skip_endpoints` to choose which endpoints the injectable shouldn't affect.\nTo define an endpoint, specify an exact string or a regular expression.\n\n<a name=\"gql-enrichment\"/>\n\n### GraphQL enrichment\n\nFor GraphQL you can use the ready to go injectable:\n\n```python\nfrom fastapi_opa.opa.enrichment.graphql_enrichment import GraphQLInjectable`\n\ngraphql = GraphQLInjectable(\"gql_injectable\")\nopa_config = OPAConfig(authentication=oidc_auth, opa_host=opa_host, injectables=[graphql])\n```\n",
    "bugtrack_url": null,
    "license": "GPL-3.0-or-later",
    "summary": "Fastapi OPA middleware incl. auth flow.",
    "version": "2.0.0",
    "project_urls": {
        "Homepage": "https://github.com/busykoala/fastapi-opa",
        "Repository": "https://github.com/busykoala/fastapi-opa"
    },
    "split_keywords": [
        "fastapi",
        "oidc",
        "authentication",
        "authorization",
        "saml"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "986c9b9e3f98735c09fe888e4a7e1c4f1ab29116b7930966c8df2e9eb58acd97",
                "md5": "91d879e4502db3359e9bf0b278447238",
                "sha256": "fbfe0b4ab62d02c12a273e976c7958136ee0c480d9c0666a0ea435ba52e64e25"
            },
            "downloads": -1,
            "filename": "fastapi_opa-2.0.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "91d879e4502db3359e9bf0b278447238",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8,<4.0",
            "size": 26854,
            "upload_time": "2024-02-07T22:34:13",
            "upload_time_iso_8601": "2024-02-07T22:34:13.026804Z",
            "url": "https://files.pythonhosted.org/packages/98/6c/9b9e3f98735c09fe888e4a7e1c4f1ab29116b7930966c8df2e9eb58acd97/fastapi_opa-2.0.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "12481d6bc3d361398bee456aa380683cb8a5bfb205825148ea1e34e4bc72d246",
                "md5": "7003eda807c5bcfd2476f064e85037d6",
                "sha256": "84cdf4dd56bfdc8c30a81e0a81a7faaf29071bfaf4d2640bcffd2efd80087074"
            },
            "downloads": -1,
            "filename": "fastapi_opa-2.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "7003eda807c5bcfd2476f064e85037d6",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8,<4.0",
            "size": 25791,
            "upload_time": "2024-02-07T22:34:15",
            "upload_time_iso_8601": "2024-02-07T22:34:15.376226Z",
            "url": "https://files.pythonhosted.org/packages/12/48/1d6bc3d361398bee456aa380683cb8a5bfb205825148ea1e34e4bc72d246/fastapi_opa-2.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-02-07 22:34:15",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "busykoala",
    "github_project": "fastapi-opa",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "fastapi-opa"
}
        
Elapsed time: 0.43013s