[PYTHON] Créons une application qui authentifie OIDC avec Azure AD

introduction

Lors de l'examen du fonctionnement d'Azure AD avec votre application, vous pouvez effectuer les opérations suivantes:

Les informations sur OpenID Connnect (OIDC) sont assez substantielles, mais j'ai senti que les informations sur SCIM étaient petites, j'ai donc commencé à écrire un article en espérant qu'il y aurait des paramètres et des exemples d'implémentation d'applications dans Azure AD. Cependant, cette fois, nous couvrirons le point où l'authentification unique peut être effectuée avec le premier OIDC. L'application est implémentée dans Django et le flux d'authentification utilise le flux de code d'authentification. Pour le moment, je vais le définir avec un œil sur l'approvisionnement automatique.

Faisons le! (Le code source pour cette fois est sur github.)

1. À propos de Django

Django est un framework d'application Web Python. Je l'utilise parce qu'il est facile de le déplacer pour le moment. Nous espérons que les explications suivantes vous en donneront une idée même si vous n'avez aucune expérience avec Django, mais si vous avez de l'expérience avec d'autres frameworks MVC, vous risquez d'être confus par la terminologie Django utilisée dans cet article. Veuillez donc faire attention à ce qui suit.

Django utilise le modèle MVC, comme la plupart des frameworks Web. Référence: À propos du modèle MVC Cependant, chaque élément est appelé différemment et s'appelle MTV (Model, View, Template). C'est déroutant, mais sachez que ** Django's View est un contrôleur MVC général **.

Source: [[Python] Django Tutorial-Making a General Purpose Business Web App Fastest](https://qiita.com/okoppe8/items/54eb105c9c94c0960f14#djnago-%E3%81%AE-mvc%E3%83%91 % E3% 82% BF% E3% 83% BC% E3% 83% B3)

2. À propos du flux de code d'auroraisation

C'est l'une des méthodes courantes d'authentification et d'autorisation des applications Web. Les détails sont résumés dans les informations publiques suivantes. Encore une fois, suivez l'article ci-dessous pour obtenir un jeton à l'aide de la bibliothèque d'authentification Microsoft (MSAL).

Référence: Microsoft ID Platform et OAuth 2.0 Authentication Code Flow

3. Enregistrez l'application auprès d'Azure AD

Il existe deux écrans pour enregistrer une application dans Azure AD, «Register App» et «Enterprise Application», mais pour le provisionnement automatique, vous devez vous inscrire à partir de «Enterprise Application». Vous avez besoin d'une licence d'Azure AD Premium P1 ou supérieure, donc si vous n'en avez pas, utilisez la version d'essai gratuite.

3.1. Enregistrez votre application dans ** Azure Portal> Azure Active Directory> Applications d'entreprise> Nouvelles applications> Applications hors galerie **.

3.2. Dans ** Azure Portal> Azure Active Directory> Inscrire l'application> Toutes les applications **, recherchez et ouvrez l'application créée ci-dessus.

3.3. Notez les "ID d'application" et "ID de répertoire" sur la page ** Vue d'ensemble **.

3.4. ** Authentification ** Ajoutez l'URI de redirection depuis la page.

ʻAccounts` est le nom de l'application que nous créons cette fois.

3.5. ** Certificats et Secrets> Créez un secret client avec le nouveau secret client ** et notez-le.

Cette fois, j'utilise le secret client pour plus de simplicité. Les certificats peuvent également être utilisés comme méthode plus sécurisée.

4. Créer un projet Django

Créez un projet mysite avec la commande suivante:

django-admin startproject mysite

Ajoutez un dossier pour mettre le modèle.

mysite/settings.py


TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],  #ajouter à
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

5. Créez des «comptes» d'application

Créez une application appelée ʻaccounts dans le projet mysite` avec la commande suivante:

python manage.py startapp accounts

Créez ʻurls.py`

accounts/urls.py


from django.urls import include, path

from . import views

urlpatterns = [
    path('', views.index),
    path('login/', views.login_view, name='login'),
    path('logout/', views.logout_view, name='logout'),
    path('oidc/callback/', views.callback_view) 
]

/ accounts / oidc / callback est l'URI de redirection défini à l'étape 3.4. Après s'être authentifié avec Azure AD, l'utilisateur sera redirigé vers cet URI. Le code d'autorisation passé à ce moment-là doit être implémenté pour pouvoir être traité par views.callback_view.

Créer un modèle utilisateur personnalisé

Permet aux utilisateurs authentifiés avec Azure AD de se connecter à cette application. Pour ce faire, vous devez associer les utilisateurs Azure AD aux utilisateurs de l'application. Cette fois, nous associerons un utilisateur dont ObjectId du côté Azure AD et external_id de l'application sont les mêmes.

Créez un modèle utilisateur personnalisé et ajoutez l'attribut external_id.

accounts/models.py


from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    external_id = models.CharField(max_length=50, blank=True)

Modifiez l'écran de l'administrateur pour afficher l'identifiant externe de l'utilisateur.

accounts/admin.py


from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin

from .models import User


class UserAdmin(BaseUserAdmin):
    list_display = ['username', 'is_staff', 'is_active', 'external_id']


admin.site.register(User, UserAdmin)

Laissez le modèle d'utilisateur personnalisé être le modèle d'utilisateur d'authentification.

mysite/settings.py


AUTH_USER_MODEL = 'accounts.User'  #ajouter à

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'accounts.apps.AccountsConfig',  #ajouter à
]

Ajoutez des «comptes» à l'URL du projet.

mysite/urls.py


from django.contrib import admin
from django.shortcuts import redirect  #ajouter à
from django.urls import include, path  #inclure ajouté

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', lambda req: redirect('accounts/', permanent=False)),  #ajouter à
    path('accounts/', include('accounts.urls')),  #ajouter à
]

6. Implémenter le flux de code d'authentification

Fondamentalement, vous pouvez réaliser l'authentification avec Azure AD en créant une URL vers le point de terminaison d'authentification Azure AD et en redirigeant l'utilisateur vers celui-ci. Les fonctions fréquemment utilisées sont également disponibles depuis la bibliothèque MSAL. Le code suivant est basé sur Flask + MSAL Sample.

6.1. Ajouter un écran.

6.2. Ajoutez des paramètres d'authentification et d'autorisation.

mysite/settings.py


# Azure AD

SCOPES = ['User.Read']
TENANT_ID = '{3.3.ID de répertoire}'
AUTHORITY = f'https://login.microsoftonline.com/{TENANT_ID}/'
CLIENT_ID = '{3.3.ID d'application}'
REDIRECT_PATH = '{3.4.Rediriger l'URI}'
CLIENT_SECRET = '{3.5.Secret du client}'
ENDPOINT = 'https://graph.microsoft.com/beta/me'

ENDPOINT est une API référencée par les privilèges de l'utilisateur connecté. Cette fois, API d'acquisition d'informations utilisateur est exécutée, donc SCOPES ʻUser.Read` est ajouté à.

6.3. Vue d'outil.

accounts/views.py


import uuid
import msal
from django.shortcuts import redirect
from django.contrib.auth import login, logout
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from django.contrib.auth import get_user_model

from mysite2 import settings

User = get_user_model()


@login_required
def index(request):
    context = {'user': request.user}
    return render(request, 'accounts/index.html', context)


def logout_view(request):
    logout(request)
    return redirect('/')


def login_view(request):
    request.session['state'] = str(uuid.uuid4())
    auth_url = _build_auth_url(
        scopes=settings.SCOPES, state=request.session['state'])
    context = {'auth_url': auth_url}
    return render(request, 'accounts/login.html', context)


def callback_view(request):
    if request.GET.get('state') != request.session.get('state'):
        # 'state'Ne correspond pas à la demande
        return redirect('/')
    if 'error' in request.GET:
        #Azure AD authentifie/A renvoyé une erreur d'autorisation
        return render(request, 'accounts/auth_error.html', request.GET)
    if 'code' in request.GET:
        cache = _load_cache(request)
        result = _build_msal_app(cache=cache).acquire_token_by_authorization_code(
            request.GET['code'],
            scopes=settings.SCOPES,  # Misspelled scope would cause an HTTP 400 error here
            redirect_uri=settings.REDIRECT_PATH)
        if 'error' in result:
            return render(request, 'accounts/auth_error.html', result)
        request.session['user'] = result.get('id_token_claims')

    try:
        #Oid du côté Azure AD et externe du côté de l'application_Vérifiez si l'utilisateur a le même identifiant
        oid = request.session['user']['oid']
        user = User.objects.get(external_id=oid)
        login(request, user)
    except User.DoesNotExist as e:
        context = {'error': 'User.DoesNotExist', 'error_description': str(e)}
        return render(request, 'accounts/auth_error.html', context)

    return redirect('/')


def _build_auth_url(authority=None, scopes=None, state=None):
    return _build_msal_app(authority=authority).get_authorization_request_url(
        scopes or [],
        state=state or str(uuid.uuid4()),
        redirect_uri=settings.REDIRECT_PATH)


def _build_msal_app(cache=None, authority=None):
    return msal.ConfidentialClientApplication(
        settings.CLIENT_ID, authority=settings.AUTHORITY,
        client_credential=settings.CLIENT_SECRET, token_cache=cache)


def _load_cache(request):
    cache = msal.SerializableTokenCache()
    if request.session.get('token_cache'):
        cache.deserialize(request.session['token_cache'])
    return cache


def _save_cache(request, cache):
    if cache.has_state_changed:
        request.session['token_cache'] = cache.serialize()

Voici les points.

7. Exécutez l'application

#Créer un DB
python manage.py makemigrations
python manage.py migrate

#Créer un utilisateur administrateur
python manage.py createsuperuser

#Démarrez le serveur
python manage.py runserver

Vous pouvez accéder à l'application en ouvrant http: // localhost: 8000 / dans votre navigateur. / admin est la console d'administration.

8. Authentifiez-vous avec Azure AD et connectez-vous à l'application

Maintenant, connectons-nous à l'application en tant qu'utilisateur Azure AD.

Tout d'abord, attribuez aux utilisateurs la possibilité de se connecter sur la page ** Azure Portal> Azure Active Directory> Applications d'entreprise> S'applique> Utilisateurs et groupes **.

Ensuite, comme nous n'avons pas configuré le provisionnement automatique cette fois, lancez le shell d'administration avec python manage.py shell et créez manuellement un utilisateur côté application comme indiqué ci-dessous.

from django.contrib.auth import get_user_model
User = get_user_model()
user = User.objects.create(username='{Nom d'utilisateur de l'utilisateur Azure AD}', external_id='{ObjectId de l'utilisateur Azure AD}') 
user.save()

Désormais, lorsque l'utilisateur ci-dessus accède à l'application et appuie sur le bouton de connexion, l'écran d'authentification Azure AD sera requis. Si vous y entrez vos informations d'identification, il vous sera demandé d'accepter les autorisations de l'application comme indiqué ci-dessous. Cet écran de consentement ne nécessite qu'un premier accès. En outre, l'administrateur peut donner son consentement locataire par locataire, auquel cas l'écran de consentement ne sera pas affiché aux utilisateurs généraux.

2019-12-24_07h04_10.png

Si vous acceptez ici, vous vous êtes connecté avec succès!

2019-12-24_07h21_12.png

L'application a également accès aux API protégées par les privilèges de l'utilisateur connecté.

2019-12-24_07h21_58.png

en conclusion

J'ai pu me connecter à l'application en tant qu'utilisateur Azure AD.

La raison pour laquelle externalId de l'utilisateur personnalisé est créé cette fois est pour l'authentification et pour implémenter [SCIM API](provisioning utilisateur de SCIM dans Azure Active Directory (Azure AD)). Cependant, il est assez difficile de l'implémenter un par un, donc si vous avez une bibliothèque pour l'API SCIM, vous devriez l'utiliser. Cette fois, Django semble avoir une bibliothèque appelée django-scim2, mais il semble que le modèle utilisateur personnalisé doit également être recréé pour la bibliothèque. Si vous utilisez l'API SCIM, vous devrez peut-être réfléchir dès la conception. Je vais essayer de créer une application qui prend en charge l'approvisionnement automatique la prochaine fois!

Recommended Posts

Créons une application qui authentifie OIDC avec Azure AD
Créez une application qui devine les étudiants avec Python
Créez une application de mots anglais avec python
Créez une application de composition d'images avec Flask + Pillow
[kotlin] Créez une application qui reconnaît les photos prises avec un appareil photo sur Android
Créez une application qui fonctionne bien avec les rapports des utilisateurs à l'aide de l'API COTOHA
Créons une application capable de rechercher des images similaires avec Python et Flask Part1
Créons une application capable de rechercher des images similaires avec Python et Flask Part2
Créer un environnement avec virtualenv
Créer une API avec Django
Créons un script qui s'enregistre avec Ideone.com en Python.
Créons une spécification externe facile à gérer
Créer une application Todo avec Django ① Créer un environnement avec Docker
Créez une application qui informe LINE de la météo tous les matins
Créez une tranche d'âge avec les pandas
Reconnaissons les émotions avec Azure Face
Créez une application Web qui peut être facilement visualisée avec Plotly Dash
Créer une application en classifiant avec Pygame
Créer une application de fractionnement d'image avec Tkinter
Créer une visionneuse de traitement d'image avec PySimpleGUI
Créons un groupe gratuit avec Python
Créez rapidement un fichier Excel avec Python #python
Créer une application graphique avec Tkinter de Python
Créer un écran de mise à jour avec Django Updateview
Créez une application Web simple avec Flask
Création de la première application avec Django startproject
[Python] Créez rapidement une API avec Flask
Générer une instance Excel compatible avec les compléments avec xlwings
Développons un algorithme d'investissement avec Python 1
Créez un fichier msi évolutif avec cx_Freeze
Avec LINEBot, j'ai fait une application qui m'informe de "l'heure du bus"