Zhuo Han
Zhuo Han

Reputation: 41

Failed to use auth code flow with python MSAL

I simply can't get acquire_token_by_auth_code_flow() from the MSAL package to work outside a flask app using the basic example giving in the MSAL documentation.

I think the problem comes from using the wrong authentication response which must be a "dict of the query string received from auth server" according to the documentation. In a flask app, I can simply use request.args which I'm not quite sure how to use outside of flask.

I've already tried using requests.request as well as urlsplit. The device flow is working fine as well as using the MSAL package in Java and connecting via R. So the app seems to be set up correctly.

The basic example code from the MSAL app below produces the error:

state mismatch: XXXXXXXXXXXX vs None

(so auth_response is wrong).
Any thoughts?

import requests
import msal

CLIENT_ID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" # Application (client) ID of app registration
CLIENT_SECRET = "XX-XXXXXXXX-XXXXXXXX.XX~XXXXX~XXXX" # Placeholder - for use ONLY during testing.
AUTHORITY = "https://login.microsoftonline.com/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX"
REDIRECT_PATH = "/getAToken"  # Used for forming an absolute URL to your redirect URI.
                              # The absolute URL must match the redirect URI you set
                              # in the app's registration in the Azure portal.
ENDPOINT = 'https://graph.microsoft.com/v1.0/me'  
SCOPE = ["https://graph.microsoft.com/.default"]

# Cache
cache = msal.SerializableTokenCache()

# Build msal app
app = msal.ConfidentialClientApplication(
        CLIENT_ID, authority=AUTHORITY,
        client_credential=CLIENT_SECRET, token_cache=cache)

# Initiate auth code flow
session = requests.Session()
session.flow = app.initiate_auth_code_flow(scopes=SCOPE, redirect_uri=REDIRECT_PATH)

# Aquire token
result = app.acquire_token_by_auth_code_flow(auth_code_flow=session.flow, auth_response = dict(parse.parse_qsl(parse.urlsplit(REDIRECT_PATH).query)))

The equivalent code for the last bit from the flask app looks like this with REDIRECT_PATH = "/getAToken":

@app.route(app_config.REDIRECT_PATH)  # Its absolute URL must match your app's redirect_uri set in AAD
def authorized():
    result = _build_msal_app(cache=cache).acquire_token_by_auth_code_flow(
        session.get("flow", {}), request.args)
    return redirect(url_for("index"))

Upvotes: 4

Views: 8819

Answers (3)

Andrew Chen
Andrew Chen

Reputation: 111

auth_response = dict(parse.parse_qsl(parse.urlsplit(REDIRECT_PATH).query)) is wrong.

I ran into this recently and I have an example for making acquire_token_by_auth_code_flow on a headless environment by visiting the auth_uri in a browser and paste the redirect url to get the token:

        authority_url = "https://login.microsoftonline.com/consumers"  # Or your tenant-specific endpoint
        app = PublicClientApplication(client_id, authority=authority_url)
        flow = app.initiate_auth_code_flow(
            scopes=scopes,
            redirect_uri="https://login.microsoftonline.com/common/oauth2/nativeclient"
            )

        if "error" in flow:
            raise ValueError(flow.get("error"))
        
        print("go to this url to login:",flow["auth_uri"])

        auth_response_url = input('enter resulting redirect_url: ').rstrip('\n')
        auth_response=dict(parse.parse_qsl(parse.urlsplit(auth_response_url).query))
        #print(auth_response)

        result = app.acquire_token_by_auth_code_flow(flow, auth_response)
        return result['access_token']

Upvotes: 0

Alexander Kukla
Alexander Kukla

Reputation: 240

Getting a token requires few requests according to documentation. To make it possible you need to create flow and store it inside session before navigating to microsoft login page.

session["flow"] = _build_auth_code_flow(authority=app_config.AUTHORITY, scopes=app_config.SCOPE)

After navigation back to your application you should use this flow object as you did in your example

result = _build_msal_app(cache=cache).acquire_token_by_auth_code_flow(
        session.get("flow", {}), request.args)

Make sure that you didn't create it twice. In this case error will be similar, but state mismatch: XXXXXXXXXXXX vs XXXXXXXXXXXX. It may happened if you route called twice.

Upvotes: 3

AlfredoRevilla-MSFT
AlfredoRevilla-MSFT

Reputation: 3505

auth_response must be a dictionary built from the current HTTP request query params.

If this is a desktop application you must switch to PublicClientApplication. You can find a sample here.

Upvotes: 0

Related Questions