Skip to content

Commit a8f12a1

Browse files
committed
Rework auth
1 parent eaf46e9 commit a8f12a1

86 files changed

Lines changed: 649 additions & 2257 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

NetEvent/Client/Models/User.cs

Lines changed: 0 additions & 23 deletions
This file was deleted.

NetEvent/Client/NetEvent.Client.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,4 @@
2828
<ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />
2929
</ItemGroup>
3030

31-
<ItemGroup>
32-
<Folder Include="Models\" />
33-
</ItemGroup>
34-
3531
</Project>

NetEvent/Client/Pages/Authentication.razor

Lines changed: 0 additions & 7 deletions
This file was deleted.

NetEvent/Client/Pages/Login.razor

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@page "/login"
2+
3+
<h3>Login</h3>
4+
5+
<EditForm Model="LoginRequest" OnValidSubmit="ExecuteLogin">
6+
<DataAnnotationsValidator />
7+
<MudCard >
8+
<MudCardContent>
9+
<MudTextField id="Input.Email" Label="Email" @bind-Value="LoginRequest.UserName" For="@(() => LoginRequest.UserName)" />
10+
<MudTextField id="Input.Password" Label="Password" @bind-Value="LoginRequest.Password" For="@(() => LoginRequest.Password)" InputType="InputType.Password" />
11+
<MudCheckBox id="Input.Remember" Label="Remember Me" @bind-Checked="LoginRequest.RememberMe" />
12+
</MudCardContent>
13+
<MudCardActions>
14+
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Outlined" Color="Color.Primary" Class="ma-2 py-2">Login</MudButton>
15+
</MudCardActions>
16+
</MudCard>
17+
</EditForm>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using Microsoft.AspNetCore.Components;
2+
using NetEvent.Client.Services;
3+
using NetEvent.Shared.Dto;
4+
5+
namespace NetEvent.Client.Pages
6+
{
7+
public partial class Login
8+
{
9+
[Inject]
10+
public CustomStateProvider AuthenticationStateProvider { get; set; }
11+
12+
[Inject]
13+
public NavigationManager NavigationManager { get; set; }
14+
15+
16+
public LoginRequest LoginRequest { get; set; } = new ();
17+
18+
19+
public string Error { get; set; }
20+
21+
22+
protected override async Task OnAfterRenderAsync(bool firstRender)
23+
{
24+
if (firstRender)
25+
{
26+
27+
}
28+
}
29+
30+
public async Task ExecuteLogin()
31+
{
32+
try
33+
{
34+
await AuthenticationStateProvider.Login(LoginRequest);
35+
NavigationManager.NavigateTo("");
36+
}
37+
catch (Exception ex)
38+
{
39+
Error = ex.Message;
40+
}
41+
}
42+
}
43+
}

NetEvent/Client/Pages/Profile/Manage.razor

Lines changed: 0 additions & 47 deletions
This file was deleted.

NetEvent/Client/Pages/Profile/Manage.razor.cs

Lines changed: 0 additions & 36 deletions
This file was deleted.

NetEvent/Client/Program.cs

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,29 @@
1+
using Microsoft.AspNetCore.Components.Authorization;
12
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
23
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
34
using MudBlazor.Services;
45
using NetEvent.Client;
6+
using NetEvent.Client.Services;
57

68
var builder = WebAssemblyHostBuilder.CreateDefault(args);
79
builder.RootComponents.Add<App>("#app");
810

11+
12+
builder.Services.AddOptions();
13+
builder.Services.AddAuthorizationCore();
14+
builder.Services.AddScoped<CustomStateProvider>();
15+
builder.Services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<CustomStateProvider>());
16+
builder.Services.AddScoped<IAuthService, AuthService>();
17+
builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
18+
19+
920
builder.Services.AddHttpClient("NetEvent.ServerAPI")
21+
.ConfigureHttpClient(client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
22+
23+
builder.Services.AddHttpClient("NetEvent.ServerAPI.Secure")
1024
.ConfigureHttpClient(client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
1125
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
1226

13-
// Supply HttpClient instances that include access tokens when making requests to the server project.
14-
builder.Services.AddScoped(provider =>
15-
{
16-
var factory = provider.GetRequiredService<IHttpClientFactory>();
17-
return factory.CreateClient("NetEvent.ServerAPI");
18-
});
19-
20-
builder.Services.AddOidcAuthentication(options =>
21-
{
22-
options.ProviderOptions.ClientId = "NetEvent-blazor-client";
23-
options.ProviderOptions.Authority = "https://localhost:44310/";
24-
options.ProviderOptions.ResponseType = "code";
25-
26-
options.UserOptions.RoleClaim = "role";
27-
options.ProviderOptions.DefaultScopes.Add("roles");
28-
29-
// Note: response_mode=fragment is the best option for a SPA. Unfortunately, the Blazor WASM
30-
// authentication stack is impacted by a bug that prevents it from correctly extracting
31-
// authorization error responses (e.g error=access_denied responses) from the URL fragment.
32-
// For more information about this bug, visit https://github.com/dotnet/aspnetcore/issues/28344.
33-
//
34-
options.ProviderOptions.ResponseMode = "query";
35-
options.AuthenticationPaths.RemoteRegisterPath = "https://localhost:44310/Identity/Account/Register";
36-
});
37-
3827
builder.Services.AddMudServices();
3928

4029
await builder.Build().RunAsync();
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using NetEvent.Shared.Dto;
2+
using System.Net.Http.Json;
3+
4+
namespace NetEvent.Client.Services
5+
{
6+
public class AuthService : IAuthService
7+
{
8+
private const string _HttpClientName = "NetEvent.ServerAPI";
9+
10+
private readonly IHttpClientFactory _HttpClientFactory;
11+
public AuthService(IHttpClientFactory httpClientFactory)
12+
{
13+
_HttpClientFactory = httpClientFactory;
14+
}
15+
public async Task<CurrentUser> CurrentUserInfo()
16+
{
17+
try
18+
{
19+
var client = _HttpClientFactory.CreateClient(_HttpClientName);
20+
21+
var result = await client.GetFromJsonAsync<CurrentUser>("api/auth/user/current");
22+
return result;
23+
}
24+
catch (Exception ex)
25+
{
26+
27+
return new CurrentUser() { IsAuthenticated = false};
28+
}
29+
}
30+
31+
public async Task Login(LoginRequest loginRequest)
32+
{
33+
var client = _HttpClientFactory.CreateClient(_HttpClientName);
34+
35+
var result = await client.PostAsJsonAsync("api/auth/login", loginRequest);
36+
if (result.StatusCode == System.Net.HttpStatusCode.BadRequest) throw new Exception(await result.Content.ReadAsStringAsync());
37+
result.EnsureSuccessStatusCode();
38+
}
39+
public async Task Logout()
40+
{
41+
var client = _HttpClientFactory.CreateClient(_HttpClientName);
42+
var result = await client.PostAsync("api/auth/logout", null);
43+
result.EnsureSuccessStatusCode();
44+
}
45+
public async Task Register(RegisterRequest registerRequest)
46+
{
47+
var client = _HttpClientFactory.CreateClient(_HttpClientName);
48+
var result = await client.PostAsJsonAsync("api/auth/register", registerRequest);
49+
if (result.StatusCode == System.Net.HttpStatusCode.BadRequest) throw new Exception(await result.Content.ReadAsStringAsync());
50+
result.EnsureSuccessStatusCode();
51+
}
52+
}
53+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Microsoft.AspNetCore.Components.Authorization;
2+
using NetEvent.Shared.Dto;
3+
using System.Security.Claims;
4+
5+
namespace NetEvent.Client.Services
6+
{
7+
public class CustomStateProvider : AuthenticationStateProvider
8+
{
9+
private readonly IAuthService api;
10+
private CurrentUser _currentUser;
11+
public CustomStateProvider(IAuthService api)
12+
{
13+
this.api = api;
14+
}
15+
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
16+
{
17+
var identity = new ClaimsIdentity();
18+
try
19+
{
20+
var userInfo = await GetCurrentUser();
21+
if (userInfo.IsAuthenticated)
22+
{
23+
var claims = new[] { new Claim(ClaimTypes.Name, _currentUser.UserName) }.Concat(_currentUser.Claims.Select(c => new Claim(c.Key, c.Value)));
24+
identity = new ClaimsIdentity(claims, "Server authentication");
25+
}
26+
}
27+
catch (HttpRequestException ex)
28+
{
29+
Console.WriteLine("Request failed:" + ex.ToString());
30+
}
31+
return new AuthenticationState(new ClaimsPrincipal(identity));
32+
}
33+
private async Task<CurrentUser> GetCurrentUser()
34+
{
35+
if (_currentUser != null && _currentUser.IsAuthenticated) return _currentUser;
36+
_currentUser = await api.CurrentUserInfo();
37+
return _currentUser;
38+
}
39+
public async Task Logout()
40+
{
41+
await api.Logout();
42+
_currentUser = null;
43+
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
44+
}
45+
46+
public async Task Login(LoginRequest loginParameters)
47+
{
48+
await api.Login(loginParameters);
49+
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
50+
}
51+
52+
public async Task Register(RegisterRequest registerParameters)
53+
{
54+
await api.Register(registerParameters);
55+
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
56+
}
57+
}
58+
}

0 commit comments

Comments
 (0)