トークンを取得する

MSAL Pythonを使用してトークンを取得する方法は多数あります。 ユーザーの操作が必要な場合もあれば、必要でないものもあります。 トークンの取得に使用されるアプローチは、開発者がパブリック クライアント (デスクトップまたはモバイル) または機密クライアント アプリケーション (Web アプリ、Web API、Windows サービスなどのデーモン) を構築しているかどうかによって異なります。

Prerequisites

MSAL Pythonでトークンを取得する前に、クライアント アプリケーションの種類について説明します。

ユーザー アカウントを取得する

アプリは、それ自体として、またはユーザーの代わりにトークンを取得できます。 ユーザーに代わってトークンを取得するには、アプリがユーザーのアカウントを認識している必要があります。 MSAL Pythonは、ユーザーのアカウントを取得するget_accountsメソッドを提供します。 このメソッドは、 PublicClientApplication クラスと ConfidentialClientApplication クラスの両方で使用できます。 このメソッドは、ユーザーが以前にサインインしたアカウント、つまりキャッシュに存在するアカウントの一覧を返します。

accounts = app.get_accounts(username=user.get("preferred_username"))

ユーザーがサインインのために選択したアカウントは、後で acquire_token_silent() でトークンを検索するために使用できます。

トークン付与フロー

MSAL Pythonでトークンを取得するために使用できる認証フローがいくつかあります。 これらのフローの詳細については、Microsoft ID プラットフォームドキュメントを参照してください

Warning

MSAL を使用してセキュリティ トークンを取得し、アプリで保護された Web API を呼び出します。 独自のトークン取得ロジックを実装することはお勧めしません。 これらのフローは、物事のしくみをより深く理解するのに役立ちます。 Web アプリケーションをセキュリティで保護する場合は、 ID ライブラリを使用することをお勧めします。 このライブラリは、Microsoftによって正式に管理されていませんが、Web アプリでトークンを取得するために必要なロジックのほとんどを実装しています。

対話型とサイレント

MSAL Pythonでは、対話型トークンとサイレント トークンの取得の両方がサポートされています。 対話型トークンの取得にはユーザーの操作が必要ですが、サイレント トークンの取得は必要ありません。 一般に、パブリック クライアントではユーザーの操作が必要ですが、機密クライアントは証明書やシークレットなどの事前プロビジョニングされた資格情報に依存します。

トークンをサイレント モードで取得するには、 acquire_token_silent_with_error メソッドを使用します。 このメソッドは、キャッシュから有効なアクセス トークン、またはキャッシュから有効な更新トークンを検索し、それを自動的に使用して新しいアクセス トークンを使用します。 どちらも true でない場合は、対話型メソッドを使用してトークンを取得する必要があります。

アプリがトークン キャッシュの検索中に正確なトークン更新エラーを気にしない場合は、 acquire_token_silent メソッドをお勧めします。

このメソッドの使用例は、次のコード スニペットに示されています。

if accounts:
    # If so, you could then somehow display these accounts and let end user choose
    chosen = accounts[0]
    result = app.acquire_token_silent(scopes=["your_scope"], account=chosen)
    
    # At this point, you can save you can update your cache if you are using token caching
    # check result variable, if its None then you should interactively acquire a token
    if not result:
        # So no suitable token exists in cache. Let's get a new one from Microsoft Entra.
        result = app.acquire_token_by_one_of_the_actual_method(..., scopes=["User.Read"])
    
    if "access_token" in result:
        access_token = result["access_token"]
    else:
        print(result.get("error"))  
        print(result.get("error_description"))
        print(result.get("correlation_id"))  # You may need this when reporting a bug

対話型トークンの取得には、いくつかの方法を使用できます。 使用する方法は、ビルドするアプリの種類と、シナリオに適用できるトークン付与フローによって異なります。

パブリック クライアントによる対話形式でのトークン取得

パブリック クライアント アプリケーションは、シークレットを安全に格納することはできません。また、製品と対話しているユーザーのみを認証できます。 MSAL Pythonは、PublicClientApplicationを介してパブリック アプリケーションのトークン取得ロジックを公開します。 パブリック クライアント アプリケーションがトークンを取得するために使用できるさまざまな方法を次に示します。

デバイス コード フロー

デバイス コード フロー は、Web ブラウザーにアクセスできないデバイスで実行されるアプリケーションでトークンを取得するために使用されます。 これらは、ヘッドレス アプリケーションと呼ばれるアプリケーションです。 このフローにより、ユーザーに URL とコードが提供されます。 ユーザーは別のデバイス上の Web ブラウザーに移動し、コードを入力してサインインします。 認証が成功すると、Microsoft Entraはブラウザーのないデバイスにトークンを返します。

まず、 initiate_device_flow メソッドを呼び出します。

flow = app.initiate_device_flow(scopes=config["scope"])
if "user_code" not in flow:
    raise ValueError(
        "Fail to create device flow. Err: %s" % json.dumps(flow, indent=4))

print(flow["message"])
sys.stdout.flush()  # Some terminal needs this to ensure the message is shown

# Ideally you should wait here, in order to save some unnecessary polling
# input("Press Enter after signing in from another device to proceed, CTRL+C to abort.")

次に、フロー ディクショナリ オブジェクトを acquire_token_by_device_flow メソッドに渡してトークンを取得します。 既定では、このメソッドは現在のスレッドをブロックします。 次の手順に従ってブロック時間を短縮したり、ブロック動作をオフにして、独自のカスタマイズされたループでacquire_token_by_device_flowを呼び出し続けたりすることもできます。

result = app.acquire_token_by_device_flow(flow)

if "access_token" in result:
    access_token = result["access_token"]
else:
    print(result.get("error"))  

access_token キーを含む辞書の成功した応答。

トークンを対話型で取得する

MSAL Pythonには、パブリック クライアント アプリ (デスクトップとモバイル) がユーザーとしてトークンを取得する機能も用意されています。 ユーザーは、Web ブラウザー経由で承認要求 URL を使用してサインインします。 アプリの登録のMicrosoft Entra 管理センターでhttp://localhostするようにアプリのリダイレクト URI を設定します。 の作成時にPublicClientApplicationすることを選択した場合、アプリではms-appx-web://Microsoft.AAD.BrokerPlugin/YOUR_CLIENT_IDをリダイレクト URI として登録する必要もあります。

result = app.acquire_token_interactive(  # It automatically provides PKCE protection
    scopes=config["scope"])

if "access_token" in result:
    access_token = result["access_token"]
else:
    print(result.get("error"))  

ユーザー名とパスワード

Warning

この API は、セキュリティ 上のリスクがあるため、パブリック クライアント フローでは非推奨となりました。より安全なフローを使用してください。 移行 ガイダンスについては、このガイド に従ってください。

この方法を使用することはお勧めしません。 ユーザー名とパスワードを使用してトークンを取得することもできます。 MSAL Pythonは、このユース ケースのacquire_token_by_username_passwordメソッドを提供します。 アプリケーションがユーザーにパスワードを直接要求するため、これは安全ではないパターンであるため、お勧めしません。

より安全なフローを使用できます。 詳細については、 ユーザー名とパスワードの認証フロー のガイダンスを参照してください。

result = app.acquire_token_by_username_password(
    username=config["username"], password=config["password"], scopes=config["scope"])

if "access_token" in result:
    access_token = result["access_token"]
else:
    print(result.get("error"))  

機密クライアントの対話型トークンの取得

機密クライアント アプリケーションは、シークレットを安全に格納でき、アプリケーションに代わって認証することも、特定のユーザーの代わりに認証することもできます。 MSAL Pythonでは、ConfidentialClientApplicationを開発するときにトークンを取得するさまざまな方法を開発者に提供します。

クライアントのトークンを取得する

ユーザーではなく、 クライアント資格情報を使用して、アプリケーション自体としてトークンを取得します。 たとえば、同期ツールなど、特定のユーザーではなく、バッチでユーザーを処理するアプリケーションでこれを使用できます。 MSAL Pythonには、これを行うacquire_token_for_clientメソッドが用意されています。 MSAL Python 1.23 であるため、このメソッドはキャッシュからトークンを自動的に検索し、キャッシュミス時にのみ ID プロバイダーに要求を送信します。

result = app.acquire_token_for_client(scopes=config["scope"])

if "access_token" in result:
    access_token = result["access_token"]
else:
    print(result.get("error"))    

代理でトークンを取得する

Web アプリまたは Web API がユーザーの名前で別のダウンストリーム Web API を呼び出す場合は、 On Behalf Of フロー を使用して、ユーザー アサーションに基づいてトークンを取得します。 たとえば、SAML と JWT です。 現在のアプリは、エンド ユーザーを表すトークンを使用して呼び出された中間層サービスです。 現在のアプリでは、このようなトークン (ユーザー アサーションとも呼ばれます) を使用して、そのユーザーに代わってダウンストリーム Web API にアクセスするための別のトークンを要求できます。 中間層アプリには、同意を得るためのユーザー操作がありません。 中間層アプリの事前の同意を得る方法については、ドキュメントを参照 してください

acquire_token_on_behalf_of メソッドを使用してアクセス トークンを取得するコードの例を次に示します。

def get(self, request): # a web service endpoint receiving a request
    
    scopes = ["your-scopes"]
    downstream_api = "https://your-downstreamapi.com/resource" #your downstream API resource endpoint
    current_access_token = request.headers.get("Authorization", None)
    
    # initialize the app
    app = msal.ConfidentialClientApplication(...) # refer to initialization of the app documentation

    #acquire token on behalf of the user that called this API
    downstream_api_access_token = app.acquire_token_on_behalf_of(
        user_assertion=current_app_access_token.split(' ')[1],
        scopes=_scopes
    )

    if "access_token" in result:
        access_token = result["access_token"]
        # use access_token to call dowstream API e.g
        requests.get(downstream_api, headers={'Authorization': f'Bearer {downstream_api_access_token}'})
    else:
        print(result.get("error")) 

承認コード フローによるトークンの取得

ユーザーの名前で認証する Web アプリの場合は、承認要求 URL を使用してユーザーがサインインできるようにした後、承認 コード を使用してトークンを取得します。 これは通常、ユーザーがこの特定のユーザーの Web API にサインインしてアクセスできるようにするアプリケーションで使用されるメカニズムです。

最初に、 initiate_auth_code_flowを使用して認証コード フローを開始する必要があります。 このメソッドは、リダイレクト URI と状態文字列を他のパラメーターの中から取り込みます。 状態パラメーターの値もトークン応答に含まれます。 この値が存在しない場合、MSAL Python は内部的に値を自動生成します。 指定されたリダイレクト URI は、Microsoft Entra 管理センターに登録されているリダイレクト URI と一致する必要があります。 このメソッドは、 auth_uristateを含むディクショナリである認証コード フローを返します。 auth_uriは、ユーザーがサインインするためにアクセスする必要がある URL です。

flow = app.initiate_auth_code_flow(
    scopes=config["scope"], redirect_uri=config["redirect_uri"], state="your-state-value")

if "error" in flow:
    print(flow.get("error"))

# Save the response somewhere e.g in session
session["auth_flow"] = flow

# At this point, the app should guide the user to visit the auth ur (session["auth_flow"]["auth_uri"])

認証 URI エンドポイントへのアクセスからの応答は、 acquire_token_by_auth_code_flow メソッドで使用されます。 状態は、承認サーバーからの応答を確認するために使用できる一意の識別子です。 ユーザーはサインイン時に要求されたスコープに同意する必要があります。

# The uth_response value from visiting the auth_uri endpoint is passed as a query string
# You can change this by passing a value to the response_mode in the initiate_auth_code_flow method
try:
    result = app.acquire_token_by_auth_code_flow(session.get("flow", {}), auth_response)
    
    if "access_token" in result:
        access_token = result["access_token"]
    else:
        print(result.get("error"))
except ValueError:  # Usually caused by CSRF
    pass  # Simply ignore them

MSAL Python トークン キャッシュ

パブリック クライアント アプリケーションと機密クライアント アプリケーションの両方で、MSAL Pythonによって直接処理されるトークン キャッシュがサポートされます。 アプリケーションは、他の手段に依存する前に、まずキャッシュからトークンを取得しようとする必要があります。 詳細については、 推奨されるトークン取得パターンを参照してください。

キャッシュを保持できるようにするには、開発者は トークン キャッシュのシリアル化 ロジックを構成する必要があります。