Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/hvalfangst/azure-oauth2-oidc-auth-code-flow-client-server-example-python
Oauth2 on Azure demonstrated with client and server applications in Python. Client utilizes OIDC with auth code flow.
https://github.com/hvalfangst/azure-oauth2-oidc-auth-code-flow-client-server-example-python
az-204 azure fastapi github-actions oauth2 oauth2-authorization-code-flow openid-connect python
Last synced: 3 months ago
JSON representation
Oauth2 on Azure demonstrated with client and server applications in Python. Client utilizes OIDC with auth code flow.
- Host: GitHub
- URL: https://github.com/hvalfangst/azure-oauth2-oidc-auth-code-flow-client-server-example-python
- Owner: hvalfangst
- Created: 2024-10-21T15:10:12.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2024-10-27T20:40:19.000Z (3 months ago)
- Last Synced: 2024-10-27T21:25:14.549Z (3 months ago)
- Topics: az-204, azure, fastapi, github-actions, oauth2, oauth2-authorization-code-flow, openid-connect, python
- Language: Python
- Homepage:
- Size: 800 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Azure OAuth2 OIDC Auth Code Flow demonstration
The goal of this repository is to demonstrate how to incorporate [OAuth 2.0](https://datatracker.ietf.org/doc/html/rfc6749) on Azure **WITHOUT** the use of [MSAL](https://learn.microsoft.com/en-us/entra/identity-platform/msal-overview) for learning purposes.
In a production environment one should (almost) always use MSAL or similar battle-tested libraries, but it is vital for any engineer to understand what is going on under the hood instead of just blindly calling a library which
automagically solves all your needs.The repo contains code for both the client and the server. The client is utilizing [OpenID Connect (OIDC)](https://auth0.com/docs/authenticate/protocols/openid-connect-protocol) with
Auth code flow. A comprehensive guide is included on how to register the client and server on Azure Entra ID.## Requirements
- **Platform**: x86-64, Linux/WSL
- **Programming Language**: [Python 3](https://www.python.org/downloads/)
- **Azure Account**: Access to [Azure Subscription](https://azure.microsoft.com/en-us/pricing/purchase-options/azure-account)
- **IAC Tool**: [Terraform](https://www.terraform.io/)## Allocate resources
The script [up](up.sh) provisions Azure resources by applying our [Terraform script](infra/terraform.tf).
It is necessary to create a file named **terraform.tfvars** in the [infra](infra) directory. This file holds sensitive information
necessary for terraform to be able to interact with your cloud resources, namely that of your tenant and subscription id.
An exemption for this file has been added in our [.gitignore](.gitignore) so that you do not accidentally commit it.The file structure is as follows:
![screenshot](images/terraform_tfvars.png)
## Set up CI/CD via Deployment Center
Now that we have our new Web App resource up and running on Azure, we may proceed to set up our means of deploying our code to the
aforementioned Web App. We will do so by connecting our Web App to our GitHub repository. Azure Web Apps has the ability
to create a fully fledged CI/CD pipeline in the form of a GitHub Action Workflows script, which it commits on our behalf. As part of this pipeline a managed identify
will be created in Azure in order to authenticate requests. Secrets will be automatically created and referenced in the CI/CD script by Azure.Click on the **Deployment Center** section under the **Deployment** blade. Choose GitHub as source and set the appropriate organization, repository and branch.
For authentication keep it as is (user-assigned identity). Click on the **Save** button in the top left corner.![screenshot](images/deployment_center.png)
After the changes have persisted, navigate to your GitHub repository. A new commit which contains the CI/CD workflows file should be present. As mentioned earlier,
this has been committed by Azure on our behalf.![screenshot](images/github_workflow_commit.png)
Navigate to the bottom of the workflow file. Take notice of the three secrets being referenced.
![screenshot](images/github_workflow_secrets.png)
If you navigate to your secrets and variables associated with your GitHub Actions you will see that there are three new secrets, the same referenced above. Again,
these have been set by Azure on your behalf in order to set up authentication with our managed identity which was created as part of the Deployment Center rollout.For the CI/CD workflow script to actually work, we have to make some adjustments. Remember, this repo contains code for both the client and server -
which are located in their own directories. The autogenerated script assumes that the files are located in the root folder, which is not the case here.
Thus, we need to change the script to reference files located under the server directory, as we are to deploy our server.The final pipeline definition should look like [this](.github/workflows/main_hvalfangstlinuxwebapp.yml).
## Deploy API
In order to deploy our code to our Azure Web App slot, we need to trigger the newly registered GitHub Actions Workflow manually. Head over to the **Actions** section of your repository. Click on the **Run workflow** button located in the right corner.
![screenshot](images/github_actions.png)
Running said action should result in the following:
![screenshot](images/github_actions_dispatched_task.png)
Navigate to the **Deployment Center** section of your Azure Web App. A new deployment will be visible. Commit author and message will be equal to that of GitHub.
![screenshot](images/deployment_center_post_action.png)
Now that we know that it deployed successfully it is finally time to access the API. Click on URI associated with **Default Domain**
![screenshot](images/overview_default_domain.png)
You will be prompted with the following default page, which indicates that the API is up and running.
![screenshot](images/firefox_api_home.png)
## Register API on Azure AD
Now that we have deployed our API to Azure Web Apps, we need to register it on Microsoft Entra ID.
### Create a new app registration
Navigate to the **App registrations** blade and click on **New registration** button in the top left tab
![screenshot](images/azuread_app_registrations.png)
Fill in the name of your application, whether it is single or multi-tenant and the redirect URI.
![screenshot](images/azuread_new_app_registration.png)
In our case the redirect URI is necessary and **MUST** be set to
http://localhost:8000/auth/callback as this is an actual [endpoint](app/routers/auth.py) in our auth router. Click on the **Register** button (omitted) to complete the app registration.![screenshot](images/azuread_hvalfangst_app_registration.png)
When the registration has succeeded, one will be redirected to the associated newly created registration. Proceed to copy & store the **client** and **tenant ID** for future use.
### Generate client secret
Now that we have registered our API, we need to generate an associated client secret. Proceed to click on **New client secret**
![screenshot](images/client_secret.png)
Enter a description and an expiry date. For demonstration purposes I set the secret duration to 1 day. Click on the **Add** button on the bottom left.
![screenshot](images/add_secret.png)
Now that we have our secret it is important to copy and store it's associated value. It is this which is referred to as **client secret**. It is important not to mix this up with **Secret ID**, which is merely the ID of the resource.
![screenshot](images/hvalfangst_api_secret.png)
### Create scope
Now that we have our client secret we may proceed to register the actual scope available in the API. In order to do so one must navigate to the **Expose an API** blade. Click on **Add a scope**.
![screenshot](images/expose_api.png)
You will be prompted to set an application id uri. Keep the autogenerated one as is and click **Save and continue**.
![screenshot](images/application_id_uri.png)
After an application URI has been registered, you will be prompted to register a scope. Proceed to set the scope name of **Heroes.Read**. Note that one is free to
set any scope name that one so desires, but it is common practice utilizing the controller domain followed by some access rights categories, such as **READ**, **WRITE**, **EDIT** and **DELETE**. Thus,
our chosen scope grants the right to access the GET endpoints contained in the [heroes router](app/routers/heroes.py), which are utilized in order to list all heroes and view a specific one.
One could take inspiration from Azure's built-in scopes for certain resources, such as Key Vault, Blob Storage etc.![screenshot](images/heroes_read_scope.png)
Copy the value associated with the newly created scope.
![screenshot](images/heroes_read_scope_created.png)
### Create environment file
In order for this application to work, one has to have a file named **.env_oauth** in the root folder.
This file is to contain variables utilized in order to communicate with the Azure Authorization server.These variables are the azure **client and tenant id**, the **client secret**, the **api scope** and the **redirect uri**. Recall that you were instructed to copy and store
the associated values used to populate these variables in earlier sections.The final file should look as follows:
![screenshot](images/oauth_env.png)
## Flow: callback
As mentioned earlier, we have a callback endpoint registered, whose URI matches the one specified in the registration.
![screenshot](images/callback_endpoint.png)
After the user logs in via Microsoft, they are redirected to this endpoint. The redirection includes an authorization code in the query params as such:
```http request
http://localhost:8000/auth/callback?code=AUTH_CODE
```This code will in turn be exchanged for an access token in the [get_access_token](app/services/auth_service.py) function:
![screenshot](images/get_access_token.png)
As one may observe the token is used in conjunction with the client id and secret, hence the code being in a query param in the callback. If not it would be a major security vulnerability.
Note that for some applications such as mobile apps and SPA's (12) one should not operate with client secrets as storing them safely locally is impossible. For instance, a hacker may easily decompile
your beloved app and grab the client secret as it would be imprinted in the app itself.## Running API
```bash
python -m uvicorn app.main:app --reload
```