Skip to content

Commit f0021f6

Browse files
committed
fix(auth): register and seed OpenIddict API scopes to resolve invalid_scope for angular-client
1 parent 71eabbc commit f0021f6

2 files changed

Lines changed: 75 additions & 1 deletion

File tree

src/TaskManagement.Auth/Features/Authorization/Configuration/OpenIddictConfiguration.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,20 @@ private static IServiceCollection AddOpenIddictServices(IServiceCollection servi
5353
.SetIssuer(new Uri(openIddictSettings.Issuer))
5454
.SetUserInfoEndpointUris("connect/userinfo");
5555

56-
options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles);
56+
var configuredScopes = builder.Configuration
57+
.GetSection("ClientSettings:Clients")
58+
.Get<ClientSettingsOptions[]>()?
59+
.SelectMany(client => client.AllowedScopes)
60+
.Where(scope => !string.IsNullOrWhiteSpace(scope))
61+
.Distinct(StringComparer.OrdinalIgnoreCase)
62+
.ToArray() ?? [];
63+
64+
var registeredScopes = new[] { Scopes.Email, Scopes.Profile, Scopes.Roles }
65+
.Concat(configuredScopes)
66+
.Distinct(StringComparer.OrdinalIgnoreCase)
67+
.ToArray();
68+
69+
options.RegisterScopes(registeredScopes);
5770

5871
options.AllowAuthorizationCodeFlow();
5972

@@ -93,6 +106,7 @@ private static IServiceCollection AddOpenIddictServices(IServiceCollection servi
93106

94107
// Register the worker responsible for seeding the database.
95108
// Note: in a real world application, this step should be part of a setup script.
109+
services.AddHostedService<OpenIddictScopeSeeder>();
96110
services.AddHostedService<OpenIddictClientSeeder>();
97111

98112
return services;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using Microsoft.Extensions.Options;
2+
using OpenIddict.Abstractions;
3+
using TaskManagement.Auth.Infrastructure.Common.Settings;
4+
5+
namespace TaskManagement.Auth.Features.Authorization.Services
6+
{
7+
public class OpenIddictScopeSeeder : IHostedService
8+
{
9+
private readonly IServiceProvider _serviceProvider;
10+
private readonly ClientSettings _clientSettings;
11+
private readonly OpenIddictSettings _openIddictSettings;
12+
13+
public OpenIddictScopeSeeder(
14+
IServiceProvider serviceProvider,
15+
IOptions<ClientSettings> clientSettings,
16+
IOptions<OpenIddictSettings> openIddictSettings)
17+
{
18+
_serviceProvider = serviceProvider;
19+
_clientSettings = clientSettings.Value;
20+
_openIddictSettings = openIddictSettings.Value;
21+
}
22+
23+
public async Task StartAsync(CancellationToken cancellationToken)
24+
{
25+
await using var scope = _serviceProvider.CreateAsyncScope();
26+
var manager = scope.ServiceProvider.GetRequiredService<IOpenIddictScopeManager>();
27+
28+
var allowedScopes = _clientSettings.Clients
29+
.SelectMany(client => client.AllowedScopes)
30+
.Where(scopeName => !string.IsNullOrWhiteSpace(scopeName))
31+
.Distinct(StringComparer.OrdinalIgnoreCase)
32+
.ToList();
33+
34+
foreach (var scopeName in allowedScopes)
35+
{
36+
var descriptor = new OpenIddictScopeDescriptor
37+
{
38+
Name = scopeName,
39+
DisplayName = $"{scopeName} scope"
40+
};
41+
42+
if (!string.IsNullOrWhiteSpace(_openIddictSettings.Audience))
43+
{
44+
descriptor.Resources.Add(_openIddictSettings.Audience);
45+
}
46+
47+
var existingScope = await manager.FindByNameAsync(scopeName, cancellationToken);
48+
if (existingScope is null)
49+
{
50+
await manager.CreateAsync(descriptor, cancellationToken);
51+
continue;
52+
}
53+
54+
await manager.UpdateAsync(existingScope, descriptor, cancellationToken);
55+
}
56+
}
57+
58+
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
59+
}
60+
}

0 commit comments

Comments
 (0)