My project was created by dotnet cli
dotnet new blazorwasm --hosted
My project structure is
I add PingOne Oauth provider which has client_secret/client_id and other configurations...
Server folder: Program.cs
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
ConfigureAuthentication(builder.Services);
void ConfigureAuthentication(IServiceCollection serviceCollection)
{
// serviceCollection
serviceCollection.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = "PingOne";
}).AddCookie().AddOAuth("PingOne", options =>
{
options.AuthorizationEndpoint = "https://xxx.xxx.xxx/xxx/xxx/authorization.oauth2";
options.CallbackPath = "/callback";
options.ClientId = "xxx";
options.ClientSecret = "xxx";
options.TokenEndpoint = "https://xxx.xxx.xxx/xxx/xx/token.oauth2";
options.UserInformationEndpoint = "https://xxx.xx.xx/xx/xxx/userinfo.openid";
options.SaveTokens = true;
options.Events = new OAuthEvents
{
OnCreatingTicket = async context =>
{
var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
var response = await context.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, context.HttpContext.RequestAborted);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
var responseText = await response.Content.ReadAsStringAsync();
var user = JsonDocument.Parse(responseText);
context.RunClaimActions(user.RootElement);
}
};
});
}
Client folder: Program.cs
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using BlazorOAuth.Client;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();
Client folder: _Imports.razor:
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using BlazorOAuth.Client
@using BlazorOAuth.Client.Shared
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@attribute [Authorize]
At Counter.razor:
@page "/counter"
@attribute [Authorize]
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount ;
}
}
I don't know how to auto jump to PingOne or other oauth provider's auth page. blazor client doesn't auto jump to the auth page cause i didn't configure something.Also I hava an WeatherForecastController with Authorize attribute. Client side send a request to api,but also didn't jump to oauth authorize page. I thought I config services.AddOuth() it would do it.
I capture the request. I found it redirect to the authorization end point url. but the response was html code . I didn't know how to display on the screen
Please help me or paste a tutorial whatever. really appreciate it.
'<' is an invalid start of a value.
this wrong reason is: the workflow is to request the weatherforecast service with authorize attribute.but it haven't be authorized yet. so service went to authorize endpoint.then oauth provider which is PingOne identity.the authorize provider response a bunch of html code. i don't know how to display on the screen or redirect to the endpoint url.in that case the client can input password to authorized and request the weather service
CodePudding user response:
When using Blazor WebAssembly (or any spa framework e.g. angular) it's best practice implement openid connect authentication with authorization-code flow.
Your client should redirect to the oauth provider and after user logs in your client obtains an access token from the oauth provider. Client sends this access token to api requests to your server.
Example Client Program.cs:
// BaseAddressAuthorizationMessageHandler attaches access token to outgoing requests
builder.Services
.AddHttpClient("ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("ServerAPI"));
builder.Services
.AddOidcAuthentication(options =>
{
options.ProviderOptions.Authority = "https://xxx";
options.ProviderOptions.ClientId = "xxx";
options.ProviderOptions.ResponseType = "code";
options.ProviderOptions.DefaultScopes.Add("scope1");
options.ProviderOptions.DefaultScopes.Add("scope2");
});
Your server then needs a way to validate this access token by asking the oauth provider. Example server configuration:
services
.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://xxx";
});
This is the documentation on how to login to your oidc provider and obtain the access token: https://docs.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/standalone-with-authentication-library?view=aspnetcore-6.0&tabs=visual-studio
This is how to attach access token to api requests to your server: https://docs.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-6.0#attach-tokens-to-outgoing-requests