Reducing Interactive Prompts When Using Different Scopes Azure

Raymond Knetemann 0 Reputation points
2025-02-24T13:33:49.32+00:00

Hi everyone,

Let me start by saying that I am relatively new to both this forum and the microsoft infrastructure. I am using the python sdk for my application that requires the program/user to have access to some Azure Graph API calls and Azure Keyvault secrets.

I am using:

  • Python 3.11.4
  • Python libraries:
    • azure-identity 1.19.0
    • azure-keyvault-secrets 4.9.0
    • msal 1.31.1
    • msal-extensions 1.2.0
    • msgraph-core 1.2.1
    • msgraph-sdk 1.12.0

The problem I am facing right now is that I require two different resource scopes for the Azure AD. This right now requires the user to interactively login 2x. Because of caching I only have to do this the first time the user logs in, though I still think this is not nice behaviour.

I have written a basic MicrosoftHandler class which handles the authentication and some other stuff, which I do not provide in the code below:

import asyncio
import aiofiles
from azure.identity import TokenCachePersistenceOptions, InteractiveBrowserCredential, AuthenticationRecord
from azure.keyvault.secrets import SecretClient
from msgraph import GraphServiceClient
from kiota_abstractions.base_request_configuration import RequestConfiguration
from msgraph.generated.models.drive_item import DriveItem
from msgraph.generated.models.file import File
from msgraph.generated.models.folder import Folder
from kiota_abstractions.api_error import APIError
import os
import dotenv

dotenv.load_dotenv()

class MicrosoftHandler():
    def __init__(self):       
        self.tenant_id = os.getenv("AZURE_TENANT_ID")
        self.client_id = os.getenv("AZURE_CLIENT_ID")
        
        self.key_vault_name = os.environ["KEY_VAULT_NAME"]
        self.vault_url = f"https://{self.key_vault_name}.vault.azure.net"
        
        self.graph_scopes = ["https://graph.microsoft.com/User.Read"] 
        self.vault_scopes = ["https://vault.azure.net/user_impersonation"]
        
        self.cache_options = TokenCachePersistenceOptions(allow_unencrypted_storage=True)  

            
    async def authenticate(self, change_user=False):        
        try: 
            
            if change_user:
                auth_record = None
            else:
                try:
                    async with aiofiles.open("authenticated_user.json", "r") as f:
                        record_data = await f.read()
                    auth_record = AuthenticationRecord.deserialize(record_data)
                except FileNotFoundError:
                    auth_record = None
                except Exception as e:
                    print(f"An error occurred while reading the authentication record: {e}")
                    auth_record = None
            
            self.credential = InteractiveBrowserCredential(
                tenant_id=self.tenant_id,
                client_id=self.client_id,
                cache_persistence_options=self.cache_options,
                authentication_record=auth_record
            )
                   
            print("Authenticating graph and vault...")
            auth_record = self.credential.authenticate(scopes=self.graph_scopes).serialize()
            auth_record = self.credential.authenticate(scopes=self.vault_scopes).serialize()
            
            with open("authenticated_user.json", "w") as f:
                f.write(auth_record)

            self.graph_client = GraphServiceClient(credentials=self.credential, scopes=self.graph_scopes)
            
            self.vault_client = SecretClient(vault_url=self.vault_url, credential=self.credential)
            
            authenticated_user = await self.graph_client.me.get() 
                    
            return authenticated_user
        except Exception as e:
            raise

The documentation hasn't been very helpful so far as I find a lot of different ways to handle authentication flows. credential.authenticate unfortunately doesn't allow me to request token(s) for multiple scopes at once.

As a little side information, the authentication for the graph scopes creates a nocae cache file and the vault scopes authentication creates a cae cache file. After these files are created for the first time, the user can authenticate without having to interactively login.

Would it be possible to only have to login once, while still being able to retrieve tokens from two different scopes?

Kind regards,

Raymond Knetemann

Microsoft Security | Microsoft Entra | Microsoft Entra ID
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Kancharla Saiteja 5,485 Reputation points Microsoft External Staff Moderator
    2025-02-27T05:41:59.9466667+00:00

    Hi @Raymond Knetemann ,

    Thank you for posting your query on Microsoft Q&A. I am Saiteja from Q&A will be assisting you with your query.

    Based on your query, I understand you would like to call two scopes in single call to avoid multiple interactions.

    You can configure two applications in Azure App registrations and make one as an API and add it to the other application. In order to perform this, please follow the document mentioned here.

    1. Sign in to the Microsoft Entra admin center as at least a Cloud Application Administrator.
    2. If you have access to multiple tenants, use the Settings icon in the top menu to switch to the tenant containing the app registration from the Directories + subscriptions menu.
    3. Perform the steps in register an application and skip the Redirect URI (optional) section. You don't need to configure a redirect URI for a web API since no user is logged in interactively.

    At the place of scope in the specified document, provide the required scope for your application.

    First, follow these steps to create an example scope named Employees.Read.All:

    Select Expose an API.

    At the top of the page, select Add next to Application ID URI. This defaults to api://<application-client-id>. The App ID URI acts as the prefix for the scopes you'll reference in your API's code, and it must be globally unique. Select Save.

    Select Add a scope:

    Once you have successfully added the scope, you will see that in this format: https://contoso.com/api/Employees.Read.All

    Now you need to register the application same as register an application . Once the application is registered, Select API permissions, then Add a permission and select My APIs in the sidebar. Now you will see your previous registration as exposed API in this format: https://contoso.com/api/Employees.Read.All. Add the other permissions required to your application and provide the admin consent for the application permissions. See the attached screenshot:

    User's image

    Once the consent has been provided, you can try retrieving the token for the same application with two different scopes in the application.

    I hope this information is helpful. Please feel free to reach out if you have any further questions.

    If the answer is helpful, please click "Accept Answer" and kindly "upvote it". If you have extra questions about this answer, please click "Comment"


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.