Last active
May 10, 2023 17:51
-
-
Save gterdem/6417a914722e715b9d7fb28e75eeee51 to your computer and use it in GitHub Desktop.
OpenIddictDataSeeder updated to OpenIddict for microservice template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using JetBrains.Annotations; | |
using Microsoft.Extensions.Configuration; | |
using Microsoft.Extensions.Localization; | |
using OpenIddict.Abstractions; | |
using Volo.Abp; | |
using Volo.Abp.Authorization.Permissions; | |
using Volo.Abp.Data; | |
using Volo.Abp.DependencyInjection; | |
using Volo.Abp.Identity; | |
using Volo.Abp.MultiTenancy; | |
using Volo.Abp.PermissionManagement; | |
using Volo.Abp.Uow; | |
namespace Acme.BookStore.IdentityService.DbMigrations; | |
public class IdentityServerDataSeeder : IDataSeedContributor, ITransientDependency | |
{ | |
private readonly IConfiguration _configuration; | |
private readonly ICurrentTenant _currentTenant; | |
private readonly IOpenIddictApplicationManager _applicationManager; | |
private readonly IOpenIddictScopeManager _scopeManager; | |
private readonly IPermissionDataSeeder _permissionDataSeeder; | |
private readonly IStringLocalizer<OpenIddictResponse> L; | |
public IdentityServerDataSeeder( | |
IConfiguration configuration, | |
ICurrentTenant currentTenant, | |
IOpenIddictApplicationManager applicationManager, | |
IOpenIddictScopeManager scopeManager, | |
IPermissionDataSeeder permissionDataSeeder, | |
IStringLocalizer<OpenIddictResponse> l) | |
{ | |
_configuration = configuration; | |
_currentTenant = currentTenant; | |
_applicationManager = applicationManager; | |
_scopeManager = scopeManager; | |
_permissionDataSeeder = permissionDataSeeder; | |
L = l; | |
} | |
public Task SeedAsync(DataSeedContext context) | |
{ | |
return SeedAsync(); | |
} | |
[UnitOfWork] | |
public virtual async Task SeedAsync() | |
{ | |
using (_currentTenant.Change(null)) | |
{ | |
await CreateApiScopesAsync(); | |
await CreateWebGatewaySwaggerClientsAsync(); | |
await CreateClientsAsync(); | |
} | |
} | |
private async Task CreateApiScopesAsync() | |
{ | |
await CreateScopesAsync("AccountService"); | |
await CreateScopesAsync("IdentityService"); | |
await CreateScopesAsync("AdministrationService"); | |
await CreateScopesAsync("SaasService"); | |
await CreateScopesAsync("ProductService"); | |
} | |
private async Task CreateWebGatewaySwaggerClientsAsync() | |
{ | |
await CreateSwaggerClientAsync("WebGateway", new[] { "AccountService", "IdentityService", "AdministrationService", "SaasService", "ProductService" }); | |
} | |
private async Task CreateSwaggerClientAsync(string name, string[] scopes = null) | |
{ | |
var commonScopes = new List<string> | |
{ | |
OpenIddictConstants.Permissions.Scopes.Address, | |
OpenIddictConstants.Permissions.Scopes.Email, | |
OpenIddictConstants.Permissions.Scopes.Phone, | |
OpenIddictConstants.Permissions.Scopes.Profile, | |
OpenIddictConstants.Permissions.Scopes.Roles | |
}; | |
scopes ??= new[] { name }; | |
// Swagger Client | |
var swaggerClientId = $"{name}_Swagger"; | |
if (!swaggerClientId.IsNullOrWhiteSpace()) | |
{ | |
var webGatewaySwaggerRootUrl = _configuration[$"OpenIddict:Applications:{name}:RootUrl"].TrimEnd('/'); | |
var publicWebGatewayRootUrl = _configuration[$"OpenIddict:Applications:PublicWebGateway:RootUrl"].TrimEnd('/'); | |
var accountServiceRootUrl = _configuration[$"OpenIddict:Resources:AccountService:RootUrl"].TrimEnd('/'); | |
var identityServiceRootUrl = _configuration[$"OpenIddict:Resources:IdentityService:RootUrl"].TrimEnd('/'); | |
var administrationServiceRootUrl = _configuration[$"OpenIddict:Resources:AdministrationService:RootUrl"].TrimEnd('/'); | |
var saasServiceRootUrl = _configuration[$"OpenIddict:Resources:SaasService:RootUrl"].TrimEnd('/'); | |
var productServiceRootUrl = _configuration[$"OpenIddict:Resources:ProductService:RootUrl"].TrimEnd('/'); | |
await CreateApplicationAsync( | |
name: swaggerClientId, | |
type: OpenIddictConstants.ClientTypes.Public, | |
consentType: OpenIddictConstants.ConsentTypes.Implicit, | |
displayName: "Swagger Client", | |
secret: null, | |
grantTypes: new List<string> | |
{ | |
OpenIddictConstants.GrantTypes.AuthorizationCode, | |
}, | |
scopes: commonScopes.Union(scopes).ToList(), | |
redirectUris: new List<string> { | |
$"{webGatewaySwaggerRootUrl}/swagger/oauth2-redirect.html", // WebGateway redirect uri | |
$"{publicWebGatewayRootUrl}/swagger/oauth2-redirect.html", // PublicWebGateway redirect uri | |
$"{accountServiceRootUrl}/swagger/oauth2-redirect.html", // AccountService redirect uri | |
$"{identityServiceRootUrl}/swagger/oauth2-redirect.html", // IdentityService redirect uri | |
$"{administrationServiceRootUrl}/swagger/oauth2-redirect.html", // AdministrationService redirect uri | |
$"{saasServiceRootUrl}/swagger/oauth2-redirect.html", // SaasService redirect uri | |
$"{productServiceRootUrl}/swagger/oauth2-redirect.html", // ProductService redirect uri | |
} | |
); | |
} | |
} | |
private async Task CreateScopesAsync(string name) | |
{ | |
if (await _scopeManager.FindByNameAsync(name) == null) | |
{ | |
await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor | |
{ | |
Name = name, | |
DisplayName = name + " API", | |
Resources = | |
{ | |
name | |
} | |
}); | |
} | |
} | |
private async Task CreateClientsAsync() | |
{ | |
var commonScopes = new List<string> | |
{ | |
OpenIddictConstants.Permissions.Scopes.Address, | |
OpenIddictConstants.Permissions.Scopes.Email, | |
OpenIddictConstants.Permissions.Scopes.Phone, | |
OpenIddictConstants.Permissions.Scopes.Profile, | |
OpenIddictConstants.Permissions.Scopes.Roles | |
}; | |
//Web Client | |
var webClientRootUrl = _configuration["OpenIddict:Applications:BookStore_Web:RootUrl"].EnsureEndsWith('/'); | |
await CreateApplicationAsync( | |
name: "BookStore_Web", | |
type: OpenIddictConstants.ClientTypes.Confidential, | |
consentType: OpenIddictConstants.ConsentTypes.Implicit, | |
displayName: "Web Client", | |
secret: "1q2w3e*", | |
grantTypes: new List<string> //Hybrid flow | |
{ | |
OpenIddictConstants.GrantTypes.AuthorizationCode, | |
OpenIddictConstants.GrantTypes.Implicit | |
}, | |
scopes: commonScopes.Union(new[] {"AccountService", "IdentityService", "AdministrationService", "SaasService", "ProductService"}).ToList(), | |
redirectUris: new List<string> { $"{webClientRootUrl}signin-oidc" }, | |
postLogoutRedirectUris: new List<string>() { $"{webClientRootUrl}signout-callback-oidc" } | |
); | |
//Blazor Client | |
var blazorClientRootUrl = _configuration["OpenIddict:Applications:BookStore_Blazor:RootUrl"].EnsureEndsWith('/'); | |
await CreateApplicationAsync( | |
name: "BookStore_Blazor", | |
type: OpenIddictConstants.ClientTypes.Public, | |
consentType: OpenIddictConstants.ConsentTypes.Implicit, | |
displayName: "Blazor Client", | |
secret: null, | |
grantTypes: new List<string> | |
{ | |
OpenIddictConstants.GrantTypes.AuthorizationCode | |
}, | |
scopes: commonScopes.Union(new[] {"AccountService", "IdentityService", "AdministrationService", "SaasService", "ProductService"}).ToList(), | |
redirectUris: new List<string> { $"{blazorClientRootUrl}authentication/login-callback" }, | |
postLogoutRedirectUris: new List<string> { $"{blazorClientRootUrl}authentication/logout-callback" } | |
); | |
//Blazor Server Client | |
var blazorServerClientRootUrl = _configuration["OpenIddict:Applications:BookStore_BlazorServer:RootUrl"].EnsureEndsWith('/'); | |
await CreateApplicationAsync( | |
name: "BookStore_BlazorServer", | |
type: OpenIddictConstants.ClientTypes.Confidential, | |
consentType: OpenIddictConstants.ConsentTypes.Implicit, | |
displayName: "Blazor Server Client", | |
secret: "1q2w3e*", | |
grantTypes: new List<string> //Hybrid flow | |
{ | |
OpenIddictConstants.GrantTypes.AuthorizationCode, | |
OpenIddictConstants.GrantTypes.Implicit | |
}, | |
scopes: commonScopes.Union(new[] {"AccountService", "IdentityService", "AdministrationService", "SaasService", "ProductService" }).ToList(), | |
redirectUris: new List<string> { $"{blazorServerClientRootUrl}signin-oidc" }, | |
postLogoutRedirectUris: new List<string> { $"{blazorServerClientRootUrl}signout-callback-oidc" } | |
); | |
//Public Web Client | |
var publicWebClientRootUrl = _configuration["OpenIddict:Applications:BookStore_PublicWeb:RootUrl"].EnsureEndsWith('/'); | |
await CreateApplicationAsync( | |
name: "BookStore_PublicWeb", | |
type: OpenIddictConstants.ClientTypes.Confidential, | |
consentType: OpenIddictConstants.ConsentTypes.Implicit, | |
displayName: "Public Web Client", | |
secret: "1q2w3e*", | |
grantTypes: new List<string> //Hybrid flow | |
{ | |
OpenIddictConstants.GrantTypes.AuthorizationCode, | |
OpenIddictConstants.GrantTypes.Implicit | |
}, | |
scopes: commonScopes.Union(new[] { "AccountService", "AdministrationService", "ProductService" }).ToList(), | |
redirectUris: new List<string> { $"{publicWebClientRootUrl}signin-oidc" }, | |
postLogoutRedirectUris: new List<string> { $"{publicWebClientRootUrl}signout-callback-oidc" } | |
); | |
//Angular Client | |
var angularClientRootUrl = _configuration["OpenIddict:Applications:BookStore_Angular:RootUrl"].TrimEnd('/'); | |
await CreateApplicationAsync( | |
name: "BookStore_Angular", | |
type: OpenIddictConstants.ClientTypes.Public, | |
consentType: OpenIddictConstants.ConsentTypes.Implicit, | |
displayName: "Angular Client", | |
secret: null, | |
grantTypes: new List<string> | |
{ | |
OpenIddictConstants.GrantTypes.AuthorizationCode, | |
OpenIddictConstants.GrantTypes.RefreshToken, | |
OpenIddictConstants.GrantTypes.Password, | |
"LinkLogin", | |
"Impersonation" | |
}, | |
scopes: commonScopes.Union(new[] {"AccountService", "IdentityService", "AdministrationService", "SaasService", "ProductService" }).ToList(), | |
redirectUris: new List<string> { $"{angularClientRootUrl}" }, | |
postLogoutRedirectUris: new List<string> { $"{angularClientRootUrl}" } | |
); | |
//Administration Service Client | |
await CreateApplicationAsync( | |
name: "BookStore_AdministrationService", | |
type: OpenIddictConstants.ClientTypes.Confidential, | |
consentType: OpenIddictConstants.ConsentTypes.Implicit, | |
displayName: "Administration Service Client", | |
secret: "1q2w3e*", | |
grantTypes: new List<string> | |
{ | |
OpenIddictConstants.GrantTypes.ClientCredentials | |
}, | |
scopes: commonScopes.Union(new[] { "IdentityService" }).ToList(), | |
permissions: new List<string> { IdentityPermissions.Users.Default } | |
); | |
} | |
private async Task CreateApplicationAsync( | |
[NotNull] string name, | |
[NotNull] string type, | |
[NotNull] string consentType, | |
string displayName, | |
string secret, | |
List<string> grantTypes, | |
List<string> scopes, | |
List<string> redirectUris = null, | |
List<string> postLogoutRedirectUris = null, | |
List<string> permissions = null) | |
{ | |
if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) | |
{ | |
throw new BusinessException(L["NoClientSecretCanBeSetForPublicApplications"]); | |
} | |
if (string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase)) | |
{ | |
throw new BusinessException(L["TheClientSecretIsRequiredForConfidentialApplications"]); | |
} | |
if (!string.IsNullOrEmpty(name) && await _applicationManager.FindByClientIdAsync(name) != null) | |
{ | |
return; | |
//throw new BusinessException(L["TheClientIdentifierIsAlreadyTakenByAnotherApplication"]); | |
} | |
var client = await _applicationManager.FindByClientIdAsync(name); | |
if (client == null) | |
{ | |
var application = new OpenIddictApplicationDescriptor | |
{ | |
ClientId = name, | |
Type = type, | |
ClientSecret = secret, | |
ConsentType = consentType, | |
DisplayName = displayName | |
}; | |
Check.NotNullOrEmpty(grantTypes, nameof(grantTypes)); | |
Check.NotNullOrEmpty(scopes, nameof(scopes)); | |
if (new [] { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.Implicit }.All(grantTypes.Contains)) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken); | |
if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken); | |
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeToken); | |
} | |
} | |
if (!redirectUris.IsNullOrEmpty() || !postLogoutRedirectUris.IsNullOrEmpty()) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Logout); | |
} | |
var buildInGrantTypes = new [] | |
{ | |
OpenIddictConstants.GrantTypes.Implicit, | |
OpenIddictConstants.GrantTypes.Password, | |
OpenIddictConstants.GrantTypes.AuthorizationCode, | |
OpenIddictConstants.GrantTypes.ClientCredentials, | |
OpenIddictConstants.GrantTypes.DeviceCode, | |
OpenIddictConstants.GrantTypes.RefreshToken | |
}; | |
foreach (var grantType in grantTypes) | |
{ | |
if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode); | |
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Code); | |
} | |
if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode || grantType == OpenIddictConstants.GrantTypes.Implicit) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Authorization); | |
} | |
if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode || | |
grantType == OpenIddictConstants.GrantTypes.ClientCredentials || | |
grantType == OpenIddictConstants.GrantTypes.Password || | |
grantType == OpenIddictConstants.GrantTypes.RefreshToken || | |
grantType == OpenIddictConstants.GrantTypes.DeviceCode) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token); | |
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Revocation); | |
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection); | |
} | |
if (grantType == OpenIddictConstants.GrantTypes.ClientCredentials) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials); | |
} | |
if (grantType == OpenIddictConstants.GrantTypes.Implicit) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Implicit); | |
} | |
if (grantType == OpenIddictConstants.GrantTypes.Password) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Password); | |
} | |
if (grantType == OpenIddictConstants.GrantTypes.RefreshToken) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.RefreshToken); | |
} | |
if (grantType == OpenIddictConstants.GrantTypes.Implicit) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdToken); | |
if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken); | |
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Token); | |
} | |
} | |
if (!buildInGrantTypes.Contains(grantType)) | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.GrantType + grantType); | |
} | |
} | |
var buildInScopes = new [] | |
{ | |
OpenIddictConstants.Permissions.Scopes.Address, | |
OpenIddictConstants.Permissions.Scopes.Email, | |
OpenIddictConstants.Permissions.Scopes.Phone, | |
OpenIddictConstants.Permissions.Scopes.Profile, | |
OpenIddictConstants.Permissions.Scopes.Roles | |
}; | |
foreach (var scope in scopes) | |
{ | |
if (buildInScopes.Contains(scope)) | |
{ | |
application.Permissions.Add(scope); | |
} | |
else | |
{ | |
application.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.Scope + scope); | |
} | |
} | |
if (!redirectUris.IsNullOrEmpty()) | |
{ | |
foreach (var redirectUri in redirectUris) | |
{ | |
if (!Uri.TryCreate(redirectUri, UriKind.Absolute, out var uri) || !uri.IsWellFormedOriginalString()) | |
{ | |
throw new BusinessException(L["InvalidRedirectUri", redirectUri]); | |
} | |
if (application.RedirectUris.All(x => x != uri)) | |
{ | |
application.RedirectUris.Add(uri); | |
} | |
} | |
} | |
if (!postLogoutRedirectUris.IsNullOrEmpty()) | |
{ | |
foreach (var postLogoutRedirectUri in postLogoutRedirectUris) | |
{ | |
if (!Uri.TryCreate(postLogoutRedirectUri, UriKind.Absolute, out var uri) || !uri.IsWellFormedOriginalString()) | |
{ | |
throw new BusinessException(L["InvalidPostLogoutRedirectUri", postLogoutRedirectUri]); | |
} | |
if (application.PostLogoutRedirectUris.All(x => x != uri)) | |
{ | |
application.PostLogoutRedirectUris.Add(uri); | |
} | |
} | |
} | |
if (permissions != null) | |
{ | |
await _permissionDataSeeder.SeedAsync( | |
ClientPermissionValueProvider.ProviderName, | |
name, | |
permissions, | |
null | |
); | |
} | |
await _applicationManager.CreateAsync(application); | |
} | |
} | |
} |
Hi @gterdem, there is one issue with this code.
CreateApplicationAsync
is missing the Introspection Endpoint Permission. This causes some issues with login timeouts in the UI. If you could please update theCreateApplicationAsync
method to match the ABP OpenId Upgrade sample code athttps://github.com/abpframework/abp-samples/blob/733bb038d7161be382e9824f9dc2f363bdd86aff/Ids2OpenId/src/Ids2OpenId.Domain/OpenIddict/OpenIddictDataSeedContributor.cs#LL261C38-L261C38
it could help others in the future.if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode || grantType == OpenIddictConstants.GrantTypes.ClientCredentials || grantType == OpenIddictConstants.GrantTypes.Password || grantType == OpenIddictConstants.GrantTypes.RefreshToken || grantType == OpenIddictConstants.GrantTypes.DeviceCode) { application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token); application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Revocation); application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection); //This line }
Thank you, I have updated it.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @gterdem, there is one issue with this code.
CreateApplicationAsync
is missing the Introspection Endpoint Permission. This causes some issues with login timeouts in the UI. If you could please update theCreateApplicationAsync
method to match the ABP OpenId Upgrade sample code athttps://github.com/abpframework/abp-samples/blob/733bb038d7161be382e9824f9dc2f363bdd86aff/Ids2OpenId/src/Ids2OpenId.Domain/OpenIddict/OpenIddictDataSeedContributor.cs#LL261C38-L261C38
it could help others in the future.