0

I have two apps both registered on Azure AD:

-APP_1: a web app that runs in flask with SSO through Azure AD

tenant_id = "tenant_id_of_APP_1_and_APP_2"
client_id_app_1 = "app_1"
client_secret_app_1 = "secret_app_1"

-APP_2: another app that makes requests to the first app:

tenant_id = "tenant_id_of_APP_1_and_APP_2"
client_id_app_2 = "app_2"
client_secret_app_2 = "secret_app_2"

the endpoints on the APP_1 are restricted. When the user reaches the web app through the browser he is automatically requested to login, and after he is identified it can navigate any endpoint he has access to otherwise he gets PermissionError.

Now I want to give access to some endpoints to the APP_2, I can get an access_token from azure in this way (I think I'm going something wrong because when I request a token I should specify that is for APP_1, but i cannot find the argument that specifies it):

import requests
# Authenticate and get an access token
auth_url = f'https://login.microsoftonline.com/{tenant_id_of_APP_1_and_APP_2}/oauth2/v2.0/token'
data = {
    'grant_type': 'client_credentials',
    'client_id': client_id_app_2,
    'client_secret': client_secret_app_2,
    'scope': 'https://graph.microsoft.com/.default'
}
response = requests.post(auth_url, data=data).json()
access_token = response['access_token']

and then I can make the request to the protected API:

headers = {
    'Authorization': f'Bearer {access_token}',
    'Content-Type': 'application/octet-stream',
}

x = requests.post("https://localhost:5000/test_api", headers=headers)
response = requests.get("http://localhost:5000/test_api", headers=headers)

What should I do the server (APP_1) when I get such request? I should validate the access_token to verify the user but I cannot decode it with jwt because I keep getting invalid signature:

token = request.headers.get("Authorization").removeprefix("Bearer ")
public_key = get_public_key(token)
decoded = jwt.decode(
    token,
    public_key,
    verify=True,
    algorithms=["RS256"],
    audience=[client_id_app_1],
)

is there a better way to just send the access_token to azure for validation?

EDIT:

So I changed the code in this way: on APP_2:

server_client_id = server_app_config.CLIENT_ID
app = msal.ConfidentialClientApplication(client_id=client_id_app_2, client_credential=client_secret_app_2, authority=f"https://login.microsoftonline.com/{tenant_id_of_APP_1_and_APP_2}")
result = app.acquire_token_for_client(scopes=[f"{client_id_app_1}/.default"])
access_token = result["access_token"]
headers = {
    'Authorization': f'Bearer {access_token}',
    'Content-Type': 'application/octet-stream',
}
response = requests.post(url, headers=payload)

on APP_1 I validate the token in this way:

token = request.headers.get("Authorization").removeprefix("Bearer ")
public_key = get_public_key(token)
decoded = jwt.decode(
    token,
    public_key,
    verify=True,
    algorithms=['RS256'],
    audience=[client_id_app_1],
)

Is this how the workflow should be?

Alberto B
  • 315
  • 1
  • 2
  • 10

1 Answers1

0

If you validate access token generated with Microsoft Graph scope, you will get "Invalid Signature" error like below as it not meant for application.

enter image description here

To resolve the error, you must use app-specific endpoint in scope while fetching token like api://client_app_ID/.default

Note that, client credentials flow works with Application type permissions. If you are using client credentials flow to get tokens, you need to add App roles in Server App instead of exposing an API.

I registered one Server App and created few App roles in it like below:

enter image description here

Now, I registered one Client App and added above App roles in it like this:

enter image description here

Make sure to grant admin consent to added App roles like below:

enter image description here

Now I ran below python code to get access token by changing scope parameter:

import requests
# Authenticate and get an access token

tenant_id = "3f5c7a77-062d-426c-858xxxxxxxxxxxx"
client_id_app_1 = "6d00f9c7-caaa-46d5-abxxxxxxxxxxxx"
client_id_app_2 = "a3bebc65-2ba6-4fxxxxxxxxxxxxx"
client_secret_app_2 = "er48xxxxxxxxxxxxxxxxxxxxxxxx"

auth_url = f'https://login.microsoftonline.com/tenant_id/oauth2/v2.0/token'
data = {
    'grant_type': 'client_credentials',
    'client_id': client_id_app_2,
    'client_secret': client_secret_app_2,
    'scope': 'api://client_id_app_1/.default'
}
response = requests.post(auth_url, data=data).json()
token = response['access_token']
print(token)

Response:

enter image description here

When I decoded above access token by pasting it in jwt.io, I got roles claim with permissions and signature verified successfully like below:

enter image description here

If you added scopes in Expose an API tab instead of adding App roles, then you need to use Delegated flows like authorization code flow, username password flow etc... to get access token that works with Delegated permissions.

Reference:

jwt - The signature verification is is failing - Stack Overflow by Rukmini

Sridevi
  • 10,599
  • 1
  • 4
  • 17