Repository navigation
[Bug] Token Acquisition Fails Without HttpContext #273
Description
Which Version of Microsoft Identity Web are you using ?
0.1.5-preview
Where is the issue?
- Web App
- Sign-in users
- Sign-in users and call web APIs
- Web API
- Protected web APIs (Validating tokens)
- Protected web APIs (Validating scopes)
- Protected web APIs call downstream web APIs
- Token cache serialization
- In Memory caches
- Session caches
- Distributed caches
[X] Other:
When token acquisition methods are invoked on the ITokenAcquisition supplied by DI outside of a valid HttpContext, these methods cause an exception. This is because the TokenAcquisition implementation makes reference to the properties of HttpContext without providing adequate null coalescence. While the package was likely designed to be used in the context of a valid HttpContext, there are scenarios in which the context will be null. The most notable example are Azure App Services WebJobs without Http Triggers. More specifically, consider a Web Job running on a Timer Trigger to perform an operation that requires AAD authentication, such as connecting to a Kusto cluster via AAD auth. In this context, when a method such as GetAccessTokenForAppAsync is invoked, it will trigger a NullReferenceException.
Is this a new or existing app?
This is a new app or experiment
Repro
- Create an Azure WebJob v3 C# .NET Core app with a TimerTrigger function.
- Add the appropriate
AzureAdkeys into the config, such as via appsettings.json. - Create a standard ConfigureServices function with the following contents:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// MSAL based authentication initiation
services.AddSignIn(Configuration);
// Token acquisition service based on MSAL.NET
services.AddWebAppCallsProtectedWebApi(Configuration, new string[] { "YOUR_SCOPE/.default" })
.AddInMemoryTokenCaches();
// ...
}- Attempt to retrieve an access token for the application:
string token = tokenAcquisition.GetAccessTokenForAppAsync(new string[] { "YOUR_SCOPE/.default" }).Result;Expected behavior
Retrieve an access token.
Actual behavior
Null Reference Exception
Possible Solution
Add null coalescence logic to src/Microsoft.Identity.Web/TokenAcquisition.cs:BuildConfidentialClientApplicationAsync. This could be accomplished by allowing a redirect uri to be passed into the function via any means other than extraction from HttpContext.
Additional context/ Logs / Screenshots
Temporary workaround is to manually construct an HttpContext before token acquisition:
if (httpContextAccessor.HttpContext is null)
{
httpContextAccessor.HttpContext = new DefaultHttpContext();
httpContextAccessor.HttpContext.Request.Scheme = "https";
httpContextAccessor.HttpContext.Request.Host = new HostString("localhost");
}
string token = tokenAcquisition.GetAccessTokenForAppAsync(new string[] { "YOUR_SCOPE/.default" }).Result;This temporary solution is a hack and should be avoided to prevent future breakage when package internals are updated.
Note: This is a first-party Microsoft issue. Please feel free to lookup my alias and ping me on teams for an internal example.
Activity
jmprieur commentedon Jun 30, 2020
Thanks for reporting Nicholas Boucher (@nickboucher)
In your scenario, how would Microsoft.Identity.Web know the user if there is no HttpContext when it's called by a web hook?
nickboucher commentedon Jul 1, 2020
Hi Jean-Marc Prieur (@jmprieur) -- thanks for the quick response. The app wouldn't know who the user is, but in this scenario there is no user. I'm specifically interested in the
GetAccessTokenForAppAsyncmethod, which acquires an access token on the behalf of the application (not a specific user).jmprieur commentedon Jul 1, 2020
Oh, sorry, Nicholas Boucher (@nickboucher), I thought I had read GetAccessTokenForUser, yesterday.
jennyf19 commentedon Jul 13, 2020
Included in 0.2.0-preview release