# sag_py_auth
[![Maintainability][codeclimate-image]][codeclimate-url]
[![Coverage Status][coveralls-image]][coveralls-url]
[![Known Vulnerabilities](https://snyk.io/test/github/SamhammerAG/sag_py_auth/badge.svg)](https://snyk.io/test/github/SamhammerAG/sag_py_auth)
[coveralls-image]:https://coveralls.io/repos/github/SamhammerAG/sag_py_auth/badge.svg?branch=master
[coveralls-url]:https://coveralls.io/github/SamhammerAG/sag_py_auth?branch=master
[codeclimate-image]:https://api.codeclimate.com/v1/badges/2da48e3952f9640f702f/maintainability
[codeclimate-url]:https://codeclimate.com/github/SamhammerAG/sag_py_auth/maintainability
This provides a way to secure your fastapi with keycloak jwt bearer authentication.
## What it does
* Secure your api endpoints
* Verifies auth tokens: signature, expiration, issuer, audience
* Allows to set permissions by specifying roles and realm roles
## How to use
### Installation
pip install sag-py-auth
### Secure your apis
First create the fast api dependency with the auth config:
```python
from sag_py_auth.models import AuthConfig, TokenRole
from sag_py_auth.jwt_auth import JwtAuth
from fastapi import Depends
auth_config = AuthConfig("https://authserver.com/auth/realms/projectName", "myaudience")
required_roles = [TokenRole("clientname", "adminrole")]
required_realm_roles = ["additionalrealmrole"]
requires_admin = Depends(JwtAuth(auth_config, required_roles, required_realm_roles))
```
Afterwards you can use it in your route like that:
```python
@app.post("/posts", dependencies=[requires_admin], tags=["posts"])
async def add_post(post: PostSchema) -> dict:
```
Or if you use sub routes, auth can also be enforced for the entire route like that:
```python
router = APIRouter()
router.include_router(sub_router, tags=["my_api_tag"], prefix="/subroute",dependencies=[requires_admin])
```
### Get user information
The Jwt call directly returns a token object that can be used to get additional information.
Furthermore you can access the context directly:
```python
from sag_py_auth.auth_context import get_token as get_token_from_context
token = get_token_from_context()
```
This works in async calls but not in sub threads (without additional changes).
See:
* https://docs.python.org/3/library/contextvars.html
* https://kobybass.medium.com/python-contextvars-and-multithreading-faa33dbe953d
#### Methods available on the token object
* get_field_value: to get the value of a claim field (or an empty string if not present)
* get_roles: Gets the roles of a specific client
* has_role: Verify if a spcific client has a role
* get_realm_roles: Get the realm roles
* has_realm_role: Check if the user has a specific realm role
### Log user data
It is possible to log the preferred_username and the azp value (party that created the token) of the token by adding a filter.
```python
import logging
from sag_py_auth import UserNameLoggingFilter
console_handler = logging.StreamHandler(sys.stdout)
console_handler.addFilter(UserNameLoggingFilter())
```
The filter provides the following two fields as soon as the user is authenticated: user_name, authorized_party
### How a token has to look like
```json
{
"iss": "https://authserver.com/auth/realms/projectName",
"aud": ["audienceOne", "audienceTwo"],
"typ": "Bearer",
"azp": "public-project-swagger",
"preferred_username": "preferredUsernameValue",
.....
"realm_access": {
"roles": ["myRealmRoleOne"]
},
"resource_access": {
"my-client-one": {
"roles": ["a-permission-role", "user"]
},
"my-client-two": {
"roles": ["a-permission-role", "admin"]
}
}
}
```
* realm_access contains the realm roles
* resource_access contains the token roles for one or multiple clients
## How to start developing
### With vscode
Just install vscode with dev containers extension. All required extensions and configurations are prepared automatically.
### With pycharm
* Install latest pycharm
* Install pycharm plugin BlackConnect
* Install pycharm plugin Mypy
* Configure the python interpreter/venv
* pip install requirements-dev.txt
* pip install black[d]
* Ctl+Alt+S => Check Tools => BlackConnect => Trigger when saving changed files
* Ctl+Alt+S => Check Tools => BlackConnect => Trigger on code reformat
* Ctl+Alt+S => Click Tools => BlackConnect => "Load from pyproject.yaml" (ensure line length is 120)
* Ctl+Alt+S => Click Tools => BlackConnect => Configure path to the blackd.exe at the "local instance" config (e.g. C:\Python310\Scripts\blackd.exe)
* Ctl+Alt+S => Click Tools => Actions on save => Reformat code
* Restart pycharm
## How to publish
* Update the version in setup.py and commit your change
* Create a tag with the same version number
* Let github do the rest
Raw data
{
"_id": null,
"home_page": "https://github.com/SamhammerAG/sag_py_auth",
"name": "sag-py-auth",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "auth, fastapi, keycloak",
"author": "Samhammer AG",
"author_email": "support@samhammer.de",
"download_url": "https://files.pythonhosted.org/packages/5e/2e/2fdedf094c91db2af77cf85a3e1882431decf041a6514f4180f6f0c5011f/sag_py_auth-1.0.1.tar.gz",
"platform": null,
"description": "# sag_py_auth\n\n[![Maintainability][codeclimate-image]][codeclimate-url]\n[![Coverage Status][coveralls-image]][coveralls-url]\n[![Known Vulnerabilities](https://snyk.io/test/github/SamhammerAG/sag_py_auth/badge.svg)](https://snyk.io/test/github/SamhammerAG/sag_py_auth)\n\n[coveralls-image]:https://coveralls.io/repos/github/SamhammerAG/sag_py_auth/badge.svg?branch=master\n[coveralls-url]:https://coveralls.io/github/SamhammerAG/sag_py_auth?branch=master\n[codeclimate-image]:https://api.codeclimate.com/v1/badges/2da48e3952f9640f702f/maintainability\n[codeclimate-url]:https://codeclimate.com/github/SamhammerAG/sag_py_auth/maintainability\n\nThis provides a way to secure your fastapi with keycloak jwt bearer authentication.\n\n## What it does\n* Secure your api endpoints\n* Verifies auth tokens: signature, expiration, issuer, audience\n* Allows to set permissions by specifying roles and realm roles\n\n## How to use\n\n### Installation\n\npip install sag-py-auth\n\n### Secure your apis\n\nFirst create the fast api dependency with the auth config:\n```python\nfrom sag_py_auth.models import AuthConfig, TokenRole\nfrom sag_py_auth.jwt_auth import JwtAuth\nfrom fastapi import Depends\n\nauth_config = AuthConfig(\"https://authserver.com/auth/realms/projectName\", \"myaudience\")\nrequired_roles = [TokenRole(\"clientname\", \"adminrole\")]\nrequired_realm_roles = [\"additionalrealmrole\"]\nrequires_admin = Depends(JwtAuth(auth_config, required_roles, required_realm_roles))\n```\n\nAfterwards you can use it in your route like that:\n\n```python\n@app.post(\"/posts\", dependencies=[requires_admin], tags=[\"posts\"])\nasync def add_post(post: PostSchema) -> dict:\n```\n\nOr if you use sub routes, auth can also be enforced for the entire route like that:\n\n```python\nrouter = APIRouter()\nrouter.include_router(sub_router, tags=[\"my_api_tag\"], prefix=\"/subroute\",dependencies=[requires_admin])\n```\n\n### Get user information\n\nThe Jwt call directly returns a token object that can be used to get additional information.\n\nFurthermore you can access the context directly:\n```python\nfrom sag_py_auth.auth_context import get_token as get_token_from_context\ntoken = get_token_from_context()\n```\n\nThis works in async calls but not in sub threads (without additional changes).\n\nSee:\n* https://docs.python.org/3/library/contextvars.html\n* https://kobybass.medium.com/python-contextvars-and-multithreading-faa33dbe953d\n\n#### Methods available on the token object\n\n* get_field_value: to get the value of a claim field (or an empty string if not present)\n* get_roles: Gets the roles of a specific client\n* has_role: Verify if a spcific client has a role\n* get_realm_roles: Get the realm roles\n* has_realm_role: Check if the user has a specific realm role\n\n\n### Log user data\n\nIt is possible to log the preferred_username and the azp value (party that created the token) of the token by adding a filter.\n\n```python\nimport logging\nfrom sag_py_auth import UserNameLoggingFilter\n\nconsole_handler = logging.StreamHandler(sys.stdout)\nconsole_handler.addFilter(UserNameLoggingFilter())\n\n```\n\nThe filter provides the following two fields as soon as the user is authenticated: user_name, authorized_party\n\n### How a token has to look like\n\n```json\n{\n\n \"iss\": \"https://authserver.com/auth/realms/projectName\",\n \"aud\": [\"audienceOne\", \"audienceTwo\"],\n \"typ\": \"Bearer\",\n \"azp\": \"public-project-swagger\",\n \"preferred_username\": \"preferredUsernameValue\",\n .....\n \"realm_access\": {\n \"roles\": [\"myRealmRoleOne\"]\n },\n \"resource_access\": {\n \"my-client-one\": {\n \"roles\": [\"a-permission-role\", \"user\"]\n },\n \"my-client-two\": {\n \"roles\": [\"a-permission-role\", \"admin\"]\n }\n }\n}\n```\n\n* realm_access contains the realm roles\n* resource_access contains the token roles for one or multiple clients\n\n## How to start developing\n\n### With vscode\n\nJust install vscode with dev containers extension. All required extensions and configurations are prepared automatically.\n\n### With pycharm\n\n* Install latest pycharm\n* Install pycharm plugin BlackConnect\n* Install pycharm plugin Mypy\n* Configure the python interpreter/venv\n* pip install requirements-dev.txt\n* pip install black[d]\n* Ctl+Alt+S => Check Tools => BlackConnect => Trigger when saving changed files\n* Ctl+Alt+S => Check Tools => BlackConnect => Trigger on code reformat\n* Ctl+Alt+S => Click Tools => BlackConnect => \"Load from pyproject.yaml\" (ensure line length is 120)\n* Ctl+Alt+S => Click Tools => BlackConnect => Configure path to the blackd.exe at the \"local instance\" config (e.g. C:\\Python310\\Scripts\\blackd.exe)\n* Ctl+Alt+S => Click Tools => Actions on save => Reformat code\n* Restart pycharm\n\n## How to publish\n* Update the version in setup.py and commit your change\n* Create a tag with the same version number\n* Let github do the rest\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Keycloak authentication for python projects",
"version": "1.0.1",
"project_urls": {
"Bug Reports": "https://github.com/SamhammerAG/sag_py_auth/issues",
"Documentation": "https://github.com/SamhammerAG/sag_py_auth",
"Homepage": "https://github.com/SamhammerAG/sag_py_auth",
"Source": "https://github.com/SamhammerAG/sag_py_auth"
},
"split_keywords": [
"auth",
" fastapi",
" keycloak"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "881963c332dee17df2dd536ea4b7f4841c8b5593edebc29d881f024ecacec811",
"md5": "3083786c56f99ccb7821d5f65c67037b",
"sha256": "63e5db02047d1a4d20cd55ae85c50db34032db989a415fb7cee8b0c47f41d365"
},
"downloads": -1,
"filename": "sag_py_auth-1.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "3083786c56f99ccb7821d5f65c67037b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 9286,
"upload_time": "2024-09-19T11:32:48",
"upload_time_iso_8601": "2024-09-19T11:32:48.435940Z",
"url": "https://files.pythonhosted.org/packages/88/19/63c332dee17df2dd536ea4b7f4841c8b5593edebc29d881f024ecacec811/sag_py_auth-1.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "5e2e2fdedf094c91db2af77cf85a3e1882431decf041a6514f4180f6f0c5011f",
"md5": "61e9592d4573835aa89a66d40eb2c99f",
"sha256": "4a06a42245a5e625e487855e4fb7837f455ea7a4d213815e7e5680f21358e85e"
},
"downloads": -1,
"filename": "sag_py_auth-1.0.1.tar.gz",
"has_sig": false,
"md5_digest": "61e9592d4573835aa89a66d40eb2c99f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 13580,
"upload_time": "2024-09-19T11:32:49",
"upload_time_iso_8601": "2024-09-19T11:32:49.356331Z",
"url": "https://files.pythonhosted.org/packages/5e/2e/2fdedf094c91db2af77cf85a3e1882431decf041a6514f4180f6f0c5011f/sag_py_auth-1.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-09-19 11:32:49",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "SamhammerAG",
"github_project": "sag_py_auth",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"lcname": "sag-py-auth"
}