I'm implementing the authorization_code flow with AzureAD OIDC for my application in python using MSAL library.
I need to use v1 OAuth2 endpoints in order to require MFA authentication (which doesn't seem to be availabile in v2.0), so:
https://login.microsoftonline.com/{tenant}/oauth2/
Instead of
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/
Here's the sample code:
msal_app = ConfidentialClientApplication(
client_id="xxx",
client_credential="yyy",
oidc_authority="https://sts.windows.net/{tenant}", # Force OAuth2 v1.0
)
...
flow = msal_app.initiate_auth_code_flow(
scopes=["User.Read"],
redirect_uri="zzz",
)
# Redirect to: flow["auth_uri"] + "&amr_values=ngcmfa" # OAuth2 v2.0 does not accept amr_values which requires the MFA authentication
...
payload = msal_app.acquire_token_by_auth_code_flow(flow, dict(request.query_params))
Here's what the payload
contains:
It worked, but the access_token
is weird, it does not appear to be a JWT token (which usually begins with ey
).
If I try to use it with Microsoft Graph to use the User.Read
scope I asked for, it fails:
requests.get(
"https://graph.microsoft.com/v1.0/me",
headers={"Authorization": f"Bearer {payload['access_token']}"}
).json()
{'error': {'code': 'InvalidAuthenticationToken', 'message': "IDX14100: JWT is not well formed, there are no dots (.).\nThe token needs to be in JWS or JWE Compact Serialization Format. (JWS): 'EncodedHeader.EndcodedPayload.EncodedSignature'. (JWE): 'EncodedProtectedHeader.EncodedEncryptedKey.EncodedInitializationVector.EncodedCiphertext.EncodedAuthenticationTag'.", 'innerError': {'date': '2024-06-03T08:58:59', 'request-id': 'qqq', 'client-request-id': 'www'}}}
So question: why is it giving an access_token
that cannot be used?
Note: If I switch to OAuth2 v2.0, it works without issues and returns a valid JWT token in access_token
, however, I cannot use amr_values
to enforce MFA, which is a requirement.
Can you help me understanding what is going on?
Note that: To force Multiple factor authentication, you need to pass
amr_values
in the authorize URL. Refer this MsDoc
https://login.microsoftonline.com/TenantID
. Refer this MsDocFor sample, I modified the code by passing the authorize URL directly and got the results successfully:
# Create a Flask web application
app = Flask(__name__)
app.secret_key = 'os.urandom(24)'
client_id = "ClientID"
tenant_id = "TenantID"
redirect_uri = "RedirectURL"
msal_app = ConfidentialClientApplication(
client_id=client_id,
authority="https://login.microsoftonline.com/" + tenant_id
)
# Define the authorization endpoint URL
auth_url = (
"https://login.microsoftonline.com/"
+ tenant_id
+ "/oauth2/authorize?"
+ "response_type=code&"
+ "client_id=" + client_id + "&"
+ "state=12345&"
+ "resource=https://graph.microsoft.com&"
+ "client-request-id=67890&"
+ "amr_values=ngcmfa&"
+ "redirect_uri=" + redirect_uri
)
@app.route("/")
def home():
return redirect(auth_url)
@app.route("/get_a_token")
def get_a_token():
auth_code = request.args.get('code')
# Acquire an access token using the authorization code
try:
token_response = msal_app.acquire_token_by_authorization_code(
auth_code,
scopes=["User.Read"],
redirect_uri=redirect_uri
)
except Exception as e:
return f"Token acquisition failed: {str(e)}"
if "access_token" in token_response:
access_token = token_response["access_token"]
print("Access Token:", access_token)
# Use the access token to make a request to Microsoft Graph
graph_response = requests.get(
"https://graph.microsoft.com/v1.0/me",
headers={"Authorization": f"Bearer {access_token}"}
)
if graph_response.status_code == 200:
user_data = graph_response.json()
return f"User: {user_data['displayName']}, Email: {user_data['mail']}"
else:
return f"Failed to fetch user data from Microsoft Graph: {graph_response.text}"
else:
error_message = token_response.get("error_description", "Unknown error")
return f"Token acquisition failed: {error_message}"
# Run the Flask application
if __name__ == "__main__":
app.run(debug=True)
The user is redirected to MFA page:
And after successful sign-in I got the access token:
Using the access token, I am able to successfully query Micrsoft Graph API like below:
Reference:
Azure Active Directory multi-factor check for authorization - Stack Overflow by Joey Cai