3
votes

I have a SPA application that communicates with my backend Web API using AAD v2 authentication. Now I'm developing a console app to call Microsoft Graph on behalf of the user signed into the SPA app.

I have a valid access token of the user (used to call backend Web API). I want to use this access token to request a new token for accessing MS Graph.

Here is the code of the console app for requesting a new access token with MS Graph scopes using MSAL.NET:

string clientId = "<clientId>";
string clientSecret = "<clientSecret>";
string accessToken = "<validAccessTokenForWebApi>";
string assertionType = "urn:ietf:params:oauth:grant-type:jwt-bearer";
string[] scopes = new string[] { "User.Read", "Mail.Send" };
string graphAccessToken = null;

try
{
    var app = ConfidentialClientApplicationBuilder
                .Create(clientId).WithClientSecret(clientSecret).Build();

    var userAssertion = new UserAssertion(accessToken, assertionType);

    var result = app.AcquireTokenOnBehalfOf(scopes, userAssertion)
                    .ExecuteAsync().GetAwaiter().GetResult();

    graphAccessToken = result.AccessToken;
}
catch (MsalServiceException ex)
{
    throw;
}

But when I call app.AcquireTokenOnBehalfOf() I get an exception:

AADSTS50013: Assertion failed signature validation. [Reason - The provided signature value did not match the expected signature value., Thumbprint of key used by client: 'BB839F3453C7C04068B078EDADAB8E6D5F382E76', Found key 'Start=06/04/2019 00:00:00, End=06/04/2021 00:00:00']

What is the reason? What is the right way of getting access token on behalf of a user?

UPDATE - why do I need console app?

I could call Graph API directly from my backend API, but some actions may be delayed by the user (e.g. send mail using Graph API after 30 minutes). That is why I need to do this using the console app that runs on schedule.

1
You might have a problem with your app registrations on Azure Portal. Take a look at this sample as a reference on how to configure them. Also, this answer might be relevant to you: stackoverflow.com/questions/54008866/…TiagoBrenck
You may want to provide the raw request (remove secrets) resulting from the failing call. The error msg seems to point to invalid access token format.Marc
@FIL I have updated my answer to tell you how to call Graph API. Please check it.Jim Xu
@FIL I check your code. You seem that you did not provide authority endpoint.Jim Xu

1 Answers

1
votes

If you want to use OAuth 2.0 On-Behalf-Of flow, I think you do not need to develop a console application to call graph api. You can directly use your backend Web API application to acquire access token then call Microsoft Graph. According to my understanding, you just do these steps

  1. Sign-in the user in the client application
  2. Acquire a token to the Web API (TodoListService) and call it.
  3. The Web API then calls another downstream Web API (The Microsoft Graph).

enter image description here

For more details, please refer to the sample.

Regarding how to get access token with on behalf flow in the console application, The detailed steps are as below.

Register the web api app

  1. Register APP
  2. Create Client secrets
  3. Configure permissions to access Graph API
  4. Configure an application to expose web APIs(Add scope for the api)

Register the SAP app

  1. Register APP
  2. Create Client secrets
  3. Configure permissions to access web API

Configure known client applications for web API application

  1. In the Azure portal, navigate to your Web api app registration and click on the Manifest section.

  2. Find the property knownClientApplications and add the Client IDs of the SAP applications

Get access token to call web api

   GET https://login.microsoftonline.com/common/oauth2/v2.0/authorize
?scope=<you web api scope> openid
    &redirect_uri=<your sap app redirect url>
    &nonce=test123
    &client_id=<you sap app client id>
    &response_type=id_token token

get access token with on behalf flow

REST API

POST https://login.microsoftonline.com/common/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&client_id=<you web api client id>
&assertion=<you acess token you get in above steps>
&client_secret=<you app secret> 
&scope=https://graph.microsoft.com/user.read
&requested_token_use=on_behalf_of

MSAL.net Code

string[] scopes = { "user.read" };
            string accesstoken = "";

            string appKey = "yor web api client secret";
            string clientId = "your web api application id";

            var app = ConfidentialClientApplicationBuilder.Create(clientId)
              .WithClientSecret(appKey)
              .Build();
            UserAssertion userAssertion = new UserAssertion(accesstoken, 
 "urn:ietf:params:oauth:grant-type:jwt-bearer");
            var result = app.AcquireTokenOnBehalfOf(scopes, userAssertion).ExecuteAsync().Result;
            Console.WriteLine(result.AccessToken);