-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #99 from DuendeSoftware/joe/principal-accessor
Add accessor for current principal
- Loading branch information
Showing
10 changed files
with
223 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,12 @@ | ||
@inject NavigationManager Navigation | ||
|
||
@code { | ||
protected override void OnInitialized() | ||
// Using the async method prevents NavigationExceptions, even though this method is synchronous | ||
#pragma warning disable CS1998 | ||
protected override async Task OnInitializedAsync() | ||
{ | ||
var returnUrl = Uri.EscapeDataString("/" + Navigation.ToBaseRelativePath(Navigation.Uri)); | ||
Navigation.NavigateTo($"account/login?returnUrl={returnUrl}", forceLoad: true); | ||
} | ||
#pragma warning restore CS1998 | ||
} |
55 changes: 55 additions & 0 deletions
55
src/Duende.AccessTokenManagement.OpenIdConnect/BlazorServerPrincipalAccessor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. | ||
|
||
using Microsoft.AspNetCore.Components.Authorization; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Logging; | ||
using System.Security.Claims; | ||
using System.Threading.Tasks; | ||
|
||
namespace Duende.AccessTokenManagement.OpenIdConnect; | ||
|
||
/// <summary> | ||
/// Accesses the current user from blazor server. | ||
/// </summary> | ||
/// <remarks> | ||
/// ctor | ||
/// </remarks> | ||
public class BlazorServerUserAccessor( | ||
// We use the CircuitServicesAccessor to resolve the | ||
// AuthenticationStateProvider, rather than injecting it. Injecting the | ||
// state provider directly doesn't work here, because this service might be | ||
// called in a non-blazor DI scope. | ||
CircuitServicesAccessor circuitServicesAccessor, | ||
IHttpContextAccessor? httpContextAccessor, | ||
ILogger<BlazorServerUserAccessor> logger) : IUserAccessor | ||
{ | ||
|
||
/// <inheritdoc/> | ||
public async Task<ClaimsPrincipal> GetCurrentUserAsync() | ||
{ | ||
var authStateProvider = circuitServicesAccessor.Services? | ||
.GetService<AuthenticationStateProvider>(); | ||
// If we are in blazor server (streaming over a circuit), this provider will be non-null | ||
if (authStateProvider != null) | ||
{ | ||
var authState = await authStateProvider.GetAuthenticationStateAsync(); | ||
return authState.User; | ||
} | ||
// Otherwise, we should be in an SSR scenario, and the httpContext should be available | ||
else if(httpContextAccessor?.HttpContext != null) | ||
{ | ||
return httpContextAccessor.HttpContext.User; | ||
} | ||
// If we are in neither blazor server or SSR, something weird is going on. | ||
else | ||
{ | ||
logger.LogWarning("Neither an authentication state provider or http context are available to obtain the current principal."); | ||
return new ClaimsPrincipal(); | ||
} | ||
} | ||
|
||
} | ||
|
||
|
64 changes: 64 additions & 0 deletions
64
src/Duende.AccessTokenManagement.OpenIdConnect/CircuitServicesAccessor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. | ||
|
||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Components.Server.Circuits; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Duende.AccessTokenManagement.OpenIdConnect; | ||
|
||
// This code is from the blazor documentation: | ||
// https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/dependency-injection?view=aspnetcore-8.0#access-server-side-blazor-services-from-a-different-di-scope | ||
|
||
/// <summary> | ||
/// Provides access to scoped blazor services from non-blazor DI scopes, such as | ||
/// scopes created using IHttpClientFactory. | ||
/// </summary> | ||
public class CircuitServicesAccessor | ||
{ | ||
static readonly AsyncLocal<IServiceProvider> blazorServices = new(); | ||
|
||
internal IServiceProvider? Services | ||
{ | ||
get => blazorServices.Value; | ||
set => blazorServices.Value = value!; | ||
} | ||
} | ||
|
||
internal class ServicesAccessorCircuitHandler : CircuitHandler | ||
{ | ||
readonly IServiceProvider services; | ||
readonly CircuitServicesAccessor circuitServicesAccessor; | ||
|
||
internal ServicesAccessorCircuitHandler(IServiceProvider services, | ||
CircuitServicesAccessor servicesAccessor) | ||
{ | ||
this.services = services; | ||
this.circuitServicesAccessor = servicesAccessor; | ||
} | ||
|
||
public override Func<CircuitInboundActivityContext, Task> CreateInboundActivityHandler( | ||
Func<CircuitInboundActivityContext, Task> next) | ||
{ | ||
return async context => | ||
{ | ||
circuitServicesAccessor.Services = services; | ||
await next(context); | ||
circuitServicesAccessor.Services = null; | ||
}; | ||
} | ||
} | ||
|
||
internal static class CircuitServicesServiceCollectionExtensions | ||
{ | ||
public static IServiceCollection AddCircuitServicesAccessor( | ||
this IServiceCollection services) | ||
{ | ||
services.AddScoped<CircuitServicesAccessor>(); | ||
services.AddScoped<CircuitHandler, ServicesAccessorCircuitHandler>(); | ||
|
||
return services; | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
src/Duende.AccessTokenManagement.OpenIdConnect/HttpContextPrincipalAccessor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. | ||
|
||
using Microsoft.AspNetCore.Http; | ||
using System.Security.Claims; | ||
using System.Threading.Tasks; | ||
|
||
namespace Duende.AccessTokenManagement.OpenIdConnect; | ||
|
||
/// <summary> | ||
/// Accesses the current principal based on the HttpContext.User. | ||
/// </summary> | ||
public class HttpContextUserAccessor : IUserAccessor | ||
{ | ||
private readonly IHttpContextAccessor _httpContextAccessor; | ||
|
||
/// <summary> | ||
/// ctor | ||
/// </summary> | ||
public HttpContextUserAccessor(IHttpContextAccessor httpContextAccessor) | ||
{ | ||
_httpContextAccessor = httpContextAccessor; | ||
} | ||
|
||
/// <inheritdoc/> | ||
public Task<ClaimsPrincipal> GetCurrentUserAsync() | ||
{ | ||
return Task.FromResult(_httpContextAccessor.HttpContext?.User ?? new ClaimsPrincipal()); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
src/Duende.AccessTokenManagement.OpenIdConnect/IPrincipalAccessor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. | ||
|
||
using System.Security.Claims; | ||
using System.Threading.Tasks; | ||
|
||
namespace Duende.AccessTokenManagement.OpenIdConnect; | ||
|
||
/// <summary> | ||
/// Service that retrieves the current principal. | ||
/// </summary> | ||
public interface IUserAccessor | ||
{ | ||
/// <summary> | ||
/// Gets the current user. | ||
/// </summary> | ||
Task<ClaimsPrincipal> GetCurrentUserAsync(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.