1

UPDATE (solution): I ended up simply extracting the token from the request that my frontend is sending with:

private async Task<string> GetApplicationAccessToken()
        {
            var token = this.Request
                            .Headers["Authorization"]
                            .First()
                            .Substring("Bearer ".Length);
            var assertion = new UserAssertion(token, _ASSERTION_TYPE);

            var authResult= await this._app.AcquireTokenOnBehalfOf(new []{""}, assertion)
                                       .ExecuteAsync();

            return authResult.AccessToken;
        }

ORIGINAL:

I want to funnel data from the MS Graph API (Azure AD endpoint) through my backend (.NET Core Web API) back to my Angular App, that requests the data. I am running into the problem that I am unable to get an Access token in my backend Web API.

I have Implemented a graph service according to this sample where user consent is prompted through a static html page that is being hosted on the web API. But I want to access MS Graph without explicit user consent.

I have looked for ways to get an access token for my web API without user consent, but not found anything helpful. Only stuff that confuses me. I have also supplied the App registration in Azure AD with application permissions and supplied my web API with sufficient information to the Azure app.

I am still not sure how to exactly adapt the sample code to work with my scenario where user consent is not required / an token already present in the request that my Angular app makes to my web API.

I am getting a userId (objectId.tenantId) in my GraphAuthProvider class when I am trying to call GetAccountAsync(). Yet I still don't receive a token from that call and don't get any error hints, just null.

public async Task<string> GetUserAccessTokenAsync(string userId)
        {
            var account = await _app.GetAccountAsync(userId);

            if (account == null)
            {
                throw new ServiceException(new Error
                {
                    Code    = "TokenNotFound",
                    Message = "User not found in token cache. Maybe the server was restarted."
                });
            }

My appsettings.json

"AzureAd": {
    "CallbackPath": "/signin-oidc",
    "BaseUrl": "https://localhost:63208",
    "ClientId": "[redacted]",
    "TenantId": "[redacted]",
    "ClientSecret": "[redacted]", // This sample uses a password (secret) to authenticate. Production apps should use a certificate.
    "Scopes": "user.read profile",
    "GraphResourceId": "https://graph.microsoft.com/",
    "GraphScopes": "User.Read.All Groups.Read.All"
  }

Can you point me in the right direction as to how to call the MS Graph API from my backend by using the application permissions?

Jayendran
  • 9,638
  • 8
  • 60
  • 103
BingeCode
  • 1,260
  • 1
  • 10
  • 21

1 Answers1

2

Client credential flow using directly http post

In you web api , you can directly create http request to authenticate using client credential flow and retire Microsoft Graph's access token :

POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded

client_id=535fb089-9ff3-47b6-9bfb-4f1264799865
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&client_secret=qWgdYAmab0YSkuL1qKv5bPX
&grant_type=client_credentials

Before that , you'd better admin consent the app permissions , see the detail steps in this article .

Client credential flow using MSAL.NET

If using the MSAL.NET , you can use below code sample for client credential flow :

// Even if this is a console application here, a daemon application is a confidential client application
IConfidentialClientApplication app;

#if !VariationWithCertificateCredentials
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
        .WithTenantId("{tenantID}")
        .WithClientSecret(config.ClientSecret)
        .Build();
#else
// Building the client credentials from a certificate
X509Certificate2 certificate = ReadCertificate(config.CertificateName);
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
    .WithTenantId("{tenantID}")
    .WithCertificate(certificate)
    .Build();
#endif

// With client credentials flows the scopes is ALWAYS of the shape "resource/.default", as the
// application permissions need to be set statically (in the portal or by PowerShell), and then granted by
// a tenant administrator
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };

AuthenticationResult result = null;
try
{
result = await app.AcquireTokenForClient(scopes)
                .ExecuteAsync();
}
catch(MsalServiceException ex)
{
// Case when ex.Message contains:
// AADSTS70011 Invalid scope. The scope has to be of the form "https://resourceUrl/.default"
// Mitigation: change the scope to be as expected
}

You can refer to this article and code sample on Github.

Client credential flow using Microsoft Graph .NET authentication library

From document : https://github.com/microsoftgraph/msgraph-sdk-dotnet-auth

You can use Client credential provider :

// Create a client application.
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
                .Create(clientId)
                .WithTenantId(tenantID)
                .WithClientSecret(clientSecret)
                .Build();
// Create an authentication provider.
ClientCredentialProvider authenticationProvider = new ClientCredentialProvider(confidentialClientApplication);
// Configure GraphServiceClient with provider.
GraphServiceClient graphServiceClient = new GraphServiceClient(authenticationProvider);

Or directly use MSAL.NET to authenticate using client credential flow and build the Microsoft Graph client like reply from @Philippe Signoret shows .

Nan Yu
  • 26,101
  • 9
  • 68
  • 148