Reputation: 2243
I'm working on figuring out how to use Microsoft Graph API in a ASP.NET Core 3.1 Razor Pages application. I found this guide and got most of it to work (along with retrieving a token) until I realized I need to get access to the API without a user.
At the moment, I am stuck because I am not able to retrieve a token using the ITokenAcquisition
GetAccessTokenForAppAsync
method. It keeps resulting in a NullReferenceException
. I don't know if my startup setup is wrong or what, but I can't figure it out.
System.NullReferenceException: 'Object reference not set to an instance of an object.'
I'm aware of the Get access without a user guide which I understand and can get to work, but I specifically want to use GetAccessTokenForAppAsync
method because it will manage refreshing tokens for me. Otherwise, I'd have to keep querying for a new token with every API call and constantly generating valid tokens seems like a bad idea.
Startup.cs ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpClient();
services.AddMicrosoftIdentityWebApiAuthentication(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
// Use in-memory token cache
// See https://github.com/AzureAD/microsoft-identity-web/wiki/token-cache-serialization
.AddInMemoryTokenCaches();
...
}
Index.cshtml.cs. This is where I make my call to get the token:
public class IndexModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
private readonly ITokenAcquisition _tokenAcquisition;
public IndexModel(IHttpClientFactory clientFactory, ITokenAcquisition tokenAcquisition)
{
_clientFactory = clientFactory;
_tokenAcquisition = tokenAcquisition;
}
public async Task OnGet()
{
// results in NullReferenceException
string token = await _tokenAcquisition.GetAccessTokenForAppAsync("https://graph.microsoft.com/.default", tenant:"tenantId");
}
}
appSettings.json. The values are populated by user secrets json.
{
"AzureAd": {
"Instance": "",
"ClientId": "",
"TenantId": "",
"CallbackPath": "",
"ClientSecret": "",
"TimeoutInMinutes": ""
},
"ConnectionStrings": {
"DefaultConnection": ""
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
Upvotes: 0
Views: 7714
Reputation: 22495
First of all, I have successfully reproduced your issue, as you can see below:
You are getting this because of private readonly ITokenAcquisition _tokenAcquisition;
Note: This is actually a service which helps you to acquire access token on behalf of application. You cannot consume this service as constructor variable.
Solution:
Instead of that you should use ITokenAcquisition
service in the below way:
public async Task OnGet()
{
var _tokenAcquisition = this.HttpContext.RequestServices
.GetRequiredService<ITokenAcquisition>() as ITokenAcquisition;
string token = await _tokenAcquisition.GetAccessTokenForAppAsync("https://graph.microsoft.com/.default", tenant: "tenantId");
}
Configuration Under Startup.cs
public void ConfigureServices(IServiceCollection services)
{
string[] initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.AddRazorPages()
.AddMicrosoftIdentityUI();
}
DownstreamApi Under appsettings.json:
"DownstreamApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "https://graph.microsoft.com/.default"
}
Output:
Note: Make sure, you have included app.UseAuthentication();
in program.cs or startup.cs file in order to get expected result.
Upvotes: 0