【问题标题】:In WASM Blazor using Azure Active Directory, how do I bypass Auth during development在使用 Azure Active Directory 的 WASM Blazor 中,如何在开发过程中绕过 Auth
【发布时间】:2021-02-05 12:48:02
【问题描述】:

Microsoft 在他们的演练中很好地介绍了针对 Azure Active Directory 对 WASM Blazor 进行身份验证。他们没有涵盖的是之后的开发工作流程。作为一个已编译的应用程序,对 UI 的每一次更改都是一个痛苦的停止-重新编译-启动过程,然后再加上 AAD 登录过程。 我们如何在开发过程中简化这一过程并设置一组伪造的凭据?

【问题讨论】:

    标签: azure-active-directory blazor


    【解决方案1】:

    目前这种方法对我有用,但我很想看看其他人会怎么做。请注意,这主要用于开发,但我可以考虑扩展它以进行集成测试(这是我列表中的下一个)。

    在客户端,让自己成为一个假的 AuthenticationStateProvider 来代替你通常使用的远程身份验证。

    using System;
    using System.Security.Claims;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Components.Authorization;
    using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
    
    namespace Blah.Client
    {
        public class FakeAuthStateProvider : AuthenticationStateProvider, IAccessTokenProvider
        {
            public override Task<AuthenticationState> GetAuthenticationStateAsync()
            {
                var identity = new ClaimsIdentity(new[]
                {
                    new Claim(ClaimTypes.Name, ">> TEST USER <<"),
                    new Claim("directoryGroup","abc4567-890-1234-abcd-1234567890abc") //Should match your group you use to determine a policy
                }, "Fake authentication type");
    
    
                var user = new ClaimsPrincipal(identity);
    
    
    
                return Task.FromResult(new AuthenticationState(user));
            }
    
            public async ValueTask<AccessTokenResult> RequestAccessToken()
            {
                return new AccessTokenResult(AccessTokenResultStatus.Success, new AccessToken() { Expires = DateTime.Now + new TimeSpan(365,0,0,0) }, "");
            }
    
            public async ValueTask<AccessTokenResult> RequestAccessToken(AccessTokenRequestOptions options)
            {
                return new AccessTokenResult(AccessTokenResultStatus.Success, new AccessToken() { Expires = DateTime.Now + new TimeSpan(365, 0, 0, 0) }, "");
            }
        }
    }
    

    在客户端program.cs中,debug时切换auth:

    #if DEBUG
                SetupFakeAuth(builder.Services);
    #else
                builder.Services.AddMsalAuthentication<RemoteAuthenticationState, CustomUserAccount>(options =>
                {
                    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
                    options.ProviderOptions.DefaultAccessTokenScopes.Add("api://1234567-890-1234-abcd-1234567890abc/API.Access");
                })
                    .AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount, CustomAccountFactory>();
    #endif
    .....
            private static void SetupFakeAuth(IServiceCollection services)
            {
                    //https://github.com/dotnet/aspnetcore/blob/c925f99cddac0df90ed0bc4a07ecda6b054a0b02/src/Components/WebAssembly/WebAssembly.Authentication/src/WebAssemblyAuthenticationServiceCollectionExtensions.cs#L28
    
                services.AddOptions();
                services.AddAuthorizationCore();
                services.TryAddScoped<AuthenticationStateProvider, FakeAuthStateProvider>();
    
    
                services.TryAddTransient<BaseAddressAuthorizationMessageHandler>();
                services.TryAddTransient<AuthorizationMessageHandler>();
    
                services.TryAddScoped(sp =>
                {
                    return (IAccessTokenProvider)sp.GetRequiredService<AuthenticationStateProvider>();
                });
    
                services.TryAddScoped<IAccessTokenProviderAccessor, FakeAccessTokenProviderAccessor>();
                services.TryAddScoped<SignOutSessionStateManager>();           
            }
    

    ... 并定义 FakeAuthState 提供者,它只是内部类微软注册的一个副本:

    // Copyright (c) .NET Foundation. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
    
    using System;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal
    {
        internal class FakeAccessTokenProviderAccessor : IAccessTokenProviderAccessor
        {
            private readonly IServiceProvider _provider;
            private IAccessTokenProvider _tokenProvider;
    
            public FakeAccessTokenProviderAccessor(IServiceProvider provider) => _provider = provider;
    
            public IAccessTokenProvider TokenProvider => _tokenProvider ??= _provider.GetRequiredService<IAccessTokenProvider>();
        }
    }
    

    这应该会导致客户端上的登录用户像往常一样具有名称和范围。

    服务器端:

    在 Startup.cs 中

        #if DEBUG            
            services.AddSingleton<IPolicyEvaluator, FakePolicyEvaluator>();
        #else                        
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
        
        #endif
    

    还有一个新课程:

    using System.Security.Claims;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Authorization.Policy;
    using Microsoft.AspNetCore.Http;
    
    namespace Blah.Server
    {
        public class FakePolicyEvaluator : IPolicyEvaluator
        {
            public virtual async Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
            {
                const string testScheme = "FakeScheme";
                var principal = new ClaimsPrincipal();
                principal.AddIdentity(new ClaimsIdentity(new[] {
                    new Claim("Permission", "CanViewPage"),
                    new Claim("Manager", "yes"),
                    new Claim(ClaimTypes.Role, "Administrator"),
                    new Claim(ClaimTypes.NameIdentifier, "John")
                }, testScheme));
                return await Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal,
                    new AuthenticationProperties(), testScheme)));
            }
    
            public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy,
                AuthenticateResult authenticationResult, HttpContext context, object resource)
            {
                return await Task.FromResult(PolicyAuthorizationResult.Success());
            }
        }
    }
    

    希望对某人有所帮助。我现在将寻求改进它并使其在测试场景中工作。

    【讨论】:

    • 这很棒。在服务器上 AuthenticateAsync 我认为您需要将 ClaimsPrincipal 分配给 context.User 否则用户在服务器端似乎是空白的。我还将 ClaimsPrincipal 创建移动到共享代码,而不是客户端和服务器上的类似代码。
    猜你喜欢
    • 2021-12-07
    • 2022-11-17
    • 2020-12-27
    • 2021-09-22
    • 1970-01-01
    • 2023-03-26
    • 2021-12-21
    • 1970-01-01
    • 2021-06-02
    相关资源
    最近更新 更多