Closed DanielMannInCycle closed 4 years ago
thanks @DanielMannInCycle
I'm still working on paring things down for a MVCE, but I'm about 90% sure I've identified the scenario that triggers this:
We have a DelegatingHandler
middleware that is responsible for constructing an HttpClient
with the Bearer token already already applied:
public class BearerTokenRequestHandler : DelegatingHandler
{
private readonly ITokenService _tokenService;
private readonly ClientSettings _clientSettings;
public BearerTokenRequestHandler(ITokenService tokenService, ClientSettings clientSettings)
{
_tokenService = tokenService;
_clientSettings = clientSettings;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var token = await _tokenService.AcquireTokenAsync(_clientSettings.EngagementsFeatureServiceSettings.Scopes?.Split(','));
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
The NullReferenceException
happens when that middleware runs. Take this snippet as an example:
protected override async Task OnInitializedAsync()
{
var token = tokensvc.AcquireTokenAsync(new[] { "https://[redacted].onmicrosoft.com/[also-redacted]/[super-duper-redacted]" });
var x = await EngagementsFeatureService.ApiCustomersGetAsync();
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
The call to AcquireTokenAsync
works fine. The call to ApiCustomersGetAsync
(which is just Swagger-generated boilerplate to make an HTTP GET call to the downstream API) calls the BearerTokenRequestHandler
, which dies with the NRE.
I haven't done a side-by-side comparison to figure out what's falling through in the Identity.Web code that shouldn't be, but I plan on doing so tomorrow.
Thanks for the update, @DanielMannInCycle
I have put the repro code into a private repo. I have added both @jennyf19 and @jmprieur to the repo.
@DanielMannInCycle I checked with ASP .NET Core team and here is their guidance for this: "The HttpContext is not "fully" available when the user uses the SignalR service. That's one of the reasons why our guidance on Blazor is to completely avoid its usage. SignalR Service will try to "mock" some of the properties of the HTTP Context, but in general it is not going to work well. Our guidance to users is to capture the HttpContext data explicitly and pass it to the Blazor application as parameters to the root component."
Here is a sample that might help: https://github.com/javiercn/blazor-server-aad-sample/blob/master/BlazorServerAuthWithAzureActiveDirectory/Pages/_Host.cshtml#L23-L33 and https://github.com/javiercn/DetectServerSideBlazor/blob/master/BlazorApp1/Startup.cs#L109-L113
cc: @jmprieur
@jennyf19 Thanks for the information. However, I'm a bit confused. My understanding is that an external SignalR hub is required for scaling a Blazor server-side app -- please correct me if I'm wrong. If there's nothing that can be done to fix this library to work for a Blazor server-side app backed by external SignalR, then wouldn't it be appropriate to say that this library is not compatible with Blazor Server-Side?
@jennyf19 @jmprieur this is exactly this, what I wrote you about the Blazor SignalR problem.
@DanielMannInCycle I used the repo you sent, and had to make a few changes to get it working, but I'm not able to repro your issue now. I'm going to have @javiercn take a look as well.
In Startup.cs, I commented this:
//_authenticationStrategy.AddAuthentication(services, Configuration);
and added this, as you'll need to add Microsoft Identity Web explicitly:
string[] scopes = Configuration.GetValue<string>("CalledApi:CalledApiScopes")?.Split(' ');
services.AddMicrosoftWebAppAuthentication(Configuration, "AzureAd")
.AddMicrosoftWebAppCallsWebApi(Configuration, scopes, "AzureAd")
Then i had to connect the Microsoft Identity Web UI, so we can pick up the challenge (which I saw is getting returned correctly).
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
endpoints.MapControllers(); //added this for our UI and the challenge
});
in FetchData, i had to add a try/catch and then process the challenge:
protected override async Task OnInitializedAsync()
{
try
{
var token = await tokensvc.AcquireTokenAsync(Program.Scopes);
var x = await EngagementsFeatureService.ApiCustomersGetAsync();
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
catch( Exception ex)
{
ConsentHandler.HandleException(ex);
}
}
The NavigationManager is not null. We are actively looking at this. Will keep you posted.
@DanielMannInCycle any update on this? Make sure you also delete your local nuget cache to make sure it's picking up the right build... @schmid37 i think this is working for you now, correct? any advice?
@jennyf19 works fine. Daniel, Javier and me verified that in the meeting.
@schmid37 awesome! so this issue can close once we release w/the changes from Javier?
@jennyf19 think so. You're about to release the 0.2.3 this week?
@jennyf19 I'm still trying to get it to work -- I'm still seeing a null NavigationManager
.
@schmid37 we are testing the release now, would be 0.2.2-preview, so probably available tomorrow, Monday at the latest.
@DanielMannInCycle did you delete the local nuget cache, to make sure you're picking up the build w/the fix? [typically on Windows under \users\you.nuget\Packages]
@jennyf19 I cloned the repo and added project references to ensure I'm picking up the latest changes. I cannot get the changes you made to my repro application to work properly. The line you commented out is necessary -- we use a strategy pattern to determine whether to use AzureAD or B2C.
If I change the AzureB2CAuthenticationStrategy.cs
file to include this snippet:
.AddMicrosoftWebAppCallsWebApi(configuration, initialScopes, "AzureAdB2CSettings")
.AddInMemoryTokenCaches();
I still see a null NavigationManager
. I'm not sure what the difference between the two methods of service registration are, so I'm at a loss with how to implement your changes properly within the context of the strategy pattern.
I've also seen this exception pop up in some situations, not sure why:
InvalidOperationException: No authentication handler is registered for the scheme 'OpenIdConnect'. The registered schemes are: Cookies, AzureADB2COpenID. Did you forget to call AddAuthentication().Add[SomeAuthHandler]("OpenIdConnect",...)?
@DanielMannInCycle can you send me an email - jeferrie@microsoft.com ? thanks. :)
Included in 0.2.2-preview release
Which version of Microsoft Identity Web are you using? Note that to get help, you need to run the latest version.
0.2.1-preview
Where is the issue?
Is this a new or an existing app?
This is an existing application. I upgraded to the latest version of the Microsoft Identity Web library. It works with self-hosted SignalR, but fails when integrated with Azure SignalR, presumably due to missing
HttpContext
.Repro I don't have a minimal repro. I am currently attempting to put one together; I will update this issue with a link to a repo if I'm successful.
Expected behavior Azure SignalR works.
Actual behavior NullReferenceException is thrown
Additional context / logs / screenshots We are registering the identity provider as follows: Startup.cs:
The authentication strategies being registered are AzureAD and AzureADB2C. They are basically the same implementation:
Here is the code for some of the methods that feature in the stack trace of the exception:
When running, the initial token retrieval appears to be successful but fails when retrieving the token for usage by a down-stream API: