Daemon app that calls web APIs - acquire a token

After you've constructed a confidential client application, you can acquire a token for the app by calling AcquireTokenForClient, passing the scope, and optionally forcing a refresh of the token.

Scopes to request

The scope to request for a client credential flow is the name of the resource followed by /.default. This notation tells Microsoft Entra ID to use the application-level permissions declared statically during application registration. Also, these API permissions must be granted by a tenant administrator.

Here's an example of defining the scopes for the web API as part of the configuration in an appsettings.json file. This example is taken from the .NET console daemon code sample on GitHub.

{
    "AzureAd": {
        // Same AzureAd section as before.
    },

    "MyWebApi": {
        "BaseUrl": "https://localhost:44372/",
        "RelativePath": "api/TodoList",
        "RequestAppToken": true,
        "Scopes": [ "[Enter here the scopes for your web API]" ]
    }
}

Azure AD (v1.0) resources

The scope used for client credentials should always be the resource ID followed by /.default.

Important

When MSAL requests an access token for a resource that accepts a version 1.0 access token, Microsoft Entra ID parses the desired audience from the requested scope by taking everything before the last slash and using it as the resource identifier. So if, like Azure SQL Database (https://database.windows.net), the resource expects an audience that ends with a slash (for Azure SQL Database, https://database.windows.net/), you'll need to request a scope of https://database.windows.net//.default. (Note the double slash.) See also MSAL.NET issue #747: Resource url's trailing slash is omitted, which caused sql auth failure.

AcquireTokenForClient API

To acquire a token for the app, use AcquireTokenForClient or its equivalent, depending on the platform.

With Microsoft.Identity.Web, you don't need to acquire a token. You can use higher level APIs, as you see in Calling a web API from a daemon application. If however you're using an SDK that requires a token, the following code snippet shows how to get this token.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Web;

// In the Program.cs, acquire a token for your downstream API

var tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
ITokenAcquirer acquirer = tokenAcquirerFactory.GetTokenAcquirer();
AcquireTokenResult tokenResult = await acquirer.GetTokenForUserAsync(new[] { "https://graph.microsoft.com/.default" });
string accessToken = tokenResult.AccessToken;

Protocol

If you don't yet have a library for your chosen language, you might want to use the protocol directly:

First case: Access the token request by using a shared secret

POST /{tenant}/oauth2/v2.0/token HTTP/1.1           //Line breaks for clarity.
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

Second case: Access the token request by using a certificate

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

scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&client_id=97e0a5b7-d745-40b6-94fe-5f77d35c6e05
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsIng1dCI6Imd4OHRHeXN5amNScUtqRlBuZDdSRnd2d1pJMCJ9.eyJ{a lot of characters here}M8U3bSUKKJDEg
&grant_type=client_credentials

For more information, see the protocol documentation: Microsoft identity platform and the OAuth 2.0 client credentials flow.

Troubleshooting

Did you use the resource/.default scope?

If you get an error message telling you that you used an invalid scope, you probably didn't use the resource/.default scope.

If you get an Insufficient privileges to complete the operation error when you call the API, the tenant administrator needs to grant permissions to the application. For guidance on how to grant admin consent for your application, see step 4 in Quickstart: Acquire a token and call Microsoft Graph in a .NET console app.

If you don't grant admin consent to your application, you'll run into the following error:

Failed to call the web API: Forbidden
Content: {
  "error": {
    "code": "Authorization_RequestDenied",
    "message": "Insufficient privileges to complete the operation.",
    "innerError": {
      "request-id": "<guid>",
      "date": "<date>"
    }
  }
}

Are you calling your own API?

If your daemon app calls your own web API and you weren't able to add an app permission to the daemon's app registration, you need to Add app roles to the web API's app registration.

Next steps

Move on to the next article in this scenario, Calling a web API.