Getting access token using MSAL fails

105 Views Asked by At

I am developing an azure web app service called emailservice, which is supposed to communicate to a mailbox created in office365 exchange online. Suppose the user account for the mailbox is x @y-domain.com.

The webapp is suppose to retrieve messages form the inbox or sent messages. I have decided to use Microsoft graph api for this matter.

To be able to have successful call to graph API, I need to register the app on Azure. I have done that as well. Delegated access has been given and Mail.ReadWrite and Mail.send permission under graph has been granted by an admin. We are good on the consent side as well. I am using MSAL to get the token. `y

const authClient = new msal.ConfidentialClientApplication({
    auth: {
        clientId,
        clientSecret,
        authority: `${azureActiveDirectoryEndPoint}${tenantId}`,
    }
});
function getToken(): Promise<string> {
    return authClient.acquireTokenByClientCredential({
        scopes,
    }).then(authResult =>{
        if (!authResult) {
            throw new Error('Authentication failed!')
        }
        return authResult.accessToken;
    })
}
    async retrieveNewEmails() {
        

        const accessToken = await getToken();
        // some code
    }

I am currently testing this code on my local machine. My call to acquireTokenByClientCredential fails. I have few questions.

  • Is this a correct method to call for my scenario in order to get accessToken

  • If the method is correct, I think I am not setting the scope correctly I have tried few different scopes. I think I do not have proper understanding of scopes and what it should include I know it should include the end point which is 'https://graph.microsoft.us/v1.0/ I think it should include user account as well. I guess it should include [email protected] as well I am not sure it should have any indication of the permissions or not like Mail.ReadWrite

  • Here is what I have tried and the error messages const scopes = ['https://graph.microsoft.us/v1.0/[email protected]']; no token is generated and it prompts me with error saying “ERROR [ExceptionsHandler]
    invalid_scope: 1002012 - [2024-03-13 22:26:25Z]: AADSTS1002012: The provided value for scope https://graph.microsoft.us/v1.0/me is not valid. Client credential flows must have a scope value with /.default suffixed to the resource identifier (application ID URI). “

        Why /.default?
    
     const scopes = ['https://graph.microsoft.us/v1.0/ [email protected]'/.default'];
           No token is generated and it prompts me with error saying “ERROR [ExceptionsHandler] 
           invalid_resource: 500011 - [2024-03-13 22:21:50Z]: AADSTS500011: The resource principal named 
           https://graph.microsoft.us/v1.0/x was not found in the tenant named * . This can happen if 
           the application has not been installed by the administrator of the tenant or consented to by 
           any user in the tenant. You might have sent your authentication request to the wrong tenant.”
    

I would appreciate if you would help me to find what is missing.

As mentioned I tried two different scopes above.

2

There are 2 best solutions below

1
Gaurav Mantri On

Please try by changing the scope to https://graph.microsoft.us/.default. You may find this link helpful about understanding scopes: https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc.

4
Sridevi On

Your scope should be https://graph.microsoft.com/.default while generating token with client credentials flow and grant permissions of Application type.

I registered one Azure AD application and granted API permissions of Application type as below:

enter image description here

Now, I used below modified code to generate access token and used it to list messages like this:

const msal = require('@azure/msal-node');
const axios = require('axios');

const tenantId = "tenantId";
const clientId = "appId";
const clientSecret = "secret";
const scopes = ["https://graph.microsoft.com/.default"]; // Microsoft Graph scope

const authClient = new msal.ConfidentialClientApplication({
    auth: {
        clientId,
        clientSecret,
        authority: `https://login.microsoftonline.com/${tenantId}`,
    }
});

async function getToken() {
    try {
        const tokenResponse = await authClient.acquireTokenByClientCredential({
            scopes
        });
        if (tokenResponse && tokenResponse.accessToken) {
            return tokenResponse.accessToken;
        } else {
            throw new Error('Failed to acquire access token');
        }
    } catch (error) {
        throw new Error('Error retrieving access token: ' + error);
    }
}

async function listMessages(accessToken, userUPN) {
    try {
        const response = await axios.get(`https://graph.microsoft.com/v1.0/users/${userUPN}/messages`, {
            headers: {
                Authorization: `Bearer ${accessToken}`
            }
        });
        const messages = response.data.value;
        console.log("\nMessages:");
        messages.forEach(message => {
            console.log(`- ${message.subject}`);
        });
    } catch (error) {
        console.error('Error retrieving messages:', error.message);
    }
}

async function retrieveEmails() {
    try {
        const accessToken = await getToken();
        console.log('Access Token:', accessToken);
        const userUPN = "[email protected]"; 
        await listMessages(accessToken, userUPN);
    } catch (error) {
        console.error('Error retrieving emails:', error.message);
    }
}

retrieveEmails();

Response:

enter image description here