-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #69 from Intility/graph
- Loading branch information
Showing
28 changed files
with
23,431 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
from demo_project.api.api_v1.endpoints import hello_world, hello_world_multi_auth | ||
from demo_project.api.api_v1.endpoints import graph, hello_world, hello_world_multi_auth | ||
from fastapi import APIRouter | ||
|
||
api_router_azure_auth = APIRouter(tags=['hello']) | ||
api_router_azure_auth.include_router(hello_world.router) | ||
api_router_multi_auth = APIRouter(tags=['hello']) | ||
api_router_multi_auth.include_router(hello_world_multi_auth.router) | ||
api_router_graph = APIRouter(tags=['graph']) | ||
api_router_graph.include_router(graph.router) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from typing import Any | ||
|
||
import httpx | ||
from demo_project.api.dependencies import azure_scheme | ||
from demo_project.core.config import settings | ||
from fastapi import APIRouter, Depends, Request | ||
from httpx import AsyncClient | ||
from jose import jwt | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.get( | ||
'/hello-graph', | ||
summary='Fetch graph API using OBO', | ||
name='graph', | ||
operation_id='helloGraph', | ||
dependencies=[Depends(azure_scheme)], | ||
) | ||
async def graph_world(request: Request) -> Any: # noqa: ANN401 | ||
""" | ||
An example on how to use "on behalf of"-flow to fetch a graph token and then fetch data from graph. | ||
""" | ||
async with AsyncClient() as client: | ||
# Use the users access token and fetch a new access token for the Graph API | ||
obo_response: httpx.Response = await client.post( | ||
f'https://login.microsoftonline.com/{settings.TENANT_ID}/oauth2/v2.0/token', | ||
data={ | ||
'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', | ||
'client_id': settings.APP_CLIENT_ID, | ||
'client_secret': settings.GRAPH_SECRET, | ||
'assertion': request.state.user.access_token, | ||
'scope': 'https://graph.microsoft.com/user.read', | ||
'requested_token_use': 'on_behalf_of', | ||
}, | ||
) | ||
|
||
if obo_response.is_success: | ||
# Call the graph `/me` endpoint to fetch more information about the current user, using the new token | ||
graph_response: httpx.Response = await client.get( | ||
'https://graph.microsoft.com/v1.0/me', | ||
headers={'Authorization': f'Bearer {obo_response.json()["access_token"]}'}, | ||
) | ||
graph = graph_response.json() | ||
else: | ||
graph = 'skipped' | ||
|
||
# Return all the information to the end user | ||
return ( | ||
{'claims': jwt.get_unverified_claims(token=request.state.user.access_token)} | ||
| {'obo_response': obo_response.json()} | ||
| {'graph_response': graph} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
{ | ||
"label": "Settings", | ||
"position": 4, | ||
"position": 5, | ||
"collapsible": true | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"label": "Usage and FAQ", | ||
"position": 4, | ||
"collapsible": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
--- | ||
title: Accessing the user object | ||
sidebar_position: 1 | ||
--- | ||
|
||
You can access your user object in two ways, either with `Depends(<schema name>)` or with `request.state.user`. | ||
|
||
### `Depends(<schema name>)` | ||
|
||
```python title="depends_api_example.py" | ||
from fastapi import APIRouter, Depends | ||
|
||
from demo_project.api.dependencies import azure_scheme | ||
from fastapi_azure_auth.user import User | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.get( | ||
'/hello-user', | ||
response_model=User, | ||
operation_id='helloWorldApiKey', | ||
) | ||
async def hello_user(user: User = Depends(azure_scheme)) -> dict[str, bool]: | ||
""" | ||
Wonder how this auth is done? | ||
""" | ||
return user.dict() | ||
``` | ||
|
||
|
||
### `request.state.user` | ||
|
||
```python title="request_state_user_api_example.py" | ||
from fastapi import APIRouter, Depends, Request | ||
|
||
from demo_project.api.dependencies import azure_scheme | ||
from fastapi_azure_auth.user import User | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.get( | ||
'/hello-user', | ||
response_model=User, | ||
operation_id='helloWorldApiKey', | ||
dependencies=[Depends(azure_scheme)] | ||
) | ||
async def hello_user(request: Request) -> dict[str, bool]: | ||
""" | ||
Wonder how this auth is done? | ||
""" | ||
return request.state.user.dict() | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
--- | ||
title: Approval required on login | ||
sidebar_position: 5 | ||
--- | ||
|
||
If you're met by this screen when attempting to log in: | ||
|
||
![approval_required](../../static/img/usage-and-faq/approval_required.png) | ||
|
||
Then please follow the steps provided in [issue 45](https://github.com/Intility/fastapi-azure-auth/issues/45): | ||
|
||
1. Navigate to [Azure -> Azure Active Directory -> App registrations](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps) | ||
and find your backend application registration | ||
2. Go to `Expose an API` | ||
3. Under `Authorized client applications` click `Add a client application` | ||
4. Add the client ID of your OpenAPI application registration (saved as `OPENAPI_CLIENT_ID` in your `.env` file) | ||
5. Select the `api://<client id>/user_impersonation` checkbox | ||
6. Click `Add Application` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
--- | ||
title: Calling your APIs from Python | ||
sidebar_position: 3 | ||
--- | ||
|
||
In order to call your APIs from Python (or any other backend), you should use the [Client Credential Flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow). | ||
|
||
1. Navigate to [Azure -> Azure Active Directory -> App registrations](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps) | ||
and find your **OpenAPI application registration** (ideally you'd create a new one for your new application, but we'll reuse this for now) | ||
2. Navigate over to `Certificate & secrets` | ||
3. Click `New client secret` | ||
4. Give it a name and an expiry time | ||
5. Click `Add` | ||
|
||
![secret_picture](../../static/img/usage-and-faq/secret_picture.png) | ||
|
||
|
||
:::info | ||
You can use client certificates too, but we won't cover this here. | ||
::: | ||
|
||
6. Copy the secret and save it for later. | ||
|
||
![copy_secret](../../static/img/usage-and-faq/copy_secret.png) | ||
|
||
|
||
7. Fetch the token from Azure, and then call your own API endpoint: | ||
|
||
```python title="my_script.py" | ||
from httpx import AsyncClient | ||
from demo_project.core.config import settings | ||
|
||
async with AsyncClient() as client: | ||
azure_response = await client.post( | ||
url=f'https://login.microsoftonline.com/{settings.TENANT_ID}/oauth2/v2.0/token', | ||
data={ | ||
'grant_type': 'client_credentials', | ||
'client_id': settings.OPENAPI_CLIENT_ID, | ||
'client_secret': settings.CLIENT_SECRET, | ||
'scope': f'api://{settings.APP_CLIENT_ID}/.default', # note: NOT .user_impersonation | ||
} | ||
) | ||
token = azure_response.json()['access_token'] | ||
|
||
my_api_response = await client.get( | ||
'http://localhost:8000/api/v1/hello-graph', | ||
headers={'Authorization': f'Bearer {token}'}, | ||
) | ||
print(my_api_response.json()) | ||
``` | ||
|
||
:::info | ||
If you install `ipython` you can use `asyncio` code directly in your terminal. | ||
(`poetry add ipython --dev`) | ||
::: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
--- | ||
title: Using Microsoft Graph | ||
sidebar_position: 4 | ||
--- | ||
|
||
[Microsoft Graph](https://docs.microsoft.com/en-us/graph/overview) can be used together with the | ||
[On Behalf Flow (OBO)](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow), | ||
but in order to make this work you must alter your app registration configuration a bit. | ||
|
||
:::info | ||
This documentation is based off [issue #40](https://github.com/Intility/fastapi-azure-auth/issues/40) | ||
::: | ||
|
||
|
||
### Backend API App Registration | ||
1. Head over to [Azure -> Azure Active Directory -> App registrations](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps), | ||
and select your **Backend API** Application Registration | ||
2. Navigate to the `Manifest` in the menu on the left | ||
3. Add your OpenAPI/Swagger ClientID to the `knownClientApplications` (saved as `OPENAPI_CLIENT_ID` in your `.env`) | ||
|
||
![manifest](../../static/img/usage-and-faq/manifest.png) | ||
|
||
|
||
4. Select `API permissions` and ensure `User.Read` is there. If not, follow the steps in the picture below: | ||
1. `Add a permission` | ||
2. Select `Microsoft Graph` under `Microsoft APIs` | ||
3. Select `Delegated permissions` | ||
4. Search for and select `User.Read` | ||
5. Click add permission | ||
|
||
![user_read](../../static/img/usage-and-faq/user_read.png) | ||
|
||
|
||
5. Select `Certificates & Secrets` and create a secret for your backend to use in order to fetch a Graph token | ||
1. `New client secret` | ||
2. Give it a name | ||
3. Add | ||
|
||
![graph_secret](../../static/img/usage-and-faq/graph_secret.png) | ||
|
||
|
||
|
||
### OpenAPI App Registration | ||
|
||
1. Head back to [Azure -> Azure Active Directory -> App registrations](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps), | ||
and select your **OpenAPI/Swagger** Application Registration | ||
2. Select `API permissions` in the menu on the left | ||
3. Add `email`, `offline_access`, `openid`, `profile` scopes | ||
1. `Add a permission` | ||
2. Select `Microsoft Graph` under `Microsoft APIs` | ||
3. Select `Delegated permissions` | ||
4. Select the permissions | ||
5. Click add permission | ||
|
||
![user_read](../../static/img/usage-and-faq/openapi_scopes.png) | ||
|
||
|
||
### Code | ||
You can now fetch a graph token using the | ||
[OBO flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow). | ||
A full code example of an API using Graph can be found in the | ||
[demo project](https://github.com/Intility/fastapi-azure-auth/blob/main/demo_project/api/api_v1/endpoints/graph.py). |
2 changes: 1 addition & 1 deletion
2
...cs/multi-tenant/locking_down_on_roles.mdx → ...s/usage-and-faq/locking_down_on_roles.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.