C# 带AzureAD的Blazor WebAssembly

C# 带AzureAD的Blazor WebAssembly,c#,asp.net-core,cors,azure-active-directory,blazor-client-side,C#,Asp.net Core,Cors,Azure Active Directory,Blazor Client Side,我需要让我正在开发的WebAssembly应用程序的用户通过AzureAD登录。 我正在使用repo(和UI) 以下是我迄今为止所做的: appsetting.json "AzureAd": { "Instance": "https://login.microsoftonline.com", "Domain": "xxx", "TenantId": "xxx", "ClientId": "xxx", "CallbackPath": "/signin-oi

我需要让我正在开发的WebAssembly应用程序的用户通过AzureAD登录。 我正在使用repo(和UI)

以下是我迄今为止所做的:

appsetting.json

 "AzureAd": {
    "Instance": "https://login.microsoftonline.com",
    "Domain": "xxx",
    "TenantId": "xxx",
    "ClientId": "xxx",
    "CallbackPath": "/signin-oidc",
    "SignedOutCallbackPath ": "/signout-callback-oidc",

    "ClientSecret": "xxx"
  },
服务器端启动.cs

public class Startup
{
    public IConfiguration Configuration { get; }
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        services.AddResponseCompression(opts =>
        {
            opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
                new[] { "application/octet-stream" });
        });

        // Sign-in users with the Microsoft identity platform
        //services.AddSignIn(Configuration);
        services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
                .AddSignIn("AzureAD", Configuration, options => Configuration.Bind("AzureAD", options));

        // Looks like I need this to have the login UI
        services.AddControllersWithViews(options =>
        {
            var policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
            options.Filters.Add(new AuthorizeFilter(policy));
        }).AddMicrosoftIdentityUI();
        services.AddRazorPages();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseResponseCompression();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBlazorDebugging();
        }

        app.UseCors();

        app.UseStaticFiles();
        app.UseClientSideBlazorFiles<Client.Program>();

        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
            endpoints.MapFallbackToClientSideBlazor<Client.Program>("index.html");
        });
    }
}
客户端程序.cs

public class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);

        // Use our CustomAuthenticationProvider as the 
        // AuthenticationStateProvider
        builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationProvider>();

        // Add Authentication support
        builder.Services.AddOptions();
        builder.Services.AddAuthorizationCore();

        builder.RootComponents.Add<App>("app");
        await builder.Build().RunAsync();
    }
}

public class CustomAuthenticationProvider : AuthenticationStateProvider
{
    private readonly HttpClient _httpClient;
    public CustomAuthenticationProvider(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        ClaimsPrincipal user;

        // Call the GetUser method to get the status
        // This only sets things like the AuthorizeView
        // and the AuthenticationState CascadingParameter
        var result = await _httpClient.GetJsonAsync<AppUser>("api/user");

        // Was a UserName returned?
        if (result.UserName != "")
        {
            // Create a ClaimsPrincipal for the user
            var identity = new ClaimsIdentity(new[]
            {
               new Claim(ClaimTypes.Name, result.UserName),
            }, "AzureAdAuth");
            user = new ClaimsPrincipal(identity);
        }
        else
        {
            user = new ClaimsPrincipal(); // Not logged in
        }
        return await Task.FromResult(new AuthenticationState(user));
    }
}
我正试图在服务器端启用CORS,正如前面所解释的,但还没有成功。 我错过了什么?

Blazor WebAssembly的预览版2附带了一个现成的工作模板。


Blazor WebAssembly的预览版2附带了一个现成的工作模板。

看起来您的API正在将API调用重定向到AAD。您应该使用MSAL在前端实现身份验证。而且你的API需要支持JWTs和交互式身份验证。因此,如果我理解正确,我应该在Blazor上使用MSAL将身份验证从服务器端移动到客户端。是的,这是JS前端的典型方法:)Blazor在这方面没有什么不同,它是一个富有意义的前端应用程序,我对此感到困惑:看起来您的API正在将API调用重定向到AAD。您应该使用MSAL在前端实现身份验证。而且你的API需要支持JWTs和交互式身份验证。因此,如果我理解正确,我应该在Blazor上使用MSAL将身份验证从服务器端移动到客户端。是的,这是JS前端的典型方法:)Blazor在这方面没有什么不同,它是一个富有意义的前端应用程序,我对此感到困惑:
public class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);

        // Use our CustomAuthenticationProvider as the 
        // AuthenticationStateProvider
        builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationProvider>();

        // Add Authentication support
        builder.Services.AddOptions();
        builder.Services.AddAuthorizationCore();

        builder.RootComponents.Add<App>("app");
        await builder.Build().RunAsync();
    }
}

public class CustomAuthenticationProvider : AuthenticationStateProvider
{
    private readonly HttpClient _httpClient;
    public CustomAuthenticationProvider(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        ClaimsPrincipal user;

        // Call the GetUser method to get the status
        // This only sets things like the AuthorizeView
        // and the AuthenticationState CascadingParameter
        var result = await _httpClient.GetJsonAsync<AppUser>("api/user");

        // Was a UserName returned?
        if (result.UserName != "")
        {
            // Create a ClaimsPrincipal for the user
            var identity = new ClaimsIdentity(new[]
            {
               new Claim(ClaimTypes.Name, result.UserName),
            }, "AzureAdAuth");
            user = new ClaimsPrincipal(identity);
        }
        else
        {
            user = new ClaimsPrincipal(); // Not logged in
        }
        return await Task.FromResult(new AuthenticationState(user));
    }
}
Access to fetch at 'https://login.microsoftonline.com/xxx/oauth2/v2.0/authorize?client_id=xxx&redirect_uri=https%3A%2F%2Flocalhost%3A44316%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile%20offline_access%xxx%2Fuser_impersonation&response_mode=form_post&nonce=xxx&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=5.5.0.0' (redirected from 'https://localhost:44316/api/user') from origin 'https://localhost:44316' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.