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?

1 Answer 1

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 .

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you! That is helpful. I have found a solution that works for me (see above).
@BingeCode , the code is for on-behalf- of flow: learn.microsoft.com/en-us/azure/active-directory/develop/…
That seems i misunderstand your requirement that "without sign-in"
The link would have been exactly what I was looking for. Thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.