azureoauth-2.0microsoft-graph-apimailboxazure-azd

Need admin approval unverified needs permission to access resources in your organisation that only an admin can grant


In my app i am trying to get the mail boxes of all the users registered in my users in azure portal using graph api and this is my code:

import React, { useState } from "react";
import { PublicClientApplication } from "@azure/msal-browser";
import { msalConfig } from "./msalConfig";
import axios from "axios";

    const GraphApiTest = () => {
      const [inboxEmails, setInboxEmails] = useState([]);
      const [sentEmails, setSentEmails] = useState([]);
    
      const msalInstance = new PublicClientApplication(msalConfig);
    
      const loginAndFetchEmails = async () => {
        try {
          await msalInstance.initialize();
    
          const accounts = msalInstance.getAllAccounts();
    
          if (accounts.length === 0) {
            // No logged-in user, perform login
            await msalInstance.loginPopup({
              scopes: [
                "Mail.Read",
                "Mail.Send",
                "User.Read",
                "User.Read.All",
                "User-Mail.ReadWrite.All",
                "Mail.ReadWrite.Shared",
                "MailboxSettings.Read",
                "Mail.Read.Shared",
              ], // Required scopes
            });
          }
    
          const account = msalInstance.getAllAccounts()[0]; // Use the first account
          let tokenResponse;
    
          try {
            // Attempt to acquire token silently
            tokenResponse = await msalInstance.acquireTokenSilent({
              account,
              scopes: [
                "Mail.Read",
                "Mail.Send",
                "User.Read",
                "User.Read.All",
                "User-Mail.ReadWrite.All",
                "Mail.ReadWrite.Shared",
                "MailboxSettings.Read",
                "Mail.Read.Shared",
              ], // Required scopes
            });
          } catch (silentError) {
            // Fallback to interactive login if silent acquisition fails
            console.warn("Silent token acquisition failed, fallback to popup.");
            tokenResponse = await msalInstance.acquireTokenPopup({
              account,
              scopes: [
                "Mail.Read",
                "Mail.Send",
                "User.Read",
                "User.Read.All",
                "User-Mail.ReadWrite.All",
                "Mail.ReadWrite.Shared",
                "MailboxSettings.Read",
                "Mail.Read.Shared",
              ], // Required scopes
            });
          }
    
          const accessToken = tokenResponse.accessToken;
          console.log("Access Token acquired:", accessToken);
    
          // Fetch received emails (Inbox)
          const inboxResponse = await axios.get(
            "https://graph.microsoft.com/v1.0/users/example@visionx060.onmicrosoft.com/mailFolders('inbox')/messages",
            {
              headers: {
                Authorization: `Bearer ${accessToken}`,
              },
            }
          );
    
          // Fetch sent emails
          const sentResponse = await axios.get(
            "https://graph.microsoft.com/v1.0/users/example@visionx060.onmicrosoft.com/mailFolders('sentitems')/messages",
            {
              headers: {
                Authorization: `Bearer ${accessToken}`,
              },
            }
          );
    
          // Update state with emails
          setInboxEmails(inboxResponse.data.value);
          setSentEmails(sentResponse.data.value); // Sent emails
    
          console.log("Sent Emails:", sentResponse.data.value);
        } catch (error) {
          console.error("Error during login or fetching emails:", error);
          if (error.response) {
            console.error("Response error:", error.response.data);
          }
        }
      };
    
      return (
        <div>
          <button onClick={loginAndFetchEmails}>Fetch Emails via Outlook</button>
    
          <h2>Inbox Emails</h2>
          <ul>
            {inboxEmails.map((email) => (
              <li key={email.id}>
                <strong>{email.subject}</strong> from{" "}
                {email.sender?.emailAddress?.address}
              </li>
            ))}
          </ul>
    
          <h2>Sent Emails</h2>
          <ul>
            {sentEmails.map((email) => (
              <li key={email.id}>
                <strong>{email.subject}</strong> to{" "}
                {email.toRecipients
                  .map((recipient) => recipient.emailAddress.address)
                  .join(", ")}
              </li>
            ))}
          </ul>
        </div>
      );
    };
    
    export default GraphApiTest;
    
    export const msalConfig = {
        auth: {
          clientId: "my client id", 
          authority: "https://login.microsoftonline.com/<tenant-id>", 
          redirectUri: "http://localhost:3000", // Adjust as per your app
        },
      };

I have admin account and i granted following permissions in azure for this app: permissions screenshot

Still i am getting this window when a user other than admin try to fetch the mailbox of other users: the issue with user login

while the admin can get the other users mailbox without any problem. I want the same for other users too that they can get the other users mailboxes too.


Solution

  • Initially, I too got same error when an non-admin user tried to access mails of other user in delegated scenario without shared access:

    enter image description here

    In delegated scenarios, app can access mailboxes or resources for which the signed-in user has permissions, such as their own mailbox, shared mailboxes, or mailboxes that the user has explicitly granted access to (via delegation).

    To allow non-admin users to access other users' mailboxes without needing delegation or sharing, you need to switch to application permissions and use the client credentials flow.

    In my case, I registered one application and granted Mail.Read permission of Application type with consent as below:

    enter image description here

    Now, I created one Node.js backend server project and installed dependencies in it with below commands:

    mkdir email-fetcher-backend
    cd email-fetcher-backend
    
    npm init -y
    npm install express axios body-parser dotenv
    

    Later, I added below code files to store credentials and fetch emails with token generated using client credentials flow:

    .env:

    TENANT_ID=tenantId
    CLIENT_ID=appId
    CLIENT_SECRET=secret_value
    

    server.js:

    const express = require('express');
    const axios = require('axios');
    const dotenv = require('dotenv');
    dotenv.config();
    
    const app = express();
    const port = 5000;
    
    const tenantId = process.env.TENANT_ID;
    const clientId = process.env.CLIENT_ID;
    const clientSecret = process.env.CLIENT_SECRET;
    const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
    
    // Fetch Access Token using Client Credentials Flow
    const getAccessToken = async () => {
      try {
        const response = await axios.post(tokenUrl, new URLSearchParams({
          client_id: clientId,
          client_secret: clientSecret,
          scope: 'https://graph.microsoft.com/.default',
          grant_type: 'client_credentials',
        }));
        return response.data.access_token;
      } catch (error) {
        console.error('Error getting access token:', error);
        throw new Error('Unable to get access token.');
      }
    };
    
    // Fetch Emails (Inbox and Sent Items) from Microsoft Graph
    app.get('/emails', async (req, res) => {
      try {
        const accessToken = await getAccessToken();
    
        // Fetch inbox emails
        const inboxResponse = await axios.get(
          'https://graph.microsoft.com/v1.0/users/sri@xxxxxxxx.onmicrosoft.com/mailFolders/inbox/messages',
          { headers: { Authorization: `Bearer ${accessToken}` } }
        );
    
        // Fetch sent items emails
        const sentResponse = await axios.get(
          'https://graph.microsoft.com/v1.0/users/sri@xxxxxxxxx.onmicrosoft.com/mailFolders/sentitems/messages',
          { headers: { Authorization: `Bearer ${accessToken}` } }
        );
    
        // Format inbox emails as per the required format
        const inboxEmails = inboxResponse.data.value.map(email => ({
          subject: email.subject,
          sender: email.sender.emailAddress.address,
        }));
    
        // Format sent emails as per the required format
        const sentEmails = sentResponse.data.value.map(email => ({
          subject: email.subject,
          to: email.toRecipients.map(recipient => recipient.emailAddress.address).join(", "),
        }));
    
        // Send the formatted data back to the frontend
        res.json({
          inbox: inboxEmails,
          sent: sentEmails,
        });
      } catch (err) {
        console.error('Error fetching emails:', err);
        res.status(500).send('Failed to fetch emails.');
      }
    });
    
    app.listen(port, () => {
      console.log(`Server running on http://localhost:${port}`);
    });
    

    When I run the server with node server.js command and visited http://localhost:5000/emails in browser, I got mails successfully like this:

    enter image description here

    Reference:

    List messages - Microsoft Graph v1.0