Can't access onedrive in another tenant

Jan C 20 Reputation points
2024-06-02T18:13:57.7533333+00:00

I want to create an aplication that will access and read all data from onedrive in another tenant.
The app will act like a daemon in backgroud, without any user intervention. I want to use MIcrosfot Graph API to access data in onedrive.

Naming convention:
X - my tenant, where my application is registered
Y - some other tenant whose onedrive I want to be accessible by my application in my tenant X

I use such scopeimage

And in my python code i use scopes=".defualt" and client msal.ConfidentialClientApplication.
Code for authorization:

client_app_auth = ConfidentialClientApplication(
    client_id=CLIENT_ID,
    client_credential=CLIENT_SECRET,
    authority="https://login.microsoftonline.com/" + TENANT_ID,
)
def save_refresh_token(refresh_token: str):
    with open(REFRESH_TOKEN_PATH, "w", encoding="utf-8") as file:
        file.write(json.dumps({"refresh_token": refresh_token}))
@app.get("/login")
def login():
    url = client_app_auth.get_authorization_request_url(
        scopes=SCOPES,
        redirect_uri="http://localhost:8000/auth/callback",
        prompt="consent",
    )
    return {"login_url": url}
@app.get("/auth/callback")
def auth_callback(code: str):
    result = client_app_auth.acquire_token_by_authorization_code(
        code,
        scopes=SCOPES,
        redirect_uri="http://localhost:8000/auth/callback",
    )
    if "access_token" in result:
        save_refresh_token(result["refresh_token"])
        return {"message": "Authentication successful", "results": result}
    else:
        return {"message": "Authentication failed", "details": result.get("error")}

Code for listing all users drives:

@app.get("/listdrive")
async def get_all_drive():
    with open(REFRESH_TOKEN_PATH, "r", encoding="utf-8") as file:
        refresh_token = json.load(file)["refresh_token"]
    auth_token = client_app_auth.acquire_token_by_refresh_token(
        refresh_token=refresh_token, scopes=SCOPES
    )
    if "access_token" in auth_token:
        save_refresh_token(auth_token["refresh_token"])
        access_token = auth_token["access_token"]
        graph_endpoint = "https://graph.microsoft.com/v1.0/users/" + USER_ID + "/drive"
        headers = {
            "Authorization": f"Bearer {access_token}",
            "Content-Type": "application/json",
        }
        user_response = requests.get(graph_endpoint, headers=headers)
        user_data = user_response.json()
        return user_data


This is configuration of authenticatoin in tenant X:
User's image

Program works fine when I login with my tenant X acccount (also global admin). I'm able to list drives and then use driveId to list files in it.
BUT, when i try to login with Y account i get such error:
User's image

I'm trying to login with Global Admin account from tenant Y, so I dont undertstand why would i need some admin approval if I AM the admin.
I've added account from tenant Y to tenant X as a Guest/ExternalAzureAD.

Could somebody help me undestand where I'm making the mistake and how to fix it?

Or maybe I'm approching my endgoal from wrong angle altogether?

Microsoft Graph
Microsoft Graph
A Microsoft programmability model that exposes REST APIs and client libraries to access data on Microsoft 365 services.
11,140 questions
OneDrive
OneDrive
A Microsoft file hosting and synchronization service.
920 questions
SharePoint
SharePoint
A group of Microsoft Products and technologies used for sharing and managing content, knowledge, and applications.
10,078 questions
0 comments No comments
{count} votes

Accepted answer
  1. AsithwMSFT 1,420 Reputation points Microsoft Vendor
    2024-06-02T21:34:52.9366667+00:00

    Hello Jan C

    To create an application that accesses and reads data from OneDrive in another tenant using the Microsoft Graph API without user interaction, you'll need to follow the client credential flow.

    It appears that you have successfully registered an Azure AD app and obtained a client ID and client secret, and also confirmed the required scopes. Here, only application scopes are needed. (no delegated scopes)

    The next step is to generate a consent URL for Tenant Y

    your application will act on behalf of a tenant without a signed-in user. An administrator from the tenant Y must grant consent to your application to access their OneDrive data

    Here is the format for the consent URL. The admin of Tenant Y should use this URL to grant consent to your app:

    GET https://login.microsoftonline.com/{tenantY-Id}/adminconsent?
    
    client_id=00001111-aaaa-2222-bbbb-3333cccc4444
    
    &state=12345
    
    &redirect_uri=http://localhost/myapp/permissions
    

    Once this is successfully done, it will redirect to the redirect_url. Here is the format:

    http://localhost/myapp/permissions?tenant=aaaabbbb-0000-cccc-1111-dddd2222eeee&state=state=12345&admin_consent=True
    

    If admin_consent=True, it means consent is successful.

    For more details, refer to this link. https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow#request-the-permissions-from-a-directory-admin

    Now, you can query Tenant Y’s OneDrive data using the client credential flow.

    If you wish to use the Python GraphClient, here is a sample for you:

    https://github.com/microsoftgraph/msgraph-training-python/blob/main/app-auth/graphapponlytutorial/graph.py

    class Graph:
        settings: SectionProxy
        client_credential: ClientSecretCredential
        app_client: GraphServiceClient
    
        def __init__(self, config: SectionProxy):
            self.settings = config
            client_id = self.settings['clientId']
            tenant_id = self.settings['tenantId']
            client_secret = self.settings['clientSecret']
    
            self.client_credential = ClientSecretCredential(tenant_id, client_id, client_secret)
            self.app_client = GraphServiceClient(self.client_credential) # type: ignore
    
        async def get_app_only_token(self):
            graph_scope = 'https://graph.microsoft.com/.default'
            access_token = await self.client_credential.get_token(graph_scope)
            return access_token.token
    
        async def make_one_drive_graph_call(self):
            # INSERT YOUR CODE HERE
            return
    
    
    

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


0 additional answers

Sort by: Most helpful