docs: python django example for login in secure api (#7285)

* docs: python django example docs

* docs: python django example docs

* docs: python django example docs

* docs: change django example with review

* docs: python django example docs

* docs: python django example docs

* docs: apply suggestions from code review

Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>

* docs: python django example docs

* docs: python django example docs

* docs: python django example docs

---------

Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
This commit is contained in:
Stefan Benz 2024-02-02 16:20:45 +01:00 committed by GitHub
parent e699103303
commit c081f72d85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 580 additions and 2 deletions

View File

@ -0,0 +1,53 @@
1. Go to your Project and click on the **New** button as shown below.
![Register the API](/img/examples/secure-api/app-jwt/1.png)
2. Give a name to your application (Test API is the name given below) and select type **API**.
![Register the API](/img/examples/secure-api/app-jwt/2.png)
3. Select **JWT** as the authentication method and click **Continue**.
![Register the API](/img/examples/secure-api/app-jwt/3.png)
4. Now review your configuration and click **Create**.
![Register the API](/img/examples/secure-api/app-jwt/4.png)
5. You will now see the APIs **Client ID**. You will not see a Client Secret because we are using a private JWT key.
![Register the API](/img/examples/secure-api/app-jwt/5.png)
6. Next, we must create the key pairs. Click on **New**.
![Register the API](/img/examples/secure-api/app-jwt/6.png)
7. Select **JSON** as the type of key. You can also set an expiration time for the key or leave it empty. Click on **Add**.
![Register the API](/img/examples/secure-api/app-jwt/7.png)
8. Download the created key by clicking the **Download** button and then click **Close**.
![Register the API](/img/examples/secure-api/app-jwt/8.png)
9. The key will be downloaded.
![Register the API](/img/examples/secure-api/app-jwt/9.png)
10. When you click on URLs on the left, you will see the relevant OIDC URLs. Note down the **issuer** URL, **token_endpoint** and **introspection_endpoint**.
![Register the API](/img/examples/secure-api/app-jwt/10.png)
11. The key that you downloaded will be of the following format.
```
{
"type":"application",
"keyId":"<YOUR_KEY_ID>",
"key":"-----BEGIN RSA PRIVATE KEY-----\<YOUR_PRIVATE_KEY>\n-----END RSA PRIVATE KEY-----\n",
"appId":"<YOUR_APP_ID>",
"clientId":"<YOUR_CLIENT_ID>"
}
```
12. Also note down the **Resource ID** of your project.
![Register the API](/img/examples/secure-api/app-jwt/11.png)

View File

@ -0,0 +1,49 @@
1. Go to the **Users** tab in your organization as shown below and click on the **Service Users** tab.
![Register the API](/img/examples/secure-api/service-user-jwt/1.png)
2. To add a service user, click on the **New** button.
![Register the API](/img/examples/secure-api/service-user-jwt/2.png)
3. Next, add the details of the service user and select either **Bearer** or **JWT** for **Access Token Type** and click on **Create**. For this example, we will select **JWT**.
![Register the API](/img/examples/secure-api/service-user-jwt/3.png)
4. Now you will see the saved details.
![Register the API](/img/examples/secure-api/service-user-jwt/4.png)
5. Next, we need to generate a private-public key pair in ZITADEL and you must get the private key to sign your JWT. Go to **Keys** and click on **New**.
![Register the API](/img/examples/secure-api/service-user-jwt/5.png)
6. Select type **JSON** and click **Add**.
![Register the API](/img/examples/secure-api/service-user-jwt/6.png)
7. Download the key by clicking **Download**. After the download, click **Close**.
![Register the API](/img/examples/secure-api/service-user-jwt/7.png)
8. You will see the following screen afterwards.
![Register the API](/img/examples/secure-api/service-user-jwt/8.png)
9. The downloaded key will be of the following format:
```
{
"type":"serviceaccount",
"keyId":"<YOUR_KEY_ID>",
"key":"-----BEGIN RSA PRIVATE KEY-----\n<YOUR_KEY>\n-----END RSA PRIVATE KEY-----\n",
"userId":"<YOUR_USER_ID>"
}
```

View File

@ -0,0 +1,36 @@
In order to access this route, you must create the role `read:messages` in your ZITADEL project and also create an authorization for the service user you created by adding the role to the user. Follow these steps to do so:
1. Go to your project and select **Roles**. Click **New**.
![Register the API](/img/examples/secure-api/service-user-jwt/scopes/1.png)
2. Add the `read:messages` role as shown below and click **Save**.
![Register the API](/img/examples/secure-api/service-user-jwt/scopes/2.png)
3. You will see the created role listed.
![Register the API](/img/examples/secure-api/service-user-jwt/scopes/3.png)
4. To assign this role to a user, click on **Authorizations**.
![Register the API](/img/examples/secure-api/service-user-jwt/scopes/4.png)
5. Select the user you want to assign the role to.
![Register the API](/img/examples/secure-api/service-user-jwt/scopes/5.png)
6. Select the project where this authorization is applicable.
![Register the API](/img/examples/secure-api/service-user-jwt/scopes/6.png)
7. Click **Continue**.
![Register the API](/img/examples/secure-api/service-user-jwt/scopes/7.png)
8. Select the role **read:messages** and click **Save**.
![Register the API](/img/examples/secure-api/service-user-jwt/scopes/8.png)
9. You will now see the your service user has been assigned the role **read:messages**.
![Register the API](/img/examples/secure-api/service-user-jwt/scopes/9.png)

View File

@ -0,0 +1,9 @@
To install Django:
```bash
python -m pip install Django
```
Then in your folder of choice call the following command to create a Django base:
```bash
django-admin startproject mysite .
```

View File

@ -0,0 +1,5 @@
As we use local environmental variables please install dotenv:
```bash
python -m pip install python-dotenv
```

View File

@ -0,0 +1 @@
You have to install Python as described in [their documentation](https://wiki.python.org/moin/BeginnersGuide/Download).

View File

@ -117,11 +117,20 @@ Our examples cover a range of programming languages and frameworks, so no matter
<td width="100px">
<img src="/docs/img/tech/python.svg" alt="python"/>
</td>
<td>Python3 Flask Web</td>
<td>Python Flask Web</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td width="100px">
<img src="/docs/img/tech/python.svg" alt="python"/>
</td>
<td>Python Django Web</td>
<td><a href="https://github.com/zitadel/example-python-django-oidc" target="_blank"><i class="lab la-github"></i></a></td>
<td><a href="/examples/login/django">Guide</a></td>
<td></td>
</tr>
<tr>
<td width="100px">
<img src="/docs/img/tech/dotnet.svg" alt="dotnet"/>
@ -161,6 +170,15 @@ Our examples cover a range of programming languages and frameworks, so no matter
<td><a href="./secure-api/python-flask">Guide</a></td>
<td></td>
</tr>
<tr>
<td>
<img src="/docs/img/tech/python.svg" alt="phyton"/>
</td>
<td>Python Django</td>
<td><a href="https://github.com/zitadel/example-python-django-oauth" target="_blank"><i class="lab la-github"></i></a></td>
<td><a href="./secure-api/django">Guide</a></td>
<td></td>
</tr>
<tr>
<td>
<img src="/docs/img/tech/dotnet.svg" alt="dotnet"/>

View File

@ -0,0 +1,241 @@
---
title: ZITADEL with Django Python
sidebar_label: Django
---
import SetupPython from '../imports/_setup_python.mdx';
import SetupDjango from '../imports/_setup_django.mdx';
import SetupDotenv from '../imports/_setup_dotenv.mdx';
This integration guide demonstrates the recommended way to incorporate ZITADEL into your Django Python application.
It explains how to enable user login in your application and how to incorporate the ZITADEL users into the existing AuthenticationBackend.
By the end of this guide, your application will have login functionality with basic role mapping, admin console and polls as described in the Django guide.
:::info
This documentation references our [example](https://github.com/zitadel/example-django-python-oidc) on GitHub.
:::
## ZITADEL setup
Before we can start building our application, we have to do a few configuration steps in ZITADEL Console.
### Project roles
The Example expects [user roles](guides/integrate/retrieve-user-roles) to be returned after login.
This example expects 3 different roles:
- `admin`: superuser with permissions to use the admin console
- `staff`: user with permissions to see results of the polls
- `user`: normal user with permission to vote on the existing polls
In your project settings make sure the "Assert Roles On Authentication" is enabled.
![Project settings in console](/img/django/project-settings.png)
In the project Role tab, add 3 special roles:
- `admin`
- `staff`
- `user`
If none of the roles is provided as a user, the user in Django will not be created.
![Project roles in console](/img/django/project-roles.png)
Finally, we can assign the roles to users in the project's authorizations tab.
![Project authorizations in console](/img/django/project-authorizations.png)
### Set up application and obtain secrets
Next you will need to provide some information about your app.
In your Project, add a new application at the top of the page.
Select Web application type and continue.
We use [Authorization Code](/apis/openidoauth/grant-types#authorization-code) for our Django application.
![Create app in console](/img/django/app-create.png)
Select `CODE` in the next step. This makes sure you still get a secret. Note that the secret never gets exposed on the browser and is therefore kept in a confidential environment. Safe the generated secret for later use.
![Configure app authentication method in console](/img/django/app-auth-method.png)
With the Redirect URIs field, you tell ZITADEL where it is allowed to redirect users to after authentication. For development, you can set dev mode to `true` to enable insecure HTTP and redirect to a `localhost` URI.
For the example application we are writing use:
- `http://localhost:8000/oidc/callback/` as Redirect URI
- `http://localhost:8000/oidc/logout/` as post-logout URI.
![Configure app redirects console](/img/django/app-redirects.png)
After the final step you are presented with a client ID and secret.
Copy and paste them to a safe location for later use by the application.
The secret will not be displayed again, but you can regenerate one if you loose it.
## Setup new Django application
### Setup Python
<SetupPython/>
### Install dependencies
For this example we need the following dependencies:
- `django`: to create an API with django
- `python-dotenv`: to use environment variables in the configuration
- `mozilla-django-oidc`: client-side OIDC functionality
For the dependencies we need a requirements.txt-file with the following content:
```python reference
https://github.com/zitadel/example-python-django-oidc/blob/main/requirements.txt
```
Then install all dependencies with:
```bash
python -m pip install -U requirements.txt
```
The used base is the "Writing your first Django app" from the Django documentation under [https://docs.djangoproject.com/en/5.0/intro/](https://docs.djangoproject.com/en/5.0/intro/),
which has documented additional parts in to use [mozilla-django-oidc](https://github.com/mozilla/mozilla-django-oidc) to integrate ZITADEL as AuthenticationBackend.
:::info
Skip this step if you are connecting ZITADEL to an existing application.
:::
## Define the Django app
### Create the settings.py to include mozilla-django-oidc
To use the mozilla-django-oidc as AuthenticationBackend, there are several things to add to the settings.py, as described in the [documentation "Add settings to settings.py"](https://mozilla-django-oidc.readthedocs.io/en/stable/installation.html#add-settings-to-settings-py):
Add INSTALLED_APPS:
```python
INSTALLED_APPS = [
...
"mozilla_django_oidc", # Load after auth
...
]
```
Add MIDDLEWARE:
```python
MIDDLEWARE = [
#...
"mozilla_django_oidc.middleware.SessionRefresh",
]
```
Add AUTHENTICATION_BACKENDS:
```python
AUTHENTICATION_BACKENDS = (
"mysite.backend.PermissionBackend",
)
```
Add configuration:
```python reference
https://github.com/zitadel/example-python-django-oidc/blob/main/mysite/settings.py#L130-L174
```
and create a ".env"-file in the root folder with the configuration:
```bash
ZITADEL_PROJECT = "ID of the project you created the application in ZITADEL"
OIDC_RP_CLIENT_ID = "ClientID provided by the created application in ZITADEL"
OIDC_RP_CLIENT_SECRET = "ClientSecret provided by the created application in ZITADEL"
OIDC_OP_BASE_URL = "Base URL to the ZITADEL instance"
```
which should then look something like this:
```bash
ZITADEL_PROJECT = "249703732336418457"
OIDC_RP_CLIENT_ID = "249703852243222581@python"
OIDC_RP_CLIENT_SECRET = "Zy3OOHaMBTj2sfamW77Vak5BeQ3nEpOf7suPKTnJKaScMh0lPJqUeDOZmgL3bds0"
OIDC_OP_BASE_URL = "https://example.zitadel.cloud"
```
### AuthenticationBackend definition
To create and update the users regarding the roles given in the authentications in ZITADEL a Subclass of OIDCAuthenticationBackend has to be created:
```python reference
https://github.com/zitadel/example-python-django-oidc/blob/main/mysite/backend.py
```
Which handles the users differently depending on if there are roles associated to:
- `admin` -> superuser
- `staff` -> staff
- `user` -> user
- `no role` -> no user gets created
### URLs
To handle the callback and logout the urls have to be added to the urls.py:
```python
urlpatterns = [
#...
path("oidc/", include("mozilla_django_oidc.urls")),
]
```
So it should like something like this:
```python reference
https://github.com/zitadel/example-python-django-oidc/blob/main/mysite/urls.py#L21-L28
```
## Configure and run the application
:::warning
Never store and commit secrets in the ".env" or settings.py file
:::
### Authentication and authorization
To check the authentication and authorization the views in the polls application are extended with decorators:
```python reference
https://github.com/zitadel/example-python-django-oidc/blob/main/mysite/views.py
```
- `@method_decorator(login_required, name="dispatch")`: means that the user has to be logged in Django, which only happens if you have one of the 3 roles("admin", "staff" or "user")
- `@method_decorator(staff_member_required, name="dispatch")`: means you have to have at least a staff user ("admin" or "staff")
- `/admin/`: all admin sides are only accessible if you have a superuser with the role "admin"
:::info
Additional permission checks could be done with "permission_required" from "django.contrib.auth.decorators" also described in the [Django documentation](https://docs.djangoproject.com/en/5.0/topics/auth/customizing/#custom-permissions).
:::
### DB
Create and run migrations:
```bash
python manage.py migrate
```
### Run
You can use a local Django server to test the application.
```bash
python manage.py runserver
```
Visit http://localhost:8000/polls or http://localhost:8000/admin and click around.
## Completion
Congratulations! You have successfully integrated your Python Django application with ZITADEL!
If you get stuck, consider checking out our [example](https://github.com/zitadel/example-python-django-oidc) application. This application includes all the functionalities mentioned in this quick-start. You can start by cloning the repository and defining the settings in the settings.py. If you face issues, contact us or raise an issue on [GitHub](https://github.com/zitadel/example-python-django-oidc/issues).
### What's next?
Now that you have enabled authentication, it's time for you to add more authorizations to your application using ZITADEL APIs. To do this, you can refer to the [docs](/apis/introduction) or check out the ZITADEL Console code on [GitHub](https://github.com/zitadel/zitadel) which uses gRPC and OpenAPI to access data.

View File

@ -0,0 +1,166 @@
---
title: ZITADEL with Django Python
sidebar_label: Django
---
import AppJWT from '../imports/_app_jwt.mdx';
import ServiceuserJWT from '../imports/_serviceuser_jwt.mdx';
import ServiceuserRole from '../imports/_serviceuser_role.mdx';
import SetupPython from '../imports/_setup_python.mdx';
This integration guide demonstrates the recommended way to incorporate ZITADEL into your Django Python application.
It explains how to check the token validity in the API and how to check for permissions.
By the end of this guide, your application will have three different endpoint which are public, private(valid token) and private-scoped(valid token with specific role).
:::info
This documentation references our [example](https://github.com/zitadel/example-django-python-oauth) on GitHub.
:::
## ZITADEL setup
Before we can start building our application, we have to do a few configuration steps in ZITADEL Console.
### Create application
<AppJWT/>
### Create Serviceuser
<ServiceuserJWT/>
### Give Serviceuser an authorization
<ServiceuserRole/>
### Prerequisites
At the end you should have the following for the API:
- Issuer, something like `https://example.zitadel.cloud` or `http://localhost:8080`
- Introspection URL, something like `https://example.zitadel.cloud/oauth/v2/introspect`
- Token URL, something like `https://example.zitadel.cloud/oauth/v2/token`
- `.json`-key-file for the API, from the application
- ID of the project
And the following from the Serviceuser:
- `.json`-key-file from the serviceuser
## Setup new Django application
### Setup Python
<SetupPython/>
### Install dependencies
For this example we need the following dependencies:
- `django`: to create an API with django
- `python-dotenv`: to use environment variables in the configuration
- `authlib`: client-side OAuth functionality
- `requests`: HTTP requests for the introspection
For the dependencies we need a requirements.txt-file with the following content:
```python reference
https://github.com/zitadel/example-python-django-oauth/blob/main/requirements.txt
```
Then install all dependencies with:
```bash
python -m pip install -U requirements.txt
```
Then in your folder of choice, call the following command to create a Django base:
```bash
django-admin startproject myapi .
```
## Define the Django API
### Add to the settings.py to include ZITADEL info
There is info needed for the introspection calls, which we put into the settings.py:
```python reference
https://github.com/zitadel/example-python-django-oauth/blob/main/myapi/settings.py#L125-L133
```
and create a ".env"-file in the root folder with the configuration as an example:
```bash
ZITADEL_INTROSPECTION_URL = 'URL to the introspection endpoint to verify the provided token'
ZITADEL_DOMAIN = 'Domain used as audience in the token verification'
API_PRIVATE_KEY_FILE_PATH = 'Path to the key.json created in ZITADEL'
```
I should look something like this:
```bash
ZITADEL_INTROSPECTION_URL = 'https://example.zitadel.cloud/oauth/v2/introspect'
ZITADEL_DOMAIN = 'https://example.zitadel.cloud'
API_PRIVATE_KEY_FILE_PATH = '/tmp/example/250719519163548112.json'
```
### Validator definition
To validate the tokens, we need a validator which can be called in the event of API-calls.
validator.py:
```python reference
https://github.com/zitadel/example-python-django-oauth/blob/main/myapi/validator.py
```
### Requests and URLs
We define 3 different endpoints which differ in terms of requirements.
views.py:
```python reference
https://github.com/zitadel/example-python-django-oauth/blob/main/myapi/views.py
```
To handle endpoints the urls have to be added to the urls.py:
```python reference
https://github.com/zitadel/example-python-django-oauth/blob/main/myapi/urls.py
```
### DB
Create and run migrations:
```bash
python manage.py migrate
```
### Run
You can use a local Django server to test the application.
```bash
python manage.py runserver
```
### Call the API
To call the API you need an access token, which is then verified by ZITADEL.
Please follow [this guide here](https://zitadel.com/docs/guides/integrate/private-key-jwt#get-an-access-token), ignoring the first step as we already have the `.json`-key-file from the serviceaccount.
Optionally set the token as an environment variable:
```
export TOKEN='MtjHodGy4zxKylDOhg6kW90WeEQs2q...'
```
With the access token, you can then do the following calls:
```
curl -H "Authorization: Bearer $TOKEN" -X GET http://localhost:8000/api/public
curl -H "Authorization: Bearer $TOKEN" -X GET http://localhost:8000/api/private
curl -H "Authorization: Bearer $TOKEN" -X GET http://localhost:8000/api/private-scoped
```
## Completion
Congratulations! You have successfully integrated your Django API with ZITADEL!
If you get stuck, consider checking out our [example](https://github.com/zitadel/example-python-django-oauth) application. This application includes all the functionalities mentioned in this quick-start. You can start by cloning the repository and defining the settings in the settings.py. If you face issues, contact us or raise an issue on [GitHub](https://github.com/zitadel/example-python-django-oauth/issues).

View File

@ -178,7 +178,7 @@ module.exports = {
selector: "div#",
},
prism: {
additionalLanguages: ["csharp", "dart", "groovy", "regex", "java", "php"],
additionalLanguages: ["csharp", "dart", "groovy", "regex", "java", "php", "python"],
},
colorMode: {
defaultMode: "dark",

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

BIN
docs/static/img/django/app-create.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
docs/static/img/django/app-redirects.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/static/img/django/project-roles.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB