421.2 Abp microserices and Keycloak - chempkovsky/CS82ANGULAR GitHub Wiki
- Only security aspects will be discussed and implemented.
- In this article we will create an analogue of the
rupbes.tstapp.AuthServer-project- It will not work as Authorization server for the front app (
rupbes.tstapp.Web.Host) - But it will not work as Authentication server (Keycloak will be used as Authentication server)
- It will not work as Authorization server for the front app (
- We are currently using:
- MS Visual studio 22 (Community Edition)
- Volo.Abp.Studio.Cli v.1.1.1 Community Edition
- Volo.Abp.Cli v.9.2.3 Community Edition
- ABP Studio v.1.1.1 Community Edition
- Keycloak v.26.3.0
- Redis v=7.0.15
-
Start
ABP Studio -
Open
rupbes.tstapp-solution withABP Studio -
right click
Host-folder- select
Add/Package/New Package - In the dialog select
Http Api Host application - In the dialog enter package name =
rupbes.tstapp.Auth.Host - Open
rupbes.tstapp.Auth.Host.csproj-file and replace<RootNamespace>rupbes.tstapp.Auth.Host</RootNamespace>with<RootNamespace>rupbes.tstapp</RootNamespace> - In the files
Program.cs,HostHttpApiHostModule.csandControllers/HomeController.csmodify the namespace, i.e. replacerupbes.tstapp.Auth.Hostwithrupbes.tstapp. - build the project but do not run it
- select
-
here is
rupbes.tstapp.Auth.Host.csproj-file
Click to show the code
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>rupbes.tstapp</RootNamespace>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<CopyLocalLockFileAssemblies>True</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog.AspNetCore" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="9.2.3" />
<PackageReference Include="Volo.Abp.Autofac" Version="9.2.3" />
<PackageReference Include="Volo.Abp.AspNetCore.Serilog" Version="9.2.3" />
<PackageReference Include="Volo.Abp.Swashbuckle" Version="9.2.3" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Logs\**" />
<Content Remove="Logs\**" />
<EmbeddedResource Remove="Logs\**" />
<None Remove="Logs\**" />
</ItemGroup>
</Project>
- In the
rupbes.tstapp.Auth.Host.csproj-project addEntityFrameworkCore-folder - create
EntityFrameworkCore/tstappAuthHostMigrationsDbContext.cs-class (we name the class the same way as it is done for thetstappHttpApiHostMigrationsDbContext.csclass ofrupbes.tstapp.HttpApi.Host.csproj-project)- the code of the class is as follows:
Click to show the code
using Microsoft.EntityFrameworkCore;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
namespace rupbes.tstapp.EntityFrameworkCore
{
public class tstappAuthHostMigrationsDbContext : AbpDbContext<tstappAuthHostMigrationsDbContext>
{
public tstappAuthHostMigrationsDbContext(DbContextOptions<tstappAuthHostMigrationsDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ConfigurePermissionManagement();
modelBuilder.ConfigureSettingManagement();
modelBuilder.ConfigureAuditLogging();
modelBuilder.ConfigureIdentity();
modelBuilder.ConfigureFeatureManagement();
modelBuilder.ConfigureTenantManagement();
}
}
}- it requires modification of the
rupbes.tstapp.Auth.Host.csproj-file. All new package-refs we add in the separete<ItemGroup>:
Click to show the code
<ItemGroup>
<PackageReference Include="Volo.Abp.EntityFrameworkCore.SqlServer" Version="9.2.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Volo.Abp.SettingManagement.EntityFrameworkCore" Version="9.2.3" />
<PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" Version="9.2.3" />
<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" Version="9.2.3" />
<PackageReference Include="Volo.Abp.FeatureManagement.EntityFrameworkCore" Version="9.2.3" />
<PackageReference Include="Volo.Abp.TenantManagement.EntityFrameworkCore" Version="9.2.3" />
<PackageReference Include="Volo.Abp.AuditLogging.EntityFrameworkCore" Version="9.2.3" />
</ItemGroup> - create
EntityFrameworkCore/tstappAuthHostMigrationsDbContextFactory.cs-class (we name the class the same way as it is done for thetstappHttpApiHostMigrationsDbContextFactory.csclass ofrupbes.tstapp.HttpApi.Host.csproj-project)- the code of the class is as follows:
Click to show the code
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using System.IO;
namespace rupbes.tstapp.EntityFrameworkCore
{
public class tstappAuthHostMigrationsDbContextFactory : IDesignTimeDbContextFactory<tstappAuthHostMigrationsDbContext>
{
public tstappAuthHostMigrationsDbContext CreateDbContext(string[] args)
{
var configuration = BuildConfiguration();
var builder = new DbContextOptionsBuilder<tstappAuthHostMigrationsDbContext>()
.UseSqlServer(configuration.GetConnectionString("Default"));
return new tstappAuthHostMigrationsDbContext(builder.Options);
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false);
return builder.Build();
}
}
}- it does not require modification of the
rupbes.tstapp.Auth.Host.csproj-file. - now we near to use
ef migrations(leftappsettings.json):
dotnet ef migrations add Initial
dotnet ef database update
-
read this first
- https://abp.io/support/questions/6448/Issues-Replacing-PermissionDataSeedContributor
- https://github.com/abpframework/abp/blob/dev/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDataSeedContributor.cs
- https://abp.io/docs/latest/framework/infrastructure/data-seeding#idataseedcontributor
-
We can not use default
IdentityDataSeeder, as we need to insertIdentityUserwith the initially definedUserId. (UserIdwill be copied from the Kaycloak) -
In the
rupbes.tstapp.Auth.Host.csproj-project addData-folder -
Add
Data/ICustomIdentityDataSeeder.cs-interface
Click to show the code
using JetBrains.Annotations;
using System;
using System.Threading.Tasks;
using Volo.Abp.Identity;
namespace rupbes.tstapp.Data
{
public interface ICustomIdentityDataSeeder
{
Task<IdentityDataSeedResult> SeedAsync(
[NotNull] string adminEmail,
[NotNull] string adminPassword,
Guid? adminId = null,
Guid? tenantId = null,
string? adminUserName = null);
}
}- Add
Data/CustomIdentityDataSeeder.cs-class
Click to show the code
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
using JetBrains.Annotations;
using IdentityRole = Volo.Abp.Identity.IdentityRole;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
namespace rupbes.tstapp.Data
{
public class CustomIdentityDataSeeder : ITransientDependency, ICustomIdentityDataSeeder
{
protected IGuidGenerator GuidGenerator { get; }
protected IIdentityRoleRepository RoleRepository { get; }
protected IIdentityUserRepository UserRepository { get; }
protected ILookupNormalizer LookupNormalizer { get; }
protected IdentityUserManager UserManager { get; }
protected IdentityRoleManager RoleManager { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IOptions<IdentityOptions> IdentityOptions { get; }
public CustomIdentityDataSeeder(
IGuidGenerator guidGenerator,
IIdentityRoleRepository roleRepository,
IIdentityUserRepository userRepository,
ILookupNormalizer lookupNormalizer,
IdentityUserManager userManager,
IdentityRoleManager roleManager,
ICurrentTenant currentTenant,
IOptions<IdentityOptions> identityOptions)
{
GuidGenerator = guidGenerator;
RoleRepository = roleRepository;
UserRepository = userRepository;
LookupNormalizer = lookupNormalizer;
UserManager = userManager;
RoleManager = roleManager;
CurrentTenant = currentTenant;
IdentityOptions = identityOptions;
}
[UnitOfWork]
public virtual async Task<IdentityDataSeedResult> SeedAsync(
[NotNull] string adminEmail,
[NotNull] string adminPassword,
Guid? adminId,
Guid? tenantId = null,
string? adminUserName = null)
{
Check.NotNullOrWhiteSpace(adminEmail, nameof(adminEmail));
Check.NotNullOrWhiteSpace(adminPassword, nameof(adminPassword));
using (CurrentTenant.Change(tenantId))
{
await IdentityOptions.SetAsync();
var result = new IdentityDataSeedResult();
//"admin" user
if (adminUserName.IsNullOrWhiteSpace())
{
adminUserName = IdentityDataSeedContributor.AdminUserNameDefaultValue;
}
var adminUser = await UserRepository.FindByNormalizedUserNameAsync(
LookupNormalizer.NormalizeName(adminUserName)
);
if (adminUser != null)
{
return result;
}
var adminIdEx =
adminId.HasValue
?
((adminId.Value == Guid.Empty) ? GuidGenerator.Create() : adminId.Value)
:
GuidGenerator.Create();
adminUser = new IdentityUser(
adminIdEx,
adminUserName,
adminEmail,
tenantId
)
{
Name = adminUserName
};
(await UserManager.CreateAsync(adminUser, adminPassword, validatePassword: false)).CheckErrors();
result.CreatedAdminUser = true;
//"admin" role
const string adminRoleName = "admin";
var adminRole =
await RoleRepository.FindByNormalizedNameAsync(LookupNormalizer.NormalizeName(adminRoleName));
if (adminRole == null)
{
adminRole = new IdentityRole(
GuidGenerator.Create(),
adminRoleName,
tenantId
)
{
IsStatic = true,
IsPublic = true
};
(await RoleManager.CreateAsync(adminRole)).CheckErrors();
result.CreatedAdminRole = true;
}
(await UserManager.AddToRoleAsync(adminUser, adminRoleName)).CheckErrors();
return result;
}
}
}
}Add Data/CustomIdentityDataSeedContributor.cs-class
Click to show the code
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace rupbes.tstapp.Data
{
// https://abp.io/support/questions/6448/Issues-Replacing-PermissionDataSeedContributor
// https://github.com/abpframework/abp/blob/dev/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDataSeedContributor.cs
// https://abp.io/docs/latest/framework/infrastructure/data-seeding#idataseedcontributor
/*
public override void ConfigureServices(ServiceConfigurationContext context)
{
PostConfigure<AbpDataSeedOptions>(options =>
{
options.Contributors.RemoveAll(x => x == typeof(IdentityDataSeedContributor));
});
}
public class YourIdentityDataSeedContributor : IDataSeedContributor, ITransientDependency
{
}
*/
// [Dependency(ReplaceServices = true)]
// [ExposeServices(typeof(CustomIdentityDataSeedContributor), typeof(IdentityDataSeedContributor), typeof(IDataSeedContributor))]
// public class CustomIdentityDataSeedContributor: IdentityDataSeedContributor
public class CustomIdentityDataSeedContributor : IDataSeedContributor, ITransientDependency
{
public const string AdminIdPropertyName = "AdminId";
public const string AdminEmailPropertyName = "AdminEmail";
public const string AdminEmailDefaultValue = "[email protected]";
public const string AdminUserNamePropertyName = "AdminUserName";
public const string AdminUserNameDefaultValue = "admin";
public const string AdminPasswordPropertyName = "AdminPassword";
public const string AdminPasswordDefaultValue = "1q2w3E*";
protected ICustomIdentityDataSeeder IdentityDataSeeder { get; }
public CustomIdentityDataSeedContributor(ICustomIdentityDataSeeder identityDataSeeder)
{
IdentityDataSeeder = identityDataSeeder;
}
public Task SeedAsync(DataSeedContext context)
{
return IdentityDataSeeder.SeedAsync(
context?[AdminEmailPropertyName] as string ?? AdminEmailDefaultValue,
context?[AdminPasswordPropertyName] as string ?? AdminPasswordDefaultValue,
context?[AdminIdPropertyName] as Guid?,
context?.TenantId,
context?[AdminUserNamePropertyName] as string ?? AdminUserNameDefaultValue
);
}
}
}- We need the transformation from the keycloak roles notation into Abp role notation.
- In the
rupbes.tstapp.Auth.Host.csproj-project addClmsTransform-folder - Add
ClmsTransform/CustomClaimsTransformation.cs-class- "HOST" is a name of the host
- other name is a tenant name
Click to show the code
using Microsoft.AspNetCore.Authentication;
using System.Collections.Generic;
using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
namespace rupbes.tstapp.ClmsTransform
{
public class CustomClaimsTransformation : IClaimsTransformation
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
Claim? clm = principal.FindFirst(claim => claim.Type == "resource_access");
if (clm != null)
{
string val = clm.Value;
JsonNode? jsn01 = JsonObject.Parse(val);
if ((jsn01 != null) && (jsn01.GetValueKind() == JsonValueKind.Object))
{
HashSet<string> hs = new HashSet<string>();
JsonObject jso01 = jsn01.AsObject();
foreach (var kv in jso01)
{
string appName = kv.Key;
JsonNode? jsn02 = kv.Value;
if ((jsn02 != null) && (jsn02.GetValueKind() == JsonValueKind.Object))
{
JsonObject jso02 = jsn02.AsObject();
foreach (var kv2 in jso02)
{
if (kv2.Key == "roles")
{
JsonNode? jsn03 = kv2.Value;
if ((jsn03 != null) && (jsn03.GetValueKind() == JsonValueKind.Array))
{
JsonArray roles = jsn03.AsArray();
foreach (var role in roles)
{
if (role != null)
{
if (role.GetValueKind() == JsonValueKind.String)
{
string roleName = role.ToJsonString();
if (roleName != null)
{
hs.Add(roleName.Trim('"'));
}
}
}
}
}
}
}
}
}
if (hs.Count > 0)
{
ClaimsIdentity claimsIdentity = new ClaimsIdentity();
foreach (var role in hs)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role));
}
principal.AddIdentity(claimsIdentity);
}
}
}
return Task.FromResult(principal);
}
}
}- we nedd to transform keycloak
tenantidclaim into TenantId of the current user - In the
rupbes.tstapp.Auth.Host.csproj-project addTenantResolver-folder - Add
TenantResolver/CustomTenantResolveContributor.cs-class
Click to show the code
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Security.Claims;
using Volo.Abp.Users;
using System.Linq;
namespace rupbes.tstapp.TenantResolver
{
public class CustomTenantResolveContributor : CurrentUserTenantResolveContributor
{
public override Task ResolveAsync(ITenantResolveContext context)
{
// A tenant resolver should set "context.TenantIdOrName" if it can determine it.If not,
// just leave it as is to allow the next resolver to determine it.
// "context.ServiceProvider" can be used if you need to additional services to resolve
// from the dependency injection system.
var currentUser = context.ServiceProvider.GetRequiredService<ICurrentUser>();
if (currentUser.IsAuthenticated)
{
var accessor = context.ServiceProvider.GetRequiredService<ICurrentPrincipalAccessor>();
Claim? cl = accessor.Principal.Claims.FirstOrDefault(claim => claim.Type == "tenantid");
if (cl != null)
{
//Guid guid = Guid.NewGuid();
//string tnnt = guid.ToString();
string tnnt = cl.Value;
if (!(String.IsNullOrEmpty(tnnt) || "HOST".Equals(tnnt, StringComparison.OrdinalIgnoreCase)))
{
context.TenantIdOrName = cl.Value; // currentUser.TenantId?.ToString();
}
context.Handled = true;
}
}
return Task.CompletedTask;
}
}
}- modify
HostHttpApiHostModule.cs-file ofrupbes.tstapp.Auth.Host.csproj-project as follows:
Click to show the code
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using rupbes.tstapp.ClmsTransform;
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.Autofac;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.SqlServer;
using Volo.Abp.FeatureManagement;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Identity;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.HttpApi;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.SettingManagement;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.Swashbuckle;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Protocols;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using StackExchange.Redis;
using Volo.Abp.Data;
using rupbes.tstapp.TenantResolver;
using rupbes.tstapp.MultiTenancy;
using Microsoft.AspNetCore.DataProtection;
namespace rupbes.tstapp;
[DependsOn(
typeof(AbpAspNetCoreMvcUiMultiTenancyModule),
typeof(AbpAuditLoggingEntityFrameworkCoreModule),
typeof(AbpCachingStackExchangeRedisModule),
typeof(AbpEntityFrameworkCoreSqlServerModule),
typeof(AbpIdentityEntityFrameworkCoreModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpIdentityHttpApiModule),
typeof(AbpPermissionManagementDomainIdentityModule), // required for Permission administration
typeof(AbpPermissionManagementEntityFrameworkCoreModule),
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpPermissionManagementHttpApiModule),
typeof(AbpSettingManagementEntityFrameworkCoreModule),
typeof(AbpSettingManagementApplicationModule),
typeof(AbpSettingManagementHttpApiModule),
typeof(AbpFeatureManagementEntityFrameworkCoreModule),
typeof(AbpFeatureManagementApplicationModule),
typeof(AbpFeatureManagementHttpApiModule),
typeof(AbpTenantManagementEntityFrameworkCoreModule),
typeof(AbpTenantManagementApplicationModule),
typeof(AbpTenantManagementHttpApiModule),
typeof(AbpAutofacModule),
// typeof(AbpAspNetCoreMvcModule)
typeof(AbpAspNetCoreSerilogModule),
typeof(AbpSwashbuckleModule)
)]
public class HostHttpApiHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration();
Configure<AbpDbContextOptions>(options =>
{
options.UseSqlServer();
});
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = MultiTenancyConsts.IsEnabled;
});
context.Services.AddAbpSwaggerGenWithOAuth(
configuration["AuthServer:Authority"]!,
new Dictionary<string, string>
{
{"rappname", "rappname API"}
},
options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "rappname DefHttpApi API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
Configure<AbpLocalizationOptions>(options =>
{
options.Languages.Add(new LanguageInfo("ar", "ar", "العربية"));
options.Languages.Add(new LanguageInfo("cs", "cs", "Čeština"));
options.Languages.Add(new LanguageInfo("en", "en", "English"));
options.Languages.Add(new LanguageInfo("en-GB", "en-GB", "English (UK)"));
options.Languages.Add(new LanguageInfo("fi", "fi", "Finnish"));
options.Languages.Add(new LanguageInfo("fr", "fr", "Français"));
options.Languages.Add(new LanguageInfo("hi", "hi", "Hindi"));
options.Languages.Add(new LanguageInfo("is", "is", "Icelandic"));
options.Languages.Add(new LanguageInfo("it", "it", "Italiano"));
options.Languages.Add(new LanguageInfo("hu", "hu", "Magyar"));
options.Languages.Add(new LanguageInfo("pt-BR", "pt-BR", "Português"));
options.Languages.Add(new LanguageInfo("ro-RO", "ro-RO", "Română"));
options.Languages.Add(new LanguageInfo("ru", "ru", "Русский"));
options.Languages.Add(new LanguageInfo("sk", "sk", "Slovak"));
options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe"));
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
options.Languages.Add(new LanguageInfo("zh-Hant", "zh-Hant", "繁體中文"));
options.Languages.Add(new LanguageInfo("de-DE", "de-DE", "Deutsch"));
options.Languages.Add(new LanguageInfo("es", "es", "Español"));
options.Languages.Add(new LanguageInfo("el", "el", "Ελληνικά"));
});
context.Services.AddTransient<IClaimsTransformation, CustomClaimsTransformation>();
// add keycloak signing schemas
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
configuration["AuthServer:MetaAddress"],
new OpenIdConnectConfigurationRetriever(),
new HttpDocumentRetriever());
var discoveryDocument = System.Threading.Tasks.Task.Run(async () => await configurationManager.GetConfigurationAsync()).Result;
var signingKeys = discoveryDocument.SigningKeys;
//
// Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler
// will be used for Authentication
//
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddAbpJwtBearer(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = configuration.GetValue<bool>("AuthServer:RequireHttpsMetadata");
options.Audience = configuration["AuthServer:Audience"]; // modified
options.TokenValidationParameters.ValidateIssuer = true; // added
options.TokenValidationParameters.ValidateAudience = true; // added
options.TokenValidationParameters.ValidateLifetime = true; // added
options.TokenValidationParameters.ValidateTokenReplay = true; // added
options.TokenValidationParameters.ValidIssuer = configuration["AuthServer:Issuer"]; // added
// == OR ==
options.TokenValidationParameters.IssuerSigningKeys = signingKeys; // added
// == OR ==
// options.MetadataAddress = configuration["AuthServer:MetaAddress"]; // added
/* START: lines added */
options.Events ??= new JwtBearerEvents(); // added
options.Events.OnTokenValidated = cntxt => // temporarily for getting debug into about returned access_token
{
if (cntxt.Principal?.Identity == null)
return Task.CompletedTask;
else
return Task.CompletedTask;
};
options.Events.OnMessageReceived = cntxt => // temporarily for getting debug into about if the client connects
{
return Task.CompletedTask;
};
options.Events.OnAuthenticationFailed = cntxt => // temporarily for getting debug into about AuthenticationFailed
{
return Task.CompletedTask;
};
/* END: lines added */
//options.ClaimActions.MapUniqueJsonKey
});
Configure<AbpDistributedCacheOptions>(options =>
{
options.KeyPrefix = "tstapp:";
});
var dataProtectionBuilder = context.Services.AddDataProtection().SetApplicationName("rappname");
if (!hostingEnvironment.IsDevelopment())
{
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!);
dataProtectionBuilder.PersistKeysToStackExchangeRedis(redis, "rappname-Protection-Keys");
}
context.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder
.WithOrigins(
configuration["App:CorsOrigins"]?
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray() ?? Array.Empty<string>()
)
.WithAbpExposedHeaders()
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
PostConfigure<AbpDataSeedOptions>(options =>
{
options.Contributors.RemoveAll(x => x == typeof(IdentityDataSeedContributor));
});
Configure<AbpTenantResolveOptions>(options =>
{
options.TenantResolvers.Clear();
options.TenantResolvers.Add(new CustomTenantResolveContributor());
});
}
public async override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseCorrelationId();
app.MapAbpStaticAssets();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
if (MultiTenancyConsts.IsEnabled)
{
app.UseMultiTenancy();
}
app.UseAbpRequestLocalization();
app.UseAuthorization();
app.UseSwagger();
app.UseAbpSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support APP API");
var configuration = context.GetConfiguration();
options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
options.OAuthScopes("rappname");
});
app.UseAuditing();
app.UseAbpSerilogEnrichers();
app.UseConfiguredEndpoints();
await SeedData(context);
}
private async Task SeedData(ApplicationInitializationContext context)
{
using (var scope = context.ServiceProvider.CreateScope())
{
var seeder = scope.ServiceProvider
.GetRequiredService<IDataSeeder>();
await seeder.SeedAsync(
new DataSeedContext()
.WithProperty("AdminId", new Guid("0953d853-d063-41ab-8635-08b92ef81869"))
.WithProperty("AdminEmail", "[email protected]")
.WithProperty("AdminUserName", "testuser")
// .WithProperty("AdminPassword", "1q2w3E*")
);
}
}
}- it requires to change
rupbes.tstapp.Auth.Host.csproj-file. All new package-refs we add in the separete :
Click to show the code
<ItemGroup>
<PackageReference Include="Volo.Abp.EntityFrameworkCore.SqlServer" Version="9.2.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Volo.Abp.SettingManagement.EntityFrameworkCore" Version="9.2.3" />
<PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" Version="9.2.3" />
<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" Version="9.2.3" />
<PackageReference Include="Volo.Abp.FeatureManagement.EntityFrameworkCore" Version="9.2.3" />
<PackageReference Include="Volo.Abp.TenantManagement.EntityFrameworkCore" Version="9.2.3" />
<PackageReference Include="Volo.Abp.AuditLogging.EntityFrameworkCore" Version="9.2.3" />
<PackageReference Include="Volo.Abp.PermissionManagement.Application" Version="9.2.3" />
<PackageReference Include="Volo.Abp.PermissionManagement.HttpApi" Version="9.2.3" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.Identity" Version="9.2.3" />
<PackageReference Include="Volo.Abp.FeatureManagement.Application" Version="9.2.3" />
<PackageReference Include="Volo.Abp.FeatureManagement.HttpApi" Version="9.2.3" />
<PackageReference Include="Volo.Abp.TenantManagement.Application" Version="9.2.3" />
<PackageReference Include="Volo.Abp.TenantManagement.HttpApi" Version="9.2.3" />
<PackageReference Include="Volo.Abp.SettingManagement.Application" Version="9.2.3" />
<PackageReference Include="Volo.Abp.SettingManagement.HttpApi" Version="9.2.3" />
<PackageReference Include="Volo.Abp.Account.Application" Version="9.2.3" />
<PackageReference Include="Volo.Abp.Account.HttpApi" Version="9.2.3" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" Version="9.2.3" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy" Version="9.2.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
<PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" Version="9.2.3" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.4" />
<PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="9.2.3" />
<ProjectReference Include="..\..\src\rupbes.tstapp.Application.Contracts\rupbes.tstapp.Application.Contracts.csproj" />
<ProjectReference Include="..\rupbes.tstapp.Host.Shared\rupbes.tstapp.Host.Shared.csproj" />
</ItemGroup> - The next step is to configure Keycloak for new applications:
- realm
- user
- apps
- roles
- user properties