From 2bfcbcd0e04646c85ce39b7c6ee2cb11e7b78069 Mon Sep 17 00:00:00 2001 From: Artyom Vancyan Date: Thu, 17 Aug 2023 18:52:25 +0400 Subject: [PATCH 01/12] GH-15: Setup initial VitePress docs with GH Actions --- .github/workflows/docs.yml | 18 +++++++++ docs/.vitepress/config.js | 51 +++++++++++++++++++++++++ docs/deploy.sh | 7 ++++ docs/index.md | 33 ++++++++++++++++ docs/integration/configuration.md | 7 ++++ docs/integration/index.md | 7 ++++ docs/integration/settings/index.md | 7 ++++ docs/integration/settings/variables.md | 7 ++++ docs/package.json | 13 +++++++ docs/public/index.css | 11 ++++++ docs/public/logo.png | Bin 0 -> 30627 bytes 11 files changed, 161 insertions(+) create mode 100644 .github/workflows/docs.yml create mode 100644 docs/.vitepress/config.js create mode 100644 docs/deploy.sh create mode 100644 docs/index.md create mode 100644 docs/integration/configuration.md create mode 100644 docs/integration/index.md create mode 100644 docs/integration/settings/index.md create mode 100644 docs/integration/settings/variables.md create mode 100644 docs/package.json create mode 100644 docs/public/index.css create mode 100644 docs/public/logo.png diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..9c02b32 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,18 @@ +name: docs + +on: + push: + branches: [ master ] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Run deployment script on server + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + key: ${{ secrets.KEY_ED25519 }} + port: ${{ secrets.PORT }} + script: sh ~/fastapi-oauth2/docs/deploy.sh diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js new file mode 100644 index 0000000..2e114ff --- /dev/null +++ b/docs/.vitepress/config.js @@ -0,0 +1,51 @@ +export default { + title: "FastAPI OAuth2", + description: "OAuth2 authentication with support for several identity providers", + head: [ + ["link", {rel: "icon", type: "image/x-icon", href: "/logo.png"}], + ["link", {href: "/index.css", rel: "stylesheet"}], + ], + cleanUrls: true, + lang: "en-US", + base: "/fastapi-oauth2/", + themeConfig: { + siteTitle: "FastAPI OAuth2", + socialLinks: [ + { + icon: "github", + link: "https://github.com/pysnippet/fastapi-oauth2", + }, + { + icon: { + svg: "" + }, + link: "https://pysnippet.org/", + }, + ], + search: { + provider: "local", + }, + nav: [ + {text: "Home", link: "/"}, + {text: "Docs", link: "/integration/", activeMatch: /integration/}, + {text: "Contributing", link: "https://github.com/pysnippet/.github/blob/master/.github/CONTRIBUTING.md"}, + {text: "Releases", link: "https://github.com/pysnippet/fastapi-oauth2/releases"}, + ], + sidebar: [ + { + text: "Integration", + items: [ + {text: "Getting Started", link: "/integration/"}, + {text: "Configuration", link: "/integration/configuration"}, + { + text: "Settings", + items: [ + {text: "Introduction", link: "/integration/settings/"}, + {text: "Variables", link: "/integration/settings/variables"}, + ], + }, + ], + }, + ], + }, +} \ No newline at end of file diff --git a/docs/deploy.sh b/docs/deploy.sh new file mode 100644 index 0000000..a5bb8d6 --- /dev/null +++ b/docs/deploy.sh @@ -0,0 +1,7 @@ +#!/bin/bash +cd ~/fastapi-oauth2/ +git restore . +git pull +sudo rm -r /var/www/docs/fastapi-oauth2/ +cd ~/fastapi-oauth2/docs/ && npm install && npm run build +sudo cp -r ~/fastapi-oauth2/docs/.vitepress/dist/ /var/www/docs/fastapi-oauth2/ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..c5496f4 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,33 @@ +--- +layout: home +sidebar: false + +title: FastAPI OAuth2 +titleTemplate: OAuth2 authentication with support for several identity providers + +hero: + name: FastAPI OAuth2 + text: OAuth2 has never been that simple + tagline: Easy to integrate OAuth2 authentication with support for several identity providers. + image: + src: /logo.png + alt: PySnippet + actions: + - theme: brand + text: Get Started + link: /integration/ + - theme: alt + text: View on GitHub + link: https://github.com/pysnippet/fastapi-oauth2 + +features: + - icon: 🛠️ + title: Free and open source + details: Enjoy the freedom of our OSS project, giving you full access to its source code and allowing you to contribute to its development. + - icon: 🧩 + title: Easy to integrate + details: Incorporate FastAPI OAuth2 into your existing projects with its straightforward integration process, saving you time. + - icon: ⚡ + title: Compatible with FastAPI 0.68.1+ + details: The package is fully compatible with FastAPI v0.68.1 and above, ensuring smooth operation and integration with your application. +--- \ No newline at end of file diff --git a/docs/integration/configuration.md b/docs/integration/configuration.md new file mode 100644 index 0000000..d7ded63 --- /dev/null +++ b/docs/integration/configuration.md @@ -0,0 +1,7 @@ +# h1 + +... + +## h2 + +... diff --git a/docs/integration/index.md b/docs/integration/index.md new file mode 100644 index 0000000..d7ded63 --- /dev/null +++ b/docs/integration/index.md @@ -0,0 +1,7 @@ +# h1 + +... + +## h2 + +... diff --git a/docs/integration/settings/index.md b/docs/integration/settings/index.md new file mode 100644 index 0000000..d7ded63 --- /dev/null +++ b/docs/integration/settings/index.md @@ -0,0 +1,7 @@ +# h1 + +... + +## h2 + +... diff --git a/docs/integration/settings/variables.md b/docs/integration/settings/variables.md new file mode 100644 index 0000000..d7ded63 --- /dev/null +++ b/docs/integration/settings/variables.md @@ -0,0 +1,7 @@ +# h1 + +... + +## h2 + +... diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..e462a7f --- /dev/null +++ b/docs/package.json @@ -0,0 +1,13 @@ +{ + "name": "vitepress-docs", + "version": "1.0.0", + "scripts": { + "dev": "vitepress dev", + "build": "vitepress build", + "serve": "vitepress serve" + }, + "dependencies": { + "vitepress": "^1.0.0-rc.4", + "vue": "^3.3.2" + } +} \ No newline at end of file diff --git a/docs/public/index.css b/docs/public/index.css new file mode 100644 index 0000000..6ed8f70 --- /dev/null +++ b/docs/public/index.css @@ -0,0 +1,11 @@ +:root { + --vp-c-brand: rgb(0, 148, 134); + --vp-c-brand-light: var(--vp-c-brand); + --vp-c-brand-lighter: var(--vp-c-brand); + --vp-c-brand-lightest: var(--vp-c-brand); + --vp-c-brand-dark: var(--vp-c-brand); + --vp-c-brand-darker: rgba(0, 148, 134, 0.8); + + --vp-button-brand-bg: var(--vp-c-brand); + --vp-button-brand-active-bg: var(--vp-button-brand-bg); +} diff --git a/docs/public/logo.png b/docs/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ae24249581e8e723a1b9543319e635210326a82f GIT binary patch literal 30627 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

0b86RZF=Ywr1gvGDM%` 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 (TnsNvwfN&Vu;8o&NA4z^%xn61& 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 literal 0 HcmV?d00001 From e3524102f23b01833fb6749b2643cb8ac5291339 Mon Sep 17 00:00:00 2001 From: Artyom Vancyan Date: Thu, 17 Aug 2023 18:52:53 +0400 Subject: [PATCH 02/12] Update the project description --- README.md | 4 ++-- setup.cfg | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a36c6e2..f7e31ed 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ [![Tests](https://github.com/pysnippet/fastapi-oauth2/actions/workflows/tests.yml/badge.svg)](https://github.com/pysnippet/fastapi-oauth2/actions/workflows/tests.yml) [![License](https://img.shields.io/pypi/l/fastapi-oauth2.svg)](https://github.com/pysnippet/fastapi-oauth2/blob/master/LICENSE) -FastAPI OAuth2 is a middleware-based social authentication mechanism supporting several auth providers. It depends on -the [social-core](https://github.com/python-social-auth/social-core) authentication backends. +FastAPI OAuth2 is a middleware-based social authentication mechanism supporting several identity providers. It depends +on the [social-core](https://github.com/python-social-auth/social-core) authentication backends. ## Installation diff --git a/setup.cfg b/setup.cfg index 81b6a1d..e3a8bd6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ name = fastapi-oauth2 version = attr: fastapi_oauth2.__version__ author = Artyom Vancyan author_email = artyom@pysnippet.org -description = Easy to setup OAuth2 authentication with support for several auth providers. +description = Easy to integrate OAuth2 authentication with support for several identity providers. long_description = file: README.md long_description_content_type = text/markdown project_urls = From e1cf04762c2027ebf285031e85b6f535a4641efa Mon Sep 17 00:00:00 2001 From: Artyom Vancyan Date: Thu, 17 Aug 2023 18:53:24 +0400 Subject: [PATCH 03/12] Update the documentation URL metadata --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index e3a8bd6..162b380 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,7 +7,7 @@ description = Easy to integrate OAuth2 authentication with support for several i long_description = file: README.md long_description_content_type = text/markdown project_urls = - Documentation=https://github.com/pysnippet/fastapi-oauth2/ + Documentation=https://docs.pysnippet.org/fastapi-oauth2/ Source Code=https://github.com/pysnippet/fastapi-oauth2/ keywords = python From 50c5fe0a62ce1e7fbe580501c2f4c9ff145897f4 Mon Sep 17 00:00:00 2001 From: Artyom Vancyan Date: Thu, 17 Aug 2023 20:07:37 +0400 Subject: [PATCH 04/12] Add the `docs` badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f7e31ed..f01b424 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Python](https://img.shields.io/pypi/pyversions/fastapi-oauth2.svg?logoColor=white)](https://pypi.org/project/fastapi-oauth2/) [![FastAPI](https://img.shields.io/badge/fastapi-%E2%89%A50.68.1-009486)](https://pypi.org/project/fastapi-oauth2/) [![Tests](https://github.com/pysnippet/fastapi-oauth2/actions/workflows/tests.yml/badge.svg)](https://github.com/pysnippet/fastapi-oauth2/actions/workflows/tests.yml) -[![License](https://img.shields.io/pypi/l/fastapi-oauth2.svg)](https://github.com/pysnippet/fastapi-oauth2/blob/master/LICENSE) +[![Docs](https://github.com/pysnippet/fastapi-oauth2/actions/workflows/docs.yml/badge.svg)](https://github.com/pysnippet/fastapi-oauth2/actions/workflows/docs.yml) FastAPI OAuth2 is a middleware-based social authentication mechanism supporting several identity providers. It depends on the [social-core](https://github.com/python-social-auth/social-core) authentication backends. From ef38bcd4a6ca276ea822c967f0b724dd1490a830 Mon Sep 17 00:00:00 2001 From: Artyom Vancyan Date: Sat, 19 Aug 2023 20:28:14 +0400 Subject: [PATCH 05/12] GH-15: Write up the "Getting Started" and "Configuration" sections --- docs/.vitepress/config.js | 15 +++--- docs/integration/configuration.md | 67 ++++++++++++++++++++++++-- docs/integration/index.md | 35 ++++++++++++-- docs/integration/integration.md | 4 ++ docs/integration/settings/index.md | 7 --- docs/integration/settings/variables.md | 7 --- 6 files changed, 106 insertions(+), 29 deletions(-) create mode 100644 docs/integration/integration.md delete mode 100644 docs/integration/settings/index.md delete mode 100644 docs/integration/settings/variables.md diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 2e114ff..8e52eb3 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -37,13 +37,14 @@ export default { items: [ {text: "Getting Started", link: "/integration/"}, {text: "Configuration", link: "/integration/configuration"}, - { - text: "Settings", - items: [ - {text: "Introduction", link: "/integration/settings/"}, - {text: "Variables", link: "/integration/settings/variables"}, - ], - }, + {text: "Integration", link: "/integration/integration"}, + // { + // text: "Settings", + // items: [ + // {text: "Introduction", link: "/integration/settings/"}, + // {text: "Variables", link: "/integration/settings/variables"}, + // ], + // }, ], }, ], diff --git a/docs/integration/configuration.md b/docs/integration/configuration.md index d7ded63..d0d9990 100644 --- a/docs/integration/configuration.md +++ b/docs/integration/configuration.md @@ -1,7 +1,66 @@ -# h1 +# Configuration -... +The configuration for the OAuth2 clients can be provided by using the [`OAuth2Config`](#oauth2config) +and [`OAuth2Client`](#oauth2client) classes. There is an alternate way to define the configuration by using the +Python's `dict` type with the same structure as these two classes. -## h2 +## OAuth2Config -... +The `OAuth2Config` class is used to define the middleware configuration, and it has the following attributes: + +- `allow_http` - Whether allow HTTP requests or not. Defaults to `False`. +- `jwt_secret` - Secret used to sign the JWT tokens. Defaults to an empty string. +- `jwt_expires` - JWT lifetime in seconds. Defaults to 900 (15 minutes). +- `jwt_algorithm` - The algorithm used to sign the JWT tokens. Defaults to `HS256`. +- `clients` - A list of [`OAuth2Client`](#oauth2client) instances. Defaults to an empty list. + +```python +OAuth2Config( + allow_http=True, + jwt_secret=os.getenv("JWT_SECRET"), + jwt_expires=os.getenv("JWT_EXPIRES"), + jwt_algorithm=os.getenv("JWT_ALGORITHM"), + clients=[ + OAuth2Client(...), + OAuth2Client(...), + ] +) +``` + +## OAuth2Client + +The `OAuth2Client` class is used to define the configuration for a given OAuth2 client, and it has the following +attributes: + +- `backend` - A backend class from the `social_core.backends` package. +- `client_id` - A string value of the generated client ID. +- `client_secret` - A string value of the generated client secret. +- `redirect_uri` - URL to redirect to after the authentication. Defaults to the base URL. +- `scope` - A list of the desired scopes. Defaults to an empty list. +- `claims` - An instance of [`Claims`](#claims) with the claim mapping definitions. + +```python +OAuth2Client( + backend=GithubOAuth2, + client_id=os.getenv("OAUTH2_GITHUB_CLIENT_ID"), + client_secret=os.getenv("OAUTH2_GITHUB_CLIENT_SECRET"), + redirect_uri="https://example.com/dashboard", + scope=["user:email"], + claims=Claims(...), +) +``` + +## Claims + +The `Claims` class is used to define the claim mapping for a given OAuth2 client, and it has `display_name`, `identity`, +`picture`, and `email` permanent attributes. It also accepts custom attributes if your case is special. Each attribute +can have a value of a string or a callable that receives the user data and returns a string. + +```python +Claims( + # Map the `picture` claim to the `avatar_url` key in the user data. + picture="avatar_url", + # Calculate the `identity` claim based on the user data. + identity=lambda user: f"{user.provider}:{user.id}", +) +``` diff --git a/docs/integration/index.md b/docs/integration/index.md index d7ded63..07df73a 100644 --- a/docs/integration/index.md +++ b/docs/integration/index.md @@ -1,7 +1,34 @@ -# h1 +# FastAPI OAuth2 -... +FastAPI OAuth2 is a middleware-based social authentication mechanism supporting several OAuth2 providers. It leverages +the [social-core](https://github.com/python-social-auth/social-core) authentication backends and integrates seamlessly +with FastAPI applications. -## h2 +## Installation -... +This package is compatible with Python 3.7+ and FastAPI 0.68.1+ versions and is available on The Python Package Index. +So you can install it using the package installer for Python. + +```bash +python -m pip install fastapi-oauth2 +``` + +## Upgrade + +Make sure you are using the latest version of FastAPI OAuth2 for getting the latest features and the best performance. +You can check the latest releases on its [PyPI page](https://pypi.org/project/fastapi-oauth2/). + +```bash +python -m pip install --upgrade fastapi-oauth2 +``` + +## Dependencies + +FastAPI OAuth2 depends on the following packages. + +- [fastapi](https://github.com/tiangolo/fastapi) +- [httpx](https://github.com/encode/httpx) +- [oauthlib](https://github.com/oauthlib/oauthlib) +- [python-jose](https://github.com/mpdavis/python-jose) +- [social-auth-core](https://github.com/python-social-auth/social-core) +- [starlette](https://github.com/encode/starlette) diff --git a/docs/integration/integration.md b/docs/integration/integration.md new file mode 100644 index 0000000..b4dd80c --- /dev/null +++ b/docs/integration/integration.md @@ -0,0 +1,4 @@ +# Integration + +In the previous section, were described the configuration components of the OAuth2 authentication middleware and this +section will cover its integration into a FastAPI application. diff --git a/docs/integration/settings/index.md b/docs/integration/settings/index.md deleted file mode 100644 index d7ded63..0000000 --- a/docs/integration/settings/index.md +++ /dev/null @@ -1,7 +0,0 @@ -# h1 - -... - -## h2 - -... diff --git a/docs/integration/settings/variables.md b/docs/integration/settings/variables.md deleted file mode 100644 index d7ded63..0000000 --- a/docs/integration/settings/variables.md +++ /dev/null @@ -1,7 +0,0 @@ -# h1 - -... - -## h2 - -... From de01d320fae94297dc223bac30f0dd5598b8bc93 Mon Sep 17 00:00:00 2001 From: Artyom Vancyan Date: Sat, 19 Aug 2023 20:34:04 +0400 Subject: [PATCH 06/12] Remove requirements.txt --- requirements.txt | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index a388b0a..0000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -fastapi>=0.68.1 -httpx>=0.23.0 -oauthlib>=3.2.2 -python-jose>=3.3.0 -social-auth-core>=4.4.2 -starlette>=0.19.1 From 644d7a474012df5144182f5963eb2141b51ccef4 Mon Sep 17 00:00:00 2001 From: Artyom Vancyan Date: Sat, 19 Aug 2023 20:36:44 +0400 Subject: [PATCH 07/12] Make all user attributes accessible by `__getattr__` --- examples/demonstration/config.py | 4 ++-- src/fastapi_oauth2/middleware.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/demonstration/config.py b/examples/demonstration/config.py index be64b0f..9877102 100644 --- a/examples/demonstration/config.py +++ b/examples/demonstration/config.py @@ -23,7 +23,7 @@ scope=["user:email"], claims=Claims( picture="avatar_url", - identity=lambda user: "%s:%s" % (user.get("provider"), user.get("id")), + identity=lambda user: f"{user.provider}:{user.id}", ), ), OAuth2Client( @@ -32,7 +32,7 @@ client_secret=os.getenv("OAUTH2_GOOGLE_CLIENT_SECRET"), scope=["openid", "profile", "email"], claims=Claims( - identity=lambda user: "%s:%s" % (user.get("provider"), user.get("sub")), + identity=lambda user: f"{user.provider}:{user.sub}", ), ), ] diff --git a/src/fastapi_oauth2/middleware.py b/src/fastapi_oauth2/middleware.py index 5dd5eb1..58dc564 100644 --- a/src/fastapi_oauth2/middleware.py +++ b/src/fastapi_oauth2/middleware.py @@ -115,6 +115,8 @@ def __getprop__(self, item, default="") -> Any: return item(self) return self.get(item, default) + __getattr__ = __getprop__ + class OAuth2Backend(AuthenticationBackend): """Authentication backend for AuthenticationMiddleware.""" From 87fff49ffffe59a78072b9b31f14db09c8b483ed Mon Sep 17 00:00:00 2001 From: Artyom Vancyan Date: Sat, 19 Aug 2023 20:39:49 +0400 Subject: [PATCH 08/12] Add tests for permanent and custom claims mappings --- tests/conftest.py | 10 ++++++--- tests/test_claims.py | 52 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 tests/test_claims.py diff --git a/tests/conftest.py b/tests/conftest.py index b96e6c5..ac6f86b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -54,9 +54,13 @@ def user(request: Request, _: str = Depends(oauth2)): @app_router.get("/auth") def auth(request: Request): access_token = request.auth.jwt_create({ - "name": "test", - "sub": "test", - "id": "test", + "id": 54321, + "followers": 80, + "sub": "1234567890", + "name": "John Doe", + "provider": "github", + "emails": ["john.doe@test.py"], + "image": "https://example.com/john.doe.png", }) response = Response() response.set_cookie( diff --git a/tests/test_claims.py b/tests/test_claims.py new file mode 100644 index 0000000..f607d22 --- /dev/null +++ b/tests/test_claims.py @@ -0,0 +1,52 @@ +import pytest +from fastapi import APIRouter +from fastapi import Request +from httpx import AsyncClient + +from fastapi_oauth2.claims import Claims + + +@pytest.mark.anyio +async def test_permanent_claims_mapping(get_app): + app = get_app() + router = APIRouter() + + @router.get("/test_claims") + def test_claims(request: Request): + user = request.user.use_claims(Claims()) # use default claims mapping + assert user.display_name == "John Doe" + assert user.identity == "1234567890" + assert user.picture == "" + assert user.email == "" + + app.include_router(router) + + async with AsyncClient(app=app, base_url="http://test") as client: + await client.get("/auth") # Simulate login + await client.get("/test_claims") + + +@pytest.mark.anyio +async def test_custom_claims_mapping(get_app): + app = get_app() + router = APIRouter() + + @router.get("/test_claims") + def test_claims(request: Request): + user = request.user.use_claims(Claims( + picture="image", + email=lambda u: u.emails[0], + identity=lambda u: f"{u.provider}:{u.sub}", + is_popular=lambda u: u.followers > 100, + )) # use custom claims mapping + assert user.display_name == "John Doe" + assert user.identity == "github:1234567890" + assert user.picture == "https://example.com/john.doe.png" + assert user.email == "john.doe@test.py" + assert not user.is_popular + + app.include_router(router) + + async with AsyncClient(app=app, base_url="http://test") as client: + await client.get("/auth") # Simulate login + await client.get("/test_claims") From ef38eb9d501ba2554bcd879d702ee50389d36cca Mon Sep 17 00:00:00 2001 From: Artyom Vancyan Date: Sun, 20 Aug 2023 21:11:19 +0400 Subject: [PATCH 09/12] GH-15: Write up the "Integration" page of the docs --- docs/.vitepress/config.js | 7 ---- docs/integration/integration.md | 74 ++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 8e52eb3..4555c23 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -38,13 +38,6 @@ export default { {text: "Getting Started", link: "/integration/"}, {text: "Configuration", link: "/integration/configuration"}, {text: "Integration", link: "/integration/integration"}, - // { - // text: "Settings", - // items: [ - // {text: "Introduction", link: "/integration/settings/"}, - // {text: "Variables", link: "/integration/settings/variables"}, - // ], - // }, ], }, ], diff --git a/docs/integration/integration.md b/docs/integration/integration.md index b4dd80c..46d0dea 100644 --- a/docs/integration/integration.md +++ b/docs/integration/integration.md @@ -1,4 +1,76 @@ +--- +outline: deep +--- + # Integration In the previous section, were described the configuration components of the OAuth2 authentication middleware and this -section will cover its integration into a FastAPI application. +section covers its integration into a FastAPI app. + +## OAuth2Middleware + +The `OAuth2Middleware` is an authentication middleware which means that its usage makes the `user` and `auth` attributes +available in the [request](https://www.starlette.io/requests/) context. It has a mandatory argument `config` of +[`OAuth2Config`](/integration/configuration#oauth2config) instance that has been discussed at the previous section and +an optional argument `callback` which is a callable that is called when the authentication succeeds. + +```python +app: FastAPI + +def on_auth_success(auth: Auth, user: User): + """This could be async function as well.""" + +app.add_middleware( + OAuth2Middleware, + config=OAuth2Config(...), + callback=on_auth_success, +) +``` + +### Auth context + +This is extended version of Starlette's [`AuthCredentials`](https://www.starlette.io/authentication/#authcredentials) +and the difference is that the `Auth` has additionally the list of the `clients` that can be used in the Jinja templates +to display them dynamically, and the `provider` is an item of the `clients` that was used to authenticate the current +user. Also, there are some methods for managing the JWT tokens: `jwt_encode`, `jwt_decode`, and `jwt_create`. + +### User context + +This is the extended version of Starlette's [`BaseUser`](https://www.starlette.io/authentication/#users) and apart from +the default `is_authenticated` and `display_name` and the extended `identity`, `picture`, and `email` properties, it +also contains all attributes of the user received from a certain provider. + +### Callback + +The `callback` is called with the [`Auth`](#auth-context) and [`User`](#user-context) arguments when the authentication +succeeds. This can be used for migrating an external user into the system of the existing application. Apart from other +OAuth2 solutions that force using their base user models, certain architectural designs, or a database from a limited +set of choices, this kind of solution gives developers freedom. + +## Router + +Router defines the endpoints that are used for the authentication and logout. The authentication is done by +the `/oauth2/{provider}/auth` endpoint and the logout is done by the `/oauth2/logout` endpoint. The `{provider}` is the +name of the provider that is going to be used for the authentication and coincides with the `name` attribute of +the `backend` provided to the certain `OAuth2Client`. + +```python +from fastapi_oauth2.router import router as oauth2_router + +app.include_router(oauth2_router) +``` + +## Security + +FastAPI's `OAuth2`, `OAuth2PasswordBearer` and `OAuth2AuthorizationCodeBearer` security models are supported, but in +case your application uses cookies for storing the authentication tokens, you can use the same named security models +from the `fastapi_oauth2.security` module. + +## Examples + +Working examples of all the above-described topics can be found in +the [examples](https://github.com/pysnippet/fastapi-oauth2/tree/master/examples) and +the [tests](https://github.com/pysnippet/fastapi-oauth2/tree/master/tests) directories of the repository. Also, feel +free to open an [issue](https://github.com/pysnippet/fastapi-oauth2/issues/new/choose) or +a [discussion](https://github.com/pysnippet/fastapi-oauth2/discussions/new/choose) if you have any questions not covered +by the documentation. From 53df2d6c578ab751a2e68a12d7c8e12ba2359ef0 Mon Sep 17 00:00:00 2001 From: Artyom Vancyan Date: Sun, 20 Aug 2023 21:48:15 +0400 Subject: [PATCH 10/12] GH-15: Shorten README by removing the docs from it --- README.md | 87 +++++++------------------------------------------------ 1 file changed, 10 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index f01b424..a0a3575 100644 --- a/README.md +++ b/README.md @@ -6,72 +6,15 @@ [![Tests](https://github.com/pysnippet/fastapi-oauth2/actions/workflows/tests.yml/badge.svg)](https://github.com/pysnippet/fastapi-oauth2/actions/workflows/tests.yml) [![Docs](https://github.com/pysnippet/fastapi-oauth2/actions/workflows/docs.yml/badge.svg)](https://github.com/pysnippet/fastapi-oauth2/actions/workflows/docs.yml) -FastAPI OAuth2 is a middleware-based social authentication mechanism supporting several identity providers. It depends -on the [social-core](https://github.com/python-social-auth/social-core) authentication backends. - -## Installation - -```shell -python -m pip install fastapi-oauth2 -``` - -## Configuration - -Configuration requires you to provide the JWT requisites and define the clients of the particular providers. The -middleware configuration is declared with the `OAuth2Config` and `OAuth2Client` classes. - -### OAuth2Config - -- `allow_http` - Allow insecure HTTP requests. Defaults to `False`. -- `jwt_secret` - The secret key used to sign the JWT. Defaults to `None`. -- `jwt_expires` - The expiration time of the JWT in seconds. Defaults to `900`. -- `jwt_algorithm` - The algorithm used to sign the JWT. Defaults to `HS256`. -- `clients` - The list of the OAuth2 clients. Defaults to `[]`. - -### OAuth2Client - -- `backend` - The [social-core](https://github.com/python-social-auth/social-core) authentication backend classname. -- `client_id` - The OAuth2 client ID for the particular provider. -- `client_secret` - The OAuth2 client secret for the particular provider. -- `redirect_uri` - The OAuth2 redirect URI to redirect to after success. Defaults to the base URL. -- `scope` - The OAuth2 scope for the particular provider. Defaults to `[]`. -- `claims` - Claims mapping for the certain provider. - -It is also important to mention that for the configured clients of the auth providers, the authorization URLs are -accessible by the `/oauth2/{provider}/auth` path where the `provider` variable represents the exact value of the auth -provider backend `name` attribute. - -```python -from fastapi_oauth2.claims import Claims -from fastapi_oauth2.client import OAuth2Client -from fastapi_oauth2.config import OAuth2Config -from social_core.backends.github import GithubOAuth2 - -oauth2_config = OAuth2Config( - allow_http=False, - jwt_secret=os.getenv("JWT_SECRET"), - jwt_expires=os.getenv("JWT_EXPIRES"), - jwt_algorithm=os.getenv("JWT_ALGORITHM"), - clients=[ - OAuth2Client( - backend=GithubOAuth2, - client_id=os.getenv("OAUTH2_CLIENT_ID"), - client_secret=os.getenv("OAUTH2_CLIENT_SECRET"), - redirect_uri="https://pysnippet.org/", - scope=["user:email"], - claims=Claims( - picture="avatar_url", - identity=lambda user: "%s:%s" % (user.get("provider"), user.get("id")), - ), - ), - ] -) -``` +FastAPI OAuth2 is a middleware-based social authentication mechanism supporting several OAuth2 providers. It leverages +the [social-core](https://github.com/python-social-auth/social-core) authentication backends and integrates seamlessly +with FastAPI applications. ## Integration -To integrate the package into your FastAPI application, you need to add the `OAuth2Middleware` with particular configs -in the above-represented format and include the router to the main router of the application. +For integrating the package into an existing FastAPI application, the router with OAuth2 routes and +the `OAuth2Middleware` with particular [configs](https://docs.pysnippet.org/fastapi-oauth2/integration/configuration) +should be added to the application. ```python from fastapi import FastAPI @@ -80,24 +23,14 @@ from fastapi_oauth2.router import router as oauth2_router app = FastAPI() app.include_router(oauth2_router) -app.add_middleware(OAuth2Middleware, config=oauth2_config) -``` - -After adding the middleware, the `user` attribute will be available in the request context. It will contain the user -data provided by the OAuth2 provider. - -```jinja2 -{% if request.user.is_authenticated %} - Sign out -{% else %} - Sign in -{% endif %} +app.add_middleware(OAuth2Middleware, config=OAuth2Config(...)) ``` ## Contribute -Any contribution is welcome. If you have any ideas or suggestions, feel free to open an issue or a pull request. And -don't forget to add tests for your changes. +Any contribution is welcome. Always feel free to open an issue or a discussion if you have any questions not covered by +the documentation. If you have any ideas or suggestions, please, open a pull request. Your name will shine in our +contributors' list. Be proud of what you build! ## License From f5548d495c43d7ac18eae67bf138d1e5ed4bbe9a Mon Sep 17 00:00:00 2001 From: Artyom Vancyan Date: Sun, 20 Aug 2023 21:50:25 +0400 Subject: [PATCH 11/12] Upgrade the version to the first `beta` series --- src/fastapi_oauth2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fastapi_oauth2/__init__.py b/src/fastapi_oauth2/__init__.py index 5186ae4..b63a7ea 100644 --- a/src/fastapi_oauth2/__init__.py +++ b/src/fastapi_oauth2/__init__.py @@ -1 +1 @@ -__version__ = "1.0.0-alpha.2" +__version__ = "1.0.0-beta" From 1573b4c1461724f421aa636831e8717793221580 Mon Sep 17 00:00:00 2001 From: Artyom Vancyan Date: Sun, 20 Aug 2023 21:50:46 +0400 Subject: [PATCH 12/12] Update the "Development Status" classifier --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 162b380..f8a7875 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,7 +27,7 @@ license_files = LICENSE platforms = unix, linux, osx, win32 classifiers = Operating System :: OS Independent - Development Status :: 3 - Alpha + Development Status :: 4 - Beta Framework :: FastAPI Programming Language :: Python Programming Language :: Python :: 3