diff --git a/OpenIddict.Samples.slnx b/OpenIddict.Samples.slnx
index 5c83e07c7..5c416e931 100644
--- a/OpenIddict.Samples.slnx
+++ b/OpenIddict.Samples.slnx
@@ -31,7 +31,6 @@
-
@@ -43,7 +42,6 @@
-
diff --git a/samples/Balosar/Balosar.Client/Balosar.Client.csproj b/samples/Balosar/Balosar.Client/Balosar.Client.csproj
index 985594ed6..840f9a8d2 100644
--- a/samples/Balosar/Balosar.Client/Balosar.Client.csproj
+++ b/samples/Balosar/Balosar.Client/Balosar.Client.csproj
@@ -5,10 +5,6 @@
true
-
-
-
-
diff --git a/samples/Balosar/Balosar.Client/Pages/FetchData.razor b/samples/Balosar/Balosar.Client/Pages/FetchData.razor
index 881ba4fc9..a91e0d069 100644
--- a/samples/Balosar/Balosar.Client/Pages/FetchData.razor
+++ b/samples/Balosar/Balosar.Client/Pages/FetchData.razor
@@ -1,7 +1,6 @@
@page "/fetchdata"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
-@using Balosar.Shared
@attribute [Authorize]
@inject HttpClient Http
@@ -53,4 +52,14 @@ else
}
}
+ class WeatherForecast
+ {
+ public DateTime Date { get; set; }
+
+ public int TemperatureC { get; set; }
+
+ public required string Summary { get; set; }
+
+ public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
+ }
}
diff --git a/samples/Balosar/Balosar.Client/Program.cs b/samples/Balosar/Balosar.Client/Program.cs
index 8447d551d..b8c8374b5 100644
--- a/samples/Balosar/Balosar.Client/Program.cs
+++ b/samples/Balosar/Balosar.Client/Program.cs
@@ -1,21 +1,15 @@
using Balosar.Client;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
+using Microsoft.Extensions.Options;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add("#app");
-builder.Services.AddHttpClient("Balosar.ServerAPI")
+builder.Services.AddHttpClient(Options.DefaultName)
.ConfigureHttpClient(client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler();
-// Supply HttpClient instances that include access tokens when making requests to the server project.
-builder.Services.AddScoped(provider =>
-{
- var factory = provider.GetRequiredService();
- return factory.CreateClient("Balosar.ServerAPI");
-});
-
builder.Services.AddOidcAuthentication(options =>
{
options.ProviderOptions.ClientId = "balosar-blazor-client";
diff --git a/samples/Balosar/Balosar.Server/Balosar.Server.csproj b/samples/Balosar/Balosar.Server/Balosar.Server.csproj
index a2a564dad..1c2e43a16 100644
--- a/samples/Balosar/Balosar.Server/Balosar.Server.csproj
+++ b/samples/Balosar/Balosar.Server/Balosar.Server.csproj
@@ -7,7 +7,6 @@
-
diff --git a/samples/Balosar/Balosar.Server/Controllers/AuthorizationController.cs b/samples/Balosar/Balosar.Server/Controllers/AuthorizationController.cs
index 72a5fe0c0..60a093f1d 100644
--- a/samples/Balosar/Balosar.Server/Controllers/AuthorizationController.cs
+++ b/samples/Balosar/Balosar.Server/Controllers/AuthorizationController.cs
@@ -138,7 +138,7 @@ public async Task Authorize()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
@@ -232,7 +232,7 @@ public async Task Accept()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
@@ -332,7 +332,7 @@ public async Task Exchange()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
identity.SetDestinations(GetDestinations);
diff --git a/samples/Balosar/Balosar.Server/Controllers/WeatherForecastController.cs b/samples/Balosar/Balosar.Server/Controllers/WeatherForecastController.cs
index 75b3c6d74..9e5997f9e 100644
--- a/samples/Balosar/Balosar.Server/Controllers/WeatherForecastController.cs
+++ b/samples/Balosar/Balosar.Server/Controllers/WeatherForecastController.cs
@@ -1,4 +1,4 @@
-using Balosar.Shared;
+using Balosar.Server.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Validation.AspNetCore;
diff --git a/samples/Balosar/Balosar.Shared/WeatherForecast.cs b/samples/Balosar/Balosar.Server/Models/WeatherForecast.cs
similarity index 86%
rename from samples/Balosar/Balosar.Shared/WeatherForecast.cs
rename to samples/Balosar/Balosar.Server/Models/WeatherForecast.cs
index 25b2492c2..23cd83b10 100644
--- a/samples/Balosar/Balosar.Shared/WeatherForecast.cs
+++ b/samples/Balosar/Balosar.Server/Models/WeatherForecast.cs
@@ -1,4 +1,4 @@
-namespace Balosar.Shared;
+namespace Balosar.Server.Models;
public class WeatherForecast
{
diff --git a/samples/Balosar/Balosar.Shared/Balosar.Shared.csproj b/samples/Balosar/Balosar.Shared/Balosar.Shared.csproj
deleted file mode 100644
index acda77487..000000000
--- a/samples/Balosar/Balosar.Shared/Balosar.Shared.csproj
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
- $(NetCoreTargetFramework)
-
-
-
-
-
-
-
diff --git a/samples/Dantooine/Dantooine.Api/Program.cs b/samples/Dantooine/Dantooine.Api/Program.cs
index 5859ce87b..0f6e119b4 100644
--- a/samples/Dantooine/Dantooine.Api/Program.cs
+++ b/samples/Dantooine/Dantooine.Api/Program.cs
@@ -46,7 +46,7 @@
app.UseAuthentication();
app.UseAuthorization();
-app.MapGet("api/DantooineApi", [Authorize] () => new string[] { "data1", "data2" });
+app.MapGet("api/downstream-api", () => new[] { "data1", "data2" }).RequireAuthorization();
app.Run();
diff --git a/samples/Dantooine/Dantooine.Server/Controllers/AuthorizationController.cs b/samples/Dantooine/Dantooine.Server/Controllers/AuthorizationController.cs
index d0b55b57e..ee00e3a31 100644
--- a/samples/Dantooine/Dantooine.Server/Controllers/AuthorizationController.cs
+++ b/samples/Dantooine/Dantooine.Server/Controllers/AuthorizationController.cs
@@ -138,7 +138,7 @@ public async Task Authorize()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
@@ -232,7 +232,7 @@ public async Task Accept()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
@@ -332,7 +332,7 @@ public async Task Exchange()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
identity.SetDestinations(GetDestinations);
diff --git a/samples/Dantooine/Dantooine.Server/Program.cs b/samples/Dantooine/Dantooine.Server/Program.cs
index 9f2d22dc5..ef3309a15 100644
--- a/samples/Dantooine/Dantooine.Server/Program.cs
+++ b/samples/Dantooine/Dantooine.Server/Program.cs
@@ -177,7 +177,7 @@ static async Task RegisterApplicationsAsync(IServiceProvider provider)
// Blazor Hosted
if (await manager.FindByClientIdAsync("blazorcodeflowpkceclient") is null)
{
- await manager.CreateAsync(new OpenIddictApplicationDescriptor
+ var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = "blazorcodeflowpkceclient",
ConsentType = ConsentTypes.Explicit,
@@ -217,14 +217,17 @@ await manager.CreateAsync(new OpenIddictApplicationDescriptor
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
- Permissions.Scopes.Roles,
- Permissions.Prefixes.Scope + "api1"
+ Permissions.Scopes.Roles
},
Requirements =
{
Requirements.Features.ProofKeyForCodeExchange
}
- });
+ };
+
+ descriptor.AddScopePermissions("api1");
+
+ await manager.CreateAsync(descriptor);
}
}
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Client/Dantooine.WebAssembly.Client.csproj b/samples/Dantooine/Dantooine.WebAssembly.Client/Dantooine.WebAssembly.Client.csproj
index 2888670e4..fd0745b25 100644
--- a/samples/Dantooine/Dantooine.WebAssembly.Client/Dantooine.WebAssembly.Client.csproj
+++ b/samples/Dantooine/Dantooine.WebAssembly.Client/Dantooine.WebAssembly.Client.csproj
@@ -12,8 +12,4 @@
-
-
-
-
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Client/Pages/DantooineApi.razor b/samples/Dantooine/Dantooine.WebAssembly.Client/Pages/DantooineApi.razor
deleted file mode 100644
index a30ea7d4d..000000000
--- a/samples/Dantooine/Dantooine.WebAssembly.Client/Pages/DantooineApi.razor
+++ /dev/null
@@ -1,43 +0,0 @@
-@page "/dantooineapi1"
-@using Dantooine.WebAssembly.Shared
-@inject IHttpClientFactory httpClientFactory
-@inject IJSRuntime JSRuntime
-
-Data retrieved from Dantooine API via YARP
-
-@if (apiData == null)
-{
- Loading...
-}
-else
-{
-
-
-
- | Name |
-
-
-
- @foreach (var data in apiData)
- {
-
- | @data |
-
- }
-
-
-}
-
-@code {
- private string[] apiData = default!;
-
- protected override async Task OnInitializedAsync()
- {
- var token = await JSRuntime.InvokeAsync("getAntiForgeryToken");
-
- var client = httpClientFactory.CreateClient("authorizedClient");
- client.DefaultRequestHeaders.Add("X-XSRF-TOKEN", token);
-
- apiData = (await client.GetFromJsonAsync("api/DantooineApi"))!;
- }
-}
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Client/Pages/DirectApi.razor b/samples/Dantooine/Dantooine.WebAssembly.Client/Pages/DirectApi.razor
deleted file mode 100644
index e13995564..000000000
--- a/samples/Dantooine/Dantooine.WebAssembly.Client/Pages/DirectApi.razor
+++ /dev/null
@@ -1,43 +0,0 @@
-@page "/directapi"
-@inject IHttpClientFactory httpClientFactory
-@inject IJSRuntime JSRuntime
-
-Data from Direct API
-
-@if (apiData == null)
-{
- Loading...
-}
-else
-{
-
-
-
- | Data |
-
-
-
- @foreach (var data in apiData)
- {
-
- | @data |
-
- }
-
-
-}
-
-@code {
- private string[] apiData = default!;
-
- protected override async Task OnInitializedAsync()
- {
- var token = await JSRuntime.InvokeAsync("getAntiForgeryToken");
-
- var client = httpClientFactory.CreateClient("authorizedClient");
- client.DefaultRequestHeaders.Add("X-XSRF-TOKEN", token);
-
- apiData = (await client.GetFromJsonAsync("api/DirectApi"))!;
- }
-
-}
\ No newline at end of file
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Client/Pages/DownstreamApi.razor b/samples/Dantooine/Dantooine.WebAssembly.Client/Pages/DownstreamApi.razor
new file mode 100644
index 000000000..0813a4249
--- /dev/null
+++ b/samples/Dantooine/Dantooine.WebAssembly.Client/Pages/DownstreamApi.razor
@@ -0,0 +1,58 @@
+@page "/downstream-api"
+@using System.Net
+@inject HttpClient client
+@inject NavigationManager manager
+@inject AuthenticationStateProvider provider
+@inject IJSRuntime runtime
+
+Data retrieved from downstream API via YARP
+
+@if (data is null)
+{
+ Loading...
+}
+else
+{
+
+
+
+ | Name |
+
+
+
+ @for (var index = 0; index < data.Length; index++)
+ {
+
+ | @data[index] |
+
+ }
+
+
+}
+
+@code {
+ private string[] data = default!;
+
+ protected override async Task OnInitializedAsync()
+ {
+ var state = await provider.GetAuthenticationStateAsync();
+ if (state is not { User.Identity.IsAuthenticated: true })
+ {
+ manager.NavigateTo($"login?returnUrl=/{Uri.EscapeDataString(manager.ToBaseRelativePath(manager.Uri))}", true);
+ return;
+ }
+
+ client.DefaultRequestHeaders.Add("X-XSRF-TOKEN", await runtime.InvokeAsync("getAntiForgeryToken"));
+
+ try
+ {
+ data = (await client.GetFromJsonAsync("api/downstream-api"))!;
+ }
+
+ catch (HttpRequestException exception) when (exception.StatusCode is HttpStatusCode.Unauthorized)
+ {
+ manager.NavigateTo($"login?returnUrl=/{Uri.EscapeDataString(manager.ToBaseRelativePath(manager.Uri))}", true);
+ return;
+ }
+ }
+}
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Client/Pages/LocalApi.razor b/samples/Dantooine/Dantooine.WebAssembly.Client/Pages/LocalApi.razor
new file mode 100644
index 000000000..e2e1270f3
--- /dev/null
+++ b/samples/Dantooine/Dantooine.WebAssembly.Client/Pages/LocalApi.razor
@@ -0,0 +1,59 @@
+@page "/local-api"
+@using System.Net
+@inject HttpClient client
+@inject NavigationManager manager
+@inject AuthenticationStateProvider provider
+@inject IJSRuntime runtime
+
+Data from local API
+
+@if (data is null)
+{
+ Loading...
+}
+else
+{
+
+
+
+ | Data |
+
+
+
+ @for (var index = 0; index < data.Length; index++)
+ {
+
+ | @data[index] |
+
+ }
+
+
+}
+
+@code {
+ private string[] data = default!;
+
+ protected override async Task OnInitializedAsync()
+ {
+ var state = await provider.GetAuthenticationStateAsync();
+ if (state is not { User.Identity.IsAuthenticated: true })
+ {
+ manager.NavigateTo($"login?returnUrl=/{Uri.EscapeDataString(manager.ToBaseRelativePath(manager.Uri))}", true);
+ return;
+ }
+
+ client.DefaultRequestHeaders.Add("X-XSRF-TOKEN", await runtime.InvokeAsync("getAntiForgeryToken"));
+
+ try
+ {
+ data = (await client.GetFromJsonAsync("api/local-api"))!;
+ }
+
+ catch (HttpRequestException exception) when (exception.StatusCode is HttpStatusCode.Unauthorized)
+ {
+ manager.NavigateTo($"login?returnUrl=/{Uri.EscapeDataString(manager.ToBaseRelativePath(manager.Uri))}", true);
+ return;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Client/Program.cs b/samples/Dantooine/Dantooine.WebAssembly.Client/Program.cs
index edc88dfd0..d931d7206 100644
--- a/samples/Dantooine/Dantooine.WebAssembly.Client/Program.cs
+++ b/samples/Dantooine/Dantooine.WebAssembly.Client/Program.cs
@@ -1,34 +1,21 @@
-using System.Net.Http.Headers;
-using Dantooine.WebAssembly.Client;
+using Dantooine.WebAssembly.Client;
using Dantooine.WebAssembly.Client.Services;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Options;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();
-builder.Services.TryAddSingleton();
-builder.Services.TryAddSingleton(provider => (HostAuthenticationStateProvider) provider.GetRequiredService());
-builder.Services.AddTransient();
-
-builder.RootComponents.Add("#app");
-builder.Services.AddHttpClient("default", client =>
-{
- client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress);
- client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
-});
+builder.Services.AddHttpClient(Options.DefaultName)
+ .ConfigureHttpClient(client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
-builder.Services.AddHttpClient("authorizedClient", client =>
-{
- client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress);
- client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
-}).AddHttpMessageHandler();
+builder.Services.TryAddSingleton();
-builder.Services.AddTransient(provider => provider.GetRequiredService().CreateClient("default"));
+builder.RootComponents.Add("#app");
var host = builder.Build();
-
await host.RunAsync();
\ No newline at end of file
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Client/Services/AuthorizedHandler.cs b/samples/Dantooine/Dantooine.WebAssembly.Client/Services/AuthorizedHandler.cs
deleted file mode 100644
index 98bf6c0f0..000000000
--- a/samples/Dantooine/Dantooine.WebAssembly.Client/Services/AuthorizedHandler.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System.Net;
-
-namespace Dantooine.WebAssembly.Client.Services;
-
-// Original source: https://github.com/berhir/BlazorWebAssemblyCookieAuth.
-public class AuthorizedHandler : DelegatingHandler
-{
- private readonly HostAuthenticationStateProvider _authenticationStateProvider;
-
- public AuthorizedHandler(HostAuthenticationStateProvider authenticationStateProvider)
- {
- _authenticationStateProvider = authenticationStateProvider;
- }
-
- protected override async Task SendAsync(
- HttpRequestMessage request,
- CancellationToken cancellationToken)
- {
- var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
- HttpResponseMessage responseMessage;
- if (authState.User is not { Identity.IsAuthenticated: true })
- {
- // if user is not authenticated, immediately set response status to 401 Unauthorized
- responseMessage = new HttpResponseMessage(HttpStatusCode.Unauthorized);
- }
- else
- {
- responseMessage = await base.SendAsync(request, cancellationToken);
- }
-
- if (responseMessage.StatusCode == HttpStatusCode.Unauthorized)
- {
- // if server returned 401 Unauthorized, redirect to login page
- _authenticationStateProvider.SignIn();
- }
-
- return responseMessage;
- }
-}
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Client/Services/HostAuthenticationStateProvider.cs b/samples/Dantooine/Dantooine.WebAssembly.Client/Services/HostAuthenticationStateProvider.cs
index 900d220c2..f9d11b7de 100644
--- a/samples/Dantooine/Dantooine.WebAssembly.Client/Services/HostAuthenticationStateProvider.cs
+++ b/samples/Dantooine/Dantooine.WebAssembly.Client/Services/HostAuthenticationStateProvider.cs
@@ -1,92 +1,36 @@
-using System.Net.Http.Json;
+using System.Net;
using System.Security.Claims;
-using Dantooine.WebAssembly.Shared.Authorization;
-using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
namespace Dantooine.WebAssembly.Client.Services;
-// Original source: https://github.com/berhir/BlazorWebAssemblyCookieAuth.
-public class HostAuthenticationStateProvider : AuthenticationStateProvider
+public class HostAuthenticationStateProvider(HttpClient client) : AuthenticationStateProvider
{
- private static readonly TimeSpan _userCacheRefreshInterval = TimeSpan.FromSeconds(60);
-
- private const string LogInPath = "login";
-
- private readonly NavigationManager _navigation;
- private readonly HttpClient _client;
- private readonly ILogger _logger;
-
- private DateTimeOffset _userLastCheck = DateTimeOffset.FromUnixTimeSeconds(0);
- private ClaimsPrincipal _cachedUser = new ClaimsPrincipal(new ClaimsIdentity());
-
- public HostAuthenticationStateProvider(NavigationManager navigation, HttpClient client, ILogger logger)
- {
- _navigation = navigation;
- _client = client;
- _logger = logger;
- }
-
public override async Task GetAuthenticationStateAsync()
{
- return new AuthenticationState(await GetUser(useCache: true));
- }
+ // Note: the resulting authentication state is deliberately not cached, to ensure that the logged-in
+ // user information is refreshed when navigating to a different page. While a caching strategy can be
+ // implemented, doing so must be done with care to ensure that the cached authentication state is
+ // invalidated when the user logs out, and that the cache is refreshed when the user logs in again.
- public void SignIn(string? customReturnUrl = null)
- {
- var returnUrl = customReturnUrl != null ? _navigation.ToAbsoluteUri(customReturnUrl).ToString() : null;
- var encodedReturnUrl = Uri.EscapeDataString(_navigation.ToBaseRelativePath(returnUrl ?? _navigation.Uri));
- var logInUrl = _navigation.ToAbsoluteUri($"{LogInPath}?returnUrl=/{encodedReturnUrl}");
- _navigation.NavigateTo(logInUrl.ToString(), true);
- }
-
- private async ValueTask GetUser(bool useCache = false)
- {
- var now = DateTimeOffset.Now;
- if (useCache && now < _userLastCheck + _userCacheRefreshInterval)
- {
- _logger.LogDebug("Taking user from cache");
- return _cachedUser;
- }
-
- _logger.LogDebug("Fetching user");
- _cachedUser = await FetchUser();
- _userLastCheck = now;
-
- return _cachedUser;
- }
-
- private async Task FetchUser()
- {
- UserInfo? user = null;
+ string name;
try
{
- user = await _client.GetFromJsonAsync("api/User");
- }
- catch (Exception exc)
- {
- _logger.LogWarning(exc, "Fetching user failed.");
+ name = await client.GetStringAsync("api/current-user-name");
}
- if (user == null || !user.IsAuthenticated)
+ catch (HttpRequestException exception) when (exception.StatusCode is HttpStatusCode.Unauthorized)
{
- return new ClaimsPrincipal(new ClaimsIdentity());
+ return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
}
- var identity = new ClaimsIdentity(
- nameof(HostAuthenticationStateProvider),
- user.NameClaimType,
- user.RoleClaimType);
+ var identity = new ClaimsIdentity("Cookies");
+ identity.AddClaim(new Claim(ClaimTypes.Name, name));
- if (user.Claims != null)
- {
- foreach (var claim in user.Claims)
- {
- identity.AddClaim(new Claim(claim.Type, claim.Value));
- }
- }
+ var state = new AuthenticationState(new ClaimsPrincipal(identity));
+ NotifyAuthenticationStateChanged(Task.FromResult(state));
- return new ClaimsPrincipal(identity);
+ return state;
}
}
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Client/Shared/NavMenu.razor b/samples/Dantooine/Dantooine.WebAssembly.Client/Shared/NavMenu.razor
index 7ae0e8a17..ceac80124 100644
--- a/samples/Dantooine/Dantooine.WebAssembly.Client/Shared/NavMenu.razor
+++ b/samples/Dantooine/Dantooine.WebAssembly.Client/Shared/NavMenu.razor
@@ -16,13 +16,13 @@
-
- Direct API
+
+ Local API
-
- Dantooine Api1
+
+ Downstream API
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Client/wwwroot/antiForgeryToken.js b/samples/Dantooine/Dantooine.WebAssembly.Client/wwwroot/antiForgeryToken.js
index ad81e5ad7..335582d4c 100644
--- a/samples/Dantooine/Dantooine.WebAssembly.Client/wwwroot/antiForgeryToken.js
+++ b/samples/Dantooine/Dantooine.WebAssembly.Client/wwwroot/antiForgeryToken.js
@@ -5,6 +5,6 @@ function getAntiForgeryToken() {
return elements[0].value
}
- console.warn('no anti forgery token found!');
+ console.warn('No anti forgery token was found in the document.');
return null;
}
\ No newline at end of file
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Server/Controllers/DirectApiController.cs b/samples/Dantooine/Dantooine.WebAssembly.Server/Controllers/DirectApiController.cs
deleted file mode 100644
index 34b83ccf8..000000000
--- a/samples/Dantooine/Dantooine.WebAssembly.Server/Controllers/DirectApiController.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Microsoft.AspNetCore.Authentication.Cookies;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-
-namespace Dantooine.WebAssembly.Server.Controllers;
-
-[ValidateAntiForgeryToken]
-[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
-[ApiController]
-[Route("api/[controller]")]
-public class DirectApiController : ControllerBase
-{
- [HttpGet]
- public IEnumerable Get()
- {
- return new List { "some data", "more data", "loads of data" };
- }
-}
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Server/Controllers/UserController.cs b/samples/Dantooine/Dantooine.WebAssembly.Server/Controllers/UserController.cs
deleted file mode 100644
index f6c9a5b2e..000000000
--- a/samples/Dantooine/Dantooine.WebAssembly.Server/Controllers/UserController.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System.Security.Claims;
-using Dantooine.WebAssembly.Shared.Authorization;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using static OpenIddict.Abstractions.OpenIddictConstants;
-
-namespace Dantooine.WebAssembly.Server.Controllers;
-
-// Original source: https://github.com/berhir/BlazorWebAssemblyCookieAuth.
-[Route("api/[controller]")]
-[ApiController]
-public class UserController : ControllerBase
-{
- [HttpGet]
- [AllowAnonymous]
- public IActionResult GetCurrentUser()
- {
- return Ok(User.Identity is { IsAuthenticated: true } ? CreateUserInfo(User) : UserInfo.Anonymous);
- }
-
- private static UserInfo CreateUserInfo(ClaimsPrincipal principal)
- {
- if (principal.Identity is not { IsAuthenticated: true })
- {
- return UserInfo.Anonymous;
- }
-
- var userinfo = new UserInfo
- {
- IsAuthenticated = true
- };
-
- if (principal.Identity is ClaimsIdentity identity)
- {
- userinfo.NameClaimType = identity.NameClaimType;
- userinfo.RoleClaimType = identity.RoleClaimType;
- }
- else
- {
- userinfo.NameClaimType = Claims.Name;
- userinfo.RoleClaimType = Claims.Role;
- }
-
- if (principal.Claims.Any())
- {
- var claims = new List();
-
- foreach (var claim in principal.FindAll(userinfo.NameClaimType))
- {
- claims.Add(new ClaimValue(userinfo.NameClaimType, claim.Value));
- }
-
- userinfo.Claims = claims;
- }
-
- return userinfo;
- }
-}
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Server/Dantooine.WebAssembly.Server.csproj b/samples/Dantooine/Dantooine.WebAssembly.Server/Dantooine.WebAssembly.Server.csproj
index eecd78aa4..a7440c4cd 100644
--- a/samples/Dantooine/Dantooine.WebAssembly.Server/Dantooine.WebAssembly.Server.csproj
+++ b/samples/Dantooine/Dantooine.WebAssembly.Server/Dantooine.WebAssembly.Server.csproj
@@ -18,7 +18,6 @@
-
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Server/Program.cs b/samples/Dantooine/Dantooine.WebAssembly.Server/Program.cs
index 8b5775e20..f4cc146d6 100644
--- a/samples/Dantooine/Dantooine.WebAssembly.Server/Program.cs
+++ b/samples/Dantooine/Dantooine.WebAssembly.Server/Program.cs
@@ -1,7 +1,9 @@
using System.Globalization;
+using System.Security.Claims;
using System.Security.Cryptography;
using Dantooine.WebAssembly.Server.Helpers;
using Dantooine.WebAssembly.Server.Models;
+using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.EntityFrameworkCore;
@@ -255,6 +257,34 @@
app.UseAuthentication();
app.UseAuthorization();
+app.Use(async (context, next) =>
+{
+ // Note: the antiforgery middleware only validates the antiforgery token for POST, PUT and PATCH requests.
+ // In this sample, the antiforgery token is validated for all requests sent to /api (except the special
+ // /api/current-user-name endpoint) to detect cases where the user logged in under a different identity in
+ // another tab and tries to perform actions in a tab that wasn't refreshed: since the antiforgery token
+ // is always bound to the user's identity contained in the authentication cookie, the validation will fail
+ // and the user agent will be automatically redirected to the login page to refresh the user information.
+
+ if (context.Request.Path.StartsWithSegments("/api") && context.Request.Path != "/api/current-user-name")
+ {
+ var service = context.RequestServices.GetRequiredService();
+
+ try
+ {
+ await service.ValidateRequestAsync(context);
+ }
+
+ catch (AntiforgeryValidationException)
+ {
+ await context.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme);
+ return;
+ }
+ }
+
+ await next(context);
+});
+
app.MapRazorPages();
app.MapControllers();
@@ -262,9 +292,18 @@
// the user agent to the login page configured in the cookie authentication options.
// In this case, this behavior is not desirable as an HTTP 401 response MUST be
// returned to the WASM client to automatically redirect the user agent to the
-// login page. As such, this logic is disabled for all proxied requests.
+// login page. As such, this logic is disabled for all local and forwarded requests.
+
app.MapReverseProxy(ConfigureProxyPipeline).DisableCookieRedirect();
+app.MapGet("/api/local-api", () => new[] { "some data", "more data", "loads of data" })
+ .RequireAuthorization()
+ .DisableCookieRedirect();
+
+app.MapGet("/api/current-user-name", (ClaimsPrincipal user) => user.Identity!.Name!)
+ .RequireAuthorization()
+ .DisableCookieRedirect();
+
app.MapFallbackToPage("/_Host");
// Before starting the host, create the database used to store the application data.
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Server/appsettings.json b/samples/Dantooine/Dantooine.WebAssembly.Server/appsettings.json
index 274c8032a..daa1ba7ea 100644
--- a/samples/Dantooine/Dantooine.WebAssembly.Server/appsettings.json
+++ b/samples/Dantooine/Dantooine.WebAssembly.Server/appsettings.json
@@ -15,7 +15,7 @@
"ClusterId": "cluster1",
"AuthorizationPolicy": "CookieAuthenticationPolicy",
"Match": {
- "Path": "api/DantooineApi"
+ "Path": "api/downstream-api"
}
}
},
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Shared/Authorization/ClaimValue.cs b/samples/Dantooine/Dantooine.WebAssembly.Shared/Authorization/ClaimValue.cs
deleted file mode 100644
index 0b6808770..000000000
--- a/samples/Dantooine/Dantooine.WebAssembly.Shared/Authorization/ClaimValue.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Dantooine.WebAssembly.Shared.Authorization;
-
-// Original source: https://github.com/berhir/BlazorWebAssemblyCookieAuth.
-public class ClaimValue
-{
- public ClaimValue(string type, string value)
- {
- Type = type;
- Value = value;
- }
-
- public string Type { get; }
-
- public string Value { get; }
-}
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Shared/Authorization/UserInfo.cs b/samples/Dantooine/Dantooine.WebAssembly.Shared/Authorization/UserInfo.cs
deleted file mode 100644
index a7cf2b3c4..000000000
--- a/samples/Dantooine/Dantooine.WebAssembly.Shared/Authorization/UserInfo.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Dantooine.WebAssembly.Shared.Authorization;
-
-// Original source: https://github.com/berhir/BlazorWebAssemblyCookieAuth.
-public class UserInfo
-{
- public static readonly UserInfo Anonymous = new UserInfo();
-
- public bool IsAuthenticated { get; set; }
-
- public string? NameClaimType { get; set; }
-
- public string? RoleClaimType { get; set; }
-
- public ICollection Claims { get; set; } = [];
-}
diff --git a/samples/Dantooine/Dantooine.WebAssembly.Shared/Dantooine.WebAssembly.Shared.csproj b/samples/Dantooine/Dantooine.WebAssembly.Shared/Dantooine.WebAssembly.Shared.csproj
deleted file mode 100644
index 70c17b8f7..000000000
--- a/samples/Dantooine/Dantooine.WebAssembly.Shared/Dantooine.WebAssembly.Shared.csproj
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
- $(NetCoreTargetFramework)
-
-
-
-
-
-
diff --git a/samples/Fornax/Fornax.Server/Account/RegisterExternalLogin.aspx.cs b/samples/Fornax/Fornax.Server/Account/RegisterExternalLogin.aspx.cs
index ab4b72bc3..689586dfe 100644
--- a/samples/Fornax/Fornax.Server/Account/RegisterExternalLogin.aspx.cs
+++ b/samples/Fornax/Fornax.Server/Account/RegisterExternalLogin.aspx.cs
@@ -24,7 +24,7 @@ protected string ProviderAccountKey
private void RedirectOnFail()
{
- Response.Redirect((User.Identity.IsAuthenticated) ? "~/Account/Manage" : "~/Account/Login");
+ Response.Redirect(User.Identity.IsAuthenticated ? "~/Account/Manage" : "~/Account/Login");
}
protected void Page_Load()
diff --git a/samples/Geonosis/Geonosis.Auth/Controllers/AuthorizationController.cs b/samples/Geonosis/Geonosis.Auth/Controllers/AuthorizationController.cs
index 1ddd98d18..45d1f375b 100644
--- a/samples/Geonosis/Geonosis.Auth/Controllers/AuthorizationController.cs
+++ b/samples/Geonosis/Geonosis.Auth/Controllers/AuthorizationController.cs
@@ -139,7 +139,7 @@ public async Task Authorize()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
@@ -234,7 +234,7 @@ public async Task Accept()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
@@ -335,7 +335,7 @@ public async Task Exchange()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
identity.SetDestinations(GetDestinations);
diff --git a/samples/Hollastin/Hollastin.Server/Controllers/AuthorizationController.cs b/samples/Hollastin/Hollastin.Server/Controllers/AuthorizationController.cs
index 12bbc9510..a240bac1f 100644
--- a/samples/Hollastin/Hollastin.Server/Controllers/AuthorizationController.cs
+++ b/samples/Hollastin/Hollastin.Server/Controllers/AuthorizationController.cs
@@ -76,7 +76,7 @@ public async Task Exchange()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
// Set the list of scopes granted to the client application.
identity.SetScopes(new[]
diff --git a/samples/Imynusoph/Imynusoph.Server/Controllers/AuthorizationController.cs b/samples/Imynusoph/Imynusoph.Server/Controllers/AuthorizationController.cs
index 03b69e433..56699c1f1 100644
--- a/samples/Imynusoph/Imynusoph.Server/Controllers/AuthorizationController.cs
+++ b/samples/Imynusoph/Imynusoph.Server/Controllers/AuthorizationController.cs
@@ -76,7 +76,7 @@ public async Task Exchange()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
@@ -128,7 +128,7 @@ public async Task Exchange()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
identity.SetDestinations(GetDestinations);
diff --git a/samples/Matty/Matty.Server/Controllers/AuthorizationController.cs b/samples/Matty/Matty.Server/Controllers/AuthorizationController.cs
index 7aba032b5..4bb5b4025 100644
--- a/samples/Matty/Matty.Server/Controllers/AuthorizationController.cs
+++ b/samples/Matty/Matty.Server/Controllers/AuthorizationController.cs
@@ -98,7 +98,7 @@ public async Task VerifyAccept()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
@@ -184,7 +184,7 @@ public async Task Exchange()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
identity.SetDestinations(GetDestinations);
diff --git a/samples/Mimban/Mimban.Server/Program.cs b/samples/Mimban/Mimban.Server/Program.cs
index 73690af51..7c6ee1cec 100644
--- a/samples/Mimban/Mimban.Server/Program.cs
+++ b/samples/Mimban/Mimban.Server/Program.cs
@@ -123,8 +123,7 @@
app.UseAuthentication();
app.UseAuthorization();
-app.MapGet("api", [Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
- (ClaimsPrincipal user) => user.Identity!.Name);
+app.MapGet("api", (ClaimsPrincipal user) => user.Identity!.Name).RequireAuthorization();
app.MapMethods("callback/login/github", [HttpMethods.Get, HttpMethods.Post], async (HttpContext context) =>
{
diff --git a/samples/Mortis/Mortis.Server/Controllers/AccountController.cs b/samples/Mortis/Mortis.Server/Controllers/AccountController.cs
index e4faa8788..0f63618fd 100644
--- a/samples/Mortis/Mortis.Server/Controllers/AccountController.cs
+++ b/samples/Mortis/Mortis.Server/Controllers/AccountController.cs
@@ -200,7 +200,7 @@ public async Task ForgotPassword(ForgotPasswordViewModel model)
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.Email);
- if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
+ if (user == null || !await UserManager.IsEmailConfirmedAsync(user.Id))
{
// Ne révélez pas que l'utilisateur n'existe pas ou qu'il n'est pas confirmé
return View("ForgotPasswordConfirmation");
diff --git a/samples/Velusia/Velusia.Server/Controllers/AuthorizationController.cs b/samples/Velusia/Velusia.Server/Controllers/AuthorizationController.cs
index 1cda38148..e4dba6052 100644
--- a/samples/Velusia/Velusia.Server/Controllers/AuthorizationController.cs
+++ b/samples/Velusia/Velusia.Server/Controllers/AuthorizationController.cs
@@ -138,7 +138,7 @@ public async Task Authorize()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
@@ -232,7 +232,7 @@ public async Task Accept()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
@@ -332,7 +332,7 @@ public async Task Exchange()
.SetClaim(Claims.Email, await _userManager.GetEmailAsync(user))
.SetClaim(Claims.Name, await _userManager.GetUserNameAsync(user))
.SetClaim(Claims.PreferredUsername, await _userManager.GetUserNameAsync(user))
- .SetClaims(Claims.Role, [.. (await _userManager.GetRolesAsync(user))]);
+ .SetClaims(Claims.Role, [.. await _userManager.GetRolesAsync(user)]);
identity.SetDestinations(GetDestinations);
diff --git a/samples/Zirku/Zirku.Api1/Program.cs b/samples/Zirku/Zirku.Api1/Program.cs
index 5fdf818b7..4cfc97a42 100644
--- a/samples/Zirku/Zirku.Api1/Program.cs
+++ b/samples/Zirku/Zirku.Api1/Program.cs
@@ -105,7 +105,7 @@
app.UseAuthentication();
app.UseAuthorization();
-app.MapGet("api", [Authorize] (ClaimsPrincipal user) => $"{user.Identity!.Name} is allowed to access Api1.");
+app.MapGet("api", (ClaimsPrincipal user) => $"{user.Identity!.Name} is allowed to access Api1.").RequireAuthorization();
app.UseWelcomePage("/");
diff --git a/samples/Zirku/Zirku.Api2/Program.cs b/samples/Zirku/Zirku.Api2/Program.cs
index 9941ed075..86e86ad9f 100644
--- a/samples/Zirku/Zirku.Api2/Program.cs
+++ b/samples/Zirku/Zirku.Api2/Program.cs
@@ -98,7 +98,7 @@
app.UseAuthentication();
app.UseAuthorization();
-app.MapGet("api", [Authorize] (ClaimsPrincipal user) => $"{user.Identity!.Name} is allowed to access Api2.");
+app.MapGet("api", (ClaimsPrincipal user) => $"{user.Identity!.Name} is allowed to access Api2.").RequireAuthorization();
app.UseWelcomePage("/");
diff --git a/samples/Zirku/Zirku.Server/Program.cs b/samples/Zirku/Zirku.Server/Program.cs
index 95be3b706..1ffad32b3 100644
--- a/samples/Zirku/Zirku.Server/Program.cs
+++ b/samples/Zirku/Zirku.Server/Program.cs
@@ -5,14 +5,12 @@
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;
-using OpenIddict.Validation.AspNetCore;
using Quartz;
using static OpenIddict.Abstractions.OpenIddictConstants;
@@ -111,16 +109,6 @@
//
options.UseAspNetCore()
.EnableAuthorizationEndpointPassthrough();
- })
-
- // Register the OpenIddict validation components.
- .AddValidation(options =>
- {
- // Import the configuration from the local OpenIddict server instance.
- options.UseLocalServer();
-
- // Register the ASP.NET Core host.
- options.UseAspNetCore();
});
// Configure Kestrel to listen on the 44319 port and configure it to enforce mTLS.
@@ -176,24 +164,18 @@
.AllowAnyMethod()
.WithOrigins("http://localhost:5112")));
-builder.Services.AddAuthorization();
-
var app = builder.Build();
app.UseCors();
app.UseHttpsRedirection();
app.UseAuthentication();
-app.UseAuthorization();
-
-app.MapGet("api",
- [Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)] (ClaimsPrincipal user) => user.Identity!.Name);
app.MapMethods("connect/authorize", [HttpMethods.Get, HttpMethods.Post], async (HttpContext context, IOpenIddictScopeManager manager) =>
{
// Retrieve the OpenIddict server request from the HTTP context.
var request = context.GetOpenIddictServerRequest() ??
- throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
+ throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
var identifier = (int?) request["hardcoded_identity_id"];
if (identifier is not (1 or 2))
@@ -266,7 +248,7 @@ async Task CreateApplicationsAsync()
if (await manager.FindByClientIdAsync("console_app") is null)
{
- await manager.CreateAsync(new OpenIddictApplicationDescriptor
+ var descriptor = new OpenIddictApplicationDescriptor
{
ApplicationType = ApplicationTypes.Native,
ClientId = "console_app",
@@ -283,16 +265,18 @@ await manager.CreateAsync(new OpenIddictApplicationDescriptor
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
- Permissions.Scopes.Roles,
- Permissions.Prefixes.Scope + "api1",
- Permissions.Prefixes.Scope + "api2"
+ Permissions.Scopes.Roles
}
- });
+ };
+
+ descriptor.AddScopePermissions("api1", "api2");
+
+ await manager.CreateAsync(descriptor);
}
if (await manager.FindByClientIdAsync("spa") is null)
{
- await manager.CreateAsync(new OpenIddictApplicationDescriptor
+ var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = "spa",
ClientType = ClientTypes.Public,
@@ -312,15 +296,17 @@ await manager.CreateAsync(new OpenIddictApplicationDescriptor
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
- Permissions.Scopes.Roles,
- Permissions.Prefixes.Scope + "api1",
- Permissions.Prefixes.Scope + "api2"
+ Permissions.Scopes.Roles
},
Requirements =
{
- Requirements.Features.ProofKeyForCodeExchange,
- },
- });
+ Requirements.Features.ProofKeyForCodeExchange
+ }
+ };
+
+ descriptor.AddScopePermissions("api1", "api2");
+
+ await manager.CreateAsync(descriptor);
}
if (await manager.FindByClientIdAsync("resource_server_1") is null)