0
votes

I am trying to send mail using Graph API from Angular app.

In Azure active directory i have given the API permission for Mail.Send and other mail related things.

below is the code

const resource = {
    grant_type : 'client_credentials',
    clientID: '****************************',
    redirectUri: 'http://localhost:4200/',
    validateAuthority : true,
    popUp: true,
    scopes: ['mail.send'],
    resource : 'https://graph.microsoft.com'
  };

  let murl : any
  murl = 'https://graph.microsoft.com/v1.0/users/' + '[email protected]' + '/sendMail';

  this.token = await this.msalService.acquireTokenSilent(resource)
      .catch((reason) => {
        // console.log(reason);
      });

      let header = new HttpHeaders({
        'Content-Type': 'application/json',
          'Authorization': 'Bearer ' + this.token.accessToken});
      let options = { headers: header };

      let resp =  this.http.post(murl, this.sendMailDet, options)
       resp.subscribe(
          (data)=>{
            console.log( data);
        }
        );

But when i send mail i get below error.

error: {code: "InvalidAuthenticationToken", message: "Access token validation failure. Invalid audience.",…}
code: "InvalidAuthenticationToken"
innerError: {date: "2021-01-06T04:52:20", request-id: "*********",…}
message: "Access token validation failure. Invalid audience."

I am using scopes: ['mail.send'] in resources still i am getting this error. Also i am using accessToken only from this.msalService.acquireTokenSilent(resource).

Token in jwt.ms showing aud as "aud": "https://graph.microsoft.com", and "scp": "Directory.Read.All email Mail.Read Mail.Read.Shared Mail.ReadBasic Mail.ReadWrite Mail.ReadWrite.Shared Mail.Send Mail.Send.Shared MailboxSettings.Read MailboxSettings.ReadWrite User.Export.All User.Invite.All User.ManageIdentities.All User.Read User.Read.All User.ReadBasic.All User.ReadWrite User.ReadWrite.All profile openid",

can anyone please help me to check this issue.

1
When acquireTokenSilent method is called, library first checks the cache in browser storage to see if a valid token exists and returns it. So in fact you are not getting an application token for Microsoft Graph here.Allen Wu
I notice that you are using client_credentials as grant_type, which means you want to use application token rather than user token. Is that right?Allen Wu
Hi @AllenWu , Initially when user log in there will be a token acquired for application. But to send mail i am taking different token with scopes included. And i am getting a different token. i verified in jwt.ms and found the scopes (which i mentioned in the last part. of question). So i think the token is proper. Or am i missing anything ?Nelson Thimothiyose
Well, actually i tried this from different sources, this client_credentials was there in that sample. My aim is to send mail from a user account. So do i have to change anything in grant_type : 'client_credentials' ?Nelson Thimothiyose
No. The the token is NOT proper. That is why I'm asking you "you want to use application token rather than user token?". For a proper token which is acquired by client_credentials flow, there should be roles claim rather than scp claim. And roles claim is Application permission. scp claim is Delegated permission. You should be able to find these 2 type of permission when you add it in AAD app.Allen Wu

1 Answers

0
votes

I had to send the token from angular , because first the accept or cancel pop up will show. So had to handle from angular. But it was giving me error mentioned in the question. So i created a API for that and send he token and other details from angular.

This is Angular part

data : object with From, To, CC, bcc, emailbody etc . using these values we create json email format in API

async sendmail(data): Promise<Boolean>  
  {

    let result : any ; 
    const resource = {
      clientID: environment.config.identityConfig.clientId , 
      redirectUri: environment.config.identityConfig.redirectUri , // 'http://localhost:4200/'
      validateAuthority : true,
      popUp: true,
      scopes: ['mail.send'],
      resource : 'https://graph.microsoft.com'
    };
    this.token = await this.msalService.acquireTokenSilent(resource)
    .catch((reason) => {
      console.log(reason);
    });

    this.atoken = this.token.accessToken;
    data.EMailAccessToken = this.token.accessToken;
    this.mailHttpService.post('/sendmailgapi', data, null, null, ApiUrl.FileService). // This is custom 
   subscribe((data) => {
     result = data; 
   });

   return result;
  }

Then in API,

this.applicationContext.GetGraphClient() : This another method from which using Client ID, tenant ID, ClientSecret and using ConfidentialClientApplicationBuilder , we create a Graph client.

sendMailDto : Passing object

GraphServiceClient graphClient = this.applicationContext.GetGraphClient();
var fromEmail = sendMailDto.EMailFrom ; // 
var accessToken = sendMailDto.EMailAccessToken;
var message = createMessage(sendMailDto);
var restClient = new RestClient("https://graph.microsoft.com/v1.0/users/" + fromEmail + "/sendMail");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer " + accessToken 
request.AddParameter("", message, ParameterType.RequestBody);
IRestResponse response = restClient.Execute(request);
               

I'm not sure if this is the correct method or not , but i had to do a workaround to solve the issue.