Share via


자습서: Python Flask 웹앱에 로그인 추가

이 자습서는 Python Flask 웹앱을 처음부터 빌드하고 Microsoft ID 플랫폼 사용하여 인증을 통합하는 방법을 보여 주는 자습서 시리즈의 세 번째 부분입니다. 이 자습서에서는 빌드한 앱에서 사용자를 인증하는 코드를 추가합니다.

  • 필요한 모듈 및 구성 가져오기
  • Flask 웹앱의 인스턴스 만들기
  • 로컬 개발을 위한 ProxyFix 미들웨어 구성
  • 로그인하고 사용자를 로그아웃하는 코드 추가
  • 웹앱의 진입점 정의

필요한 패키지 및 구성 가져오기

빌드하는 웹앱은 MSAL Python을 기반으로 빌드된 패키지를 사용하여 identity.web 웹앱에서 사용자를 인증합니다. 패키지, Flask 프레임워크, Flask 모듈, Flask 세션 및 이전 자습서에 정의된 앱 구성을 가져오 identity.web 려면 다음 코드를 추가하여 app.py.

import identity.web
import requests
from flask import Flask, redirect, render_template, request, session, url_for
from flask_session import Session

import app_config

이 코드 조각에서는 Flask에서 웹 요청 및 세션을 처리하기 위한 함수 및 개체 , , 및 : 을 가져옵니다redirect. render_templaterequestsessionurl_for 또한 앱에 대한 구성 설정을 포함하는 가져오기 app_config도 합니다.

Flask 웹앱의 인스턴스 만들기

필요한 모듈을 가져온 후 .의 구성을 사용하여 웹앱을 초기화합니다 app-config. 웹앱의 인스턴스를 만들려면 다음 코드 조각을 다음 코드 조각에 추가합니다 app.py.

app = Flask(__name__)
app.config.from_object(app_config)
assert app.config["REDIRECT_PATH"] != "/", "REDIRECT_PATH must not be /"
Session(app)

위의 코드 조각에서는 새 Flask 애플리케이션을 초기화하고 app.config.from_object(app_config). 을 사용하여 from_object앱은 지정된 (app_config)에서 구성을 상속합니다.

또한 어설션 검사 수행하여 앱의 리디렉션 경로가 루트 경로("/")로 설정되지 않았는지 확인합니다. Session(app) 는 여러 요청에 걸쳐 세션을 처리하고 사용자 인증 상태와 같은 데이터를 저장할 수 있도록 앱에 대한 세션 관리를 초기화합니다.

로컬 개발을 위한 ProxyFix 미들웨어 구성

샘플 웹앱은 로컬 호스트에서 실행되므로 미들웨어를 ProxyFix 사용하여 요청 헤더의 URL 구성표 및 호스트 정보를 수정합니다. ProxyFix를 적용하려면 app.py 다음 코드를 추가합니다.

from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)

인증 개체 초기화

다음으로, 클래스의 인스턴스를 만들어 인증 개체를 초기화합니다 [identity.web.Auth](https://identity-library.readthedocs.io/en/latest/#identity.web.Auth) . 또한 다음과 같이 Auth 개체를 초기화할 때 생성자에 매개 변수, 및 client_idclient_credential 매개 변수sessionauthority를 전달합니다.

app.jinja_env.globals.update(Auth=identity.web.Auth)  # Useful in template for B2C
auth = identity.web.Auth(
    session=session,
    authority=app.config["AUTHORITY"],
    client_id=app.config["CLIENT_ID"],
    client_credential=app.config["CLIENT_SECRET"],
)

이 코드 조각 app.jinja_env.globals.update(Auth=identity.web.Auth)에서 명명된 Auth 새 전역 변수를 추가하고 해당 값을 identity.web.Auth할당합니다. 이렇게 하면 AuthFlask 애플리케이션에서 렌더링하는 모든 템플릿에서 액세스할 수 있습니다.

사용자 로그인

이 앱에서 빌드하는 권한 부여 흐름은 두 다리로 구성됩니다. 첫 번째 레그에서는 다음과 같이 함수를 auth.log_in 호출하여 사용자를 로그인합니다.

@app.route("/login")
def login():
    return render_template("login.html", version=__version__, **auth.log_in(
        scopes=app_config.SCOPE, # Have user consent to scopes during log-in
        redirect_uri=url_for("auth_response", _external=True), # Optional. If present, this absolute URL must match your app's redirect_uri registered in Microsoft Entra admin center
        prompt="select_account",  # Optional.
        ))

사용자가 앱의 /login URL로 이동하면 Flask는 템플릿 렌더링 요청을 처리하는 뷰 함수를 login.html 호출합니다. 내부에서 login()사용자가 로그인 프로세스 중에 동의해야 하는 범위 목록을 사용하여 함수를 호출 auth.log_in 합니다. 또한 앱의 리디렉션 URI i Microsoft Azure 관리 센터와 일치해야 하는 매개 변수를 제공합니다 redirect_uri .

필요에 따라 활성 세션이 있는 계정 간에 재인증, 사용자 동의 또는 계정 선택을 요청하여 로그인 프롬프트의 동작을 제어하는 매개 prompt변수를 추가할 수 있습니다.

권한 부여 흐름의 두 번째 레그에서는 다음과 같이 redirect_uri 컨트롤러 내에서 함수를 호출 auth.complete_log_in 하여 인증 응답을 처리합니다.

@app.route(app_config.REDIRECT_PATH)
def auth_response():
    result = auth.complete_log_in(request.args)
    if "error" in result:
        return render_template("auth_error.html", result=result)
    return redirect(url_for("index"))

함수는 complete_log_in() 들어오는 auth_response 사전을 쿼리 매개 변수로 사용합니다. 성공하면 함수는 .를 사용하여 redirect(url_for("index"))사용자를 "인덱스" 경로로 리디렉션합니다. 즉, 사용자가 자신의 정보에 성공적으로 로그인한 경우 이미 유효성이 검사된 ID 토큰의 클레임을 포함하는 사전으로 사용할 수 있습니다.

조건에 if "error" in result:따라 결정된 오류가 결과에 포함된 경우 템플릿을 "auth_error.html" 사용자에게 렌더링합니다.

사용자 로그아웃

Flask 애플리케이션에서 사용자를 로그아웃하려면 다음과 같이 메서드를 auth.log_out() 호출합니다.

@app.route("/logout")
def logout():
    return redirect(auth.log_out(url_for("index", _external=True)))

사용자가 앱의 /logout URL 경로로 이동하면 Flask는 현재 앱에서 로그아웃하는 로그아웃 함수를 호출합니다. 또한 사용자가 로그아웃할 때 리디렉션해야 하는 페이지를 지정합니다. 코드 조각에서는 다음을 사용하여 사용자를 앱의 홈페이지로 리디렉션합니다. url_for("index", _external=True).

웹앱의 진입점 정의

로그인 및 로그아웃 논리를 구현한 후 다음과 같이 함수를 만들어 앱 홈페이지에 index() 진입점을 추가합니다.

@app.route("/")
def index():
    if not (app.config["CLIENT_ID"] and app.config["CLIENT_SECRET"]):
        # This check is not strictly necessary.
        # You can remove this check from your production code.
        return render_template('config_error.html')
    if not auth.get_user():
        return redirect(url_for("login"))
    return render_template('index.html', user=auth.get_user(), version=__version__)

사용자가 index() 앱의 루트 URL("/")로 이동할 때 함수가 호출됩니다. 앱의 홈페이지를 렌더링하기 전에 구성 검사 처리하고 사용자 인증의 유효성을 검사합니다. 구성에서 클라이언트 ID 및 클라이언트 암호가 누락된 경우와 두 값 중 하나 또는 둘 다 누락된 경우 Flask에서 템플릿을 "config_error.html" 렌더링하는 검사.

또한 함수는 사용자가 인증되었는지 여부를 확인하기 위해 호출 auth.get_user() 합니다. 사용자가 인증되지 않은 경우 경로로 "login" 리디렉션됩니다. 인증된 경우 Flask는 "index.html" 템플릿을 렌더링하고 렌더링을 위해 사용자 개체(검색 auth.get_user()됨)를 전달합니다.

다음 단계