Description:
I have been working on an Outlook add-in from a template that is created using Yeoman Generator for Office Add-ins (javascript based and xml manifest). The idea is to make an easy way to download emails and store it in my organisations document system. This is mostly working, i can browse my email folders and list their displaynames, so i have access to something but if i try to download an email from the Exchange server i get an error:
"Resource not found for the segment 'messages'" or "Resource not found for the segment '<itemId>'"
Depending on if i use the me/ or not.
Im thinking theres is something wrong in my Azure App registration or my MSAL use somewhere but i cant quite figure out what, so now im asking for help here.
My setup:
Im running the web add-in locally on localhost:3000 - it is also deployed as an app service on azure to get a client id, and to be able to do an app registration
My local manifest file in the project have this bit:
<Permissions>ReadWriteMailbox</Permissions>
but nothing else regarding security or permissions.
I also have the client id from the app registration as my id in the manifest.
In the code im first requesting an accessToken with Msal:
import { PublicClientApplication } from "@azure/msal-browser";
const msalConfig = {
auth: {
clientId: "<my client id>",
authority: "https://login.microsoftonline.com/common",
redirectUri: "https://localhost:3000/auth.html",
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true,
},
};
const msalInstance = new PublicClientApplication(msalConfig);
async function getAccessToken() {
try {
await msalInstance.initialize();
const accounts = msalInstance.getAllAccounts();
if (accounts.length === 0) {
await msalInstance.loginPopup({
scopes: ["openid", "profile", "Mail.Read", "Mail.ReadWrite"]
});
}
const response = await msalInstance.acquireTokenSilent({
scopes: ["openid", "profile", "Mail.Read", "Mail.ReadWrite"]
account: msalInstance.getAllAccounts()[0],
});
return response.accessToken;
} catch (error) {
console.error(error);
if (error instanceof InteractionRequiredAuthError) {
const response = await msalInstance.acquireTokenPopup({
scopes: ["openid", "profile", "Mail.Read", "Mail.ReadWrite"],
});
return response.accessToken;
}
}
}
This is working as far as i can tell and i get an accessToken.
Next step would be to create a graph client to access my emails and download the file:
const { Client } = require('@microsoft/microsoft-graph-client');
async function getGraphClient(accessToken) {
return Client.init({
authProvider: (done) => {
done(null, accessToken);
}
});
}
try {
const accessToken = await getAccessToken();
console.log("Test accessToken " + accessToken);
const emailId = Office.context.mailbox.item.itemId;
console.log("Test emailId " + emailId);
const client = await getGraphClient(accessToken);
const response = await client.api("/me/mailFolders").get();
const mailFolders = response.value;
mailFolders.forEach(folder => {
console.log(Folder Name: ${folder.displayName});
});
//const email = await client.api("/me/messages/" + emailId + "/$value").get();
const email = await client.api("/messages/" + emailId + "/$value").get();
} catch (error) {
console.error('Error fetching and downloading email:', error);
}
This is where i get the error Resource not found for the segment 'messages'
Im guessing that there might be something wrong with the setup in my app registration, but i have no idea what.
Its set up as a SPA (Single Page App).
In Authentication under "Select the tokens you would like to be issued by the authorization endpoint:" i checked both checkmarks
Access tokens
ID tokens
and i chose the multitenant in supported account types
In API Permissions i listed:
- Mail.Read
- Mail.ReadWrite
- offline_access
- openid
- profile
All of them are delegated
I also added a client secret, but isn't using it for anything - not sure if that should be used somewhere.
I also exposed my API but not really doing much else in the app registration with that bit.
What to do?
Im hoping someone have an idea about what im doing wrong, all the guides i found online and via chatGTP suggest this setup should be the right one, im mostly worried that im missing something small somewhere
Anyway thank you for looking into this