Extract IDP configuration to external JSON file #1

Merged
administrator merged 1 commits from claude/update-set-up-logic into main 2026-03-22 11:26:31 +00:00
3 changed files with 194 additions and 87 deletions
+125 -87
View File
@@ -1,9 +1,10 @@
// Copyright (c) SimpleIdServer. All rights reserved. // Copyright (c) SimpleIdServer. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Text.Json;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using SimpleIdServer.IdServer.Builders; using SimpleIdServer.IdServer.Builders;
@@ -14,83 +15,48 @@ using SimpleIdServer.IdServer.Stores;
var corsPolicyName = "AllowAll"; var corsPolicyName = "AllowAll";
// ── Load configuration ────────────────────────────────────────────────────────
var configPath = Path.Combine(AppContext.BaseDirectory, "config", "idp-config.json");
if (!File.Exists(configPath))
throw new FileNotFoundException(
$"IDP configuration file not found at '{configPath}'. " +
"Mount your config file to /app/config/idp-config.json when running in Docker.");
var idpConfig = JsonSerializer.Deserialize<IdpConfig>(
File.ReadAllText(configPath),
new JsonSerializerOptions { PropertyNameCaseInsensitive = true })
?? throw new InvalidOperationException("Failed to deserialize IDP configuration.");
// ── Build realm ───────────────────────────────────────────────────────────────
var realm = RealmBuilder.CreateMaster().Build(); var realm = RealmBuilder.CreateMaster().Build();
//api.Audience = "urn:bighand:api:bi:portal"; // ── Build API resource ────────────────────────────────────────────────────────
var users = new List<User>
{
UserBuilder
.Create("administrator", "password", "Administrator")
.SetEmail("adm@mail.com")
.SetFirstname("Administrator")
.AddRole("BI.PORTAL_ADMIN")
.AddRole("BI.TENANT_ADMIN")
.AddClaim("tid", "cbaa13c2-e95b-470a-bbcb-18911d5a6025")
.AddClaim("aud","urn:bighand:api:bi:portal")
.AddConsent("master","212C9DB96C2A4B6DA0AFDB2222F6EEAA.bighand.com","bi.portal")
.SetEmailVerified(true)
.Build(),
};
var rUser = new RealmUser
{
Realm = realm,
User = users[0],
};
users[0].Realms.Add(rUser);
var api = new ApiResource var api = new ApiResource
{ {
Realms = { realm }, Realms = { realm },
Name = "BI Portal API", Name = idpConfig.Api.Name,
Audience = "urn:bighand:api:bi:portal" Audience = idpConfig.Api.Audience
}; };
//var scopes = new List<Scope> { ScopeBuilder.CreateRoleScope(clients[0], "bi.portal", "").Build() }; // ── Build custom scope with claim mappers ─────────────────────────────────────
var biScope = new Scope() var biScope = new Scope
{ {
Realms = { realm }, Realms = { realm },
ApiResources = { api }, ApiResources = { api },
Protocol = ScopeProtocols.OAUTH, Protocol = ScopeProtocols.OAUTH,
Type = ScopeTypes.APIRESOURCE, Type = ScopeTypes.APIRESOURCE,
Name = "bi.portal", Name = idpConfig.Scope.Name,
Description = "BI Portal Scope", Description = idpConfig.Scope.Description,
ClaimMappers = ClaimMappers = idpConfig.Scope.ClaimMappers.Select(cm => new ScopeClaimMapper
{ {
new ScopeClaimMapper() IncludeInAccessToken = true,
{ TokenClaimJsonType = Enum.Parse<TokenClaimJsonTypes>(cm.TokenClaimJsonType, ignoreCase: true),
IncludeInAccessToken = true, TargetClaimPath = cm.TargetClaimPath,
TokenClaimJsonType = TokenClaimJsonTypes.STRING, MapperType = MappingRuleTypes.USERATTRIBUTE,
TargetClaimPath = "roles", IsMultiValued = cm.IsMultiValued,
MapperType = MappingRuleTypes.USERATTRIBUTE, SourceUserAttribute = cm.SourceUserAttribute,
IsMultiValued=true, SourceUserProperty = cm.SourceUserAttribute,
SourceUserAttribute = "role", }).ToList(),
SourceUserProperty = "role",
},
new ScopeClaimMapper()
{
IncludeInAccessToken = true,
TokenClaimJsonType = TokenClaimJsonTypes.STRING,
TargetClaimPath = "tid",
MapperType = MappingRuleTypes.USERATTRIBUTE,
SourceUserAttribute = "tid",
SourceUserProperty = "tid",
},
new ScopeClaimMapper()
{
IncludeInAccessToken = true,
TokenClaimJsonType = TokenClaimJsonTypes.STRING,
TargetClaimPath = "upn",
MapperType = MappingRuleTypes.USERATTRIBUTE,
SourceUserAttribute = "email",
SourceUserProperty = "email",
},
},
}; };
api.Scopes.Add(biScope); api.Scopes.Add(biScope);
@@ -99,41 +65,69 @@ api.Realms.Add(realm);
var scopes = new List<Scope> var scopes = new List<Scope>
{ {
new Scope("openid") { Realms = { realm } }, new Scope("openid") { Realms = { realm } },
new Scope("profile"){ Realms = { realm } }, new Scope("profile") { Realms = { realm } },
new Scope("offline_access"){ Realms = { realm } }, new Scope("offline_access") { Realms = { realm } },
biScope biScope
}; };
var clients = new List<Client>
// ── Build client ──────────────────────────────────────────────────────────────
var client = ClientBuilder
.BuildUserAgentClient(
idpConfig.Client.ClientId,
null,
realm,
idpConfig.Client.RedirectUris.ToArray())
.SetClientName(idpConfig.Client.Name)
.AddScope(scopes.ToArray())
.AddRefreshToken()
.Build();
client.IsPublic = idpConfig.Client.IsPublic;
client.Realms.Add(realm);
var clients = new List<Client> { client };
// ── Build users ───────────────────────────────────────────────────────────────
var users = idpConfig.Users.Select(u =>
{ {
ClientBuilder var userBuilder = UserBuilder
.BuildUserAgentClient("212C9DB96C2A4B6DA0AFDB2222F6EEAA.bighand.com", null, realm, new[] { "http://localhost:4200/loggedin" }) .Create(u.Login, u.Password, u.Name)
.SetClientName("BI Portal") .SetEmail(u.Email)
.AddScope(scopes.ToArray()) .SetFirstname(u.Firstname)
.AddRefreshToken() .SetEmailVerified(u.EmailVerified);
.Build(),
};
foreach (var role in u.Roles)
userBuilder = userBuilder.AddRole(role);
clients[0].IsPublic = true; foreach (var claim in u.Claims)
clients[0].Realms.Add(realm); userBuilder = userBuilder.AddClaim(claim.Key, claim.Value);
realm.Clients.Add(clients[0]); foreach (var consent in u.Consents)
userBuilder = userBuilder.AddConsent(consent.Realm, consent.ClientId, consent.Scope);
return userBuilder.Build();
}).ToList();
// ── Wire up realm relationships ───────────────────────────────────────────────
foreach (var user in users)
{
var rUser = new RealmUser { Realm = realm, User = user };
user.Realms.Add(rUser);
realm.Users.Add(rUser);
}
realm.Clients.Add(client);
realm.ApiResources.Add(api); realm.ApiResources.Add(api);
realm.Users.Add(rUser);
scopes.ForEach(s => realm.Scopes.Add(s)); scopes.ForEach(s => realm.Scopes.Add(s));
// ── Configure services ────────────────────────────────────────────────────────
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options => builder.Services.AddCors(options =>
{ {
options.AddPolicy( options.AddPolicy(
name: corsPolicyName, name: corsPolicyName,
policy => policy => policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
{
policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
}
);
}); });
builder builder
@@ -146,7 +140,6 @@ builder
.AddInMemoryLanguages(DefaultLanguages.All) .AddInMemoryLanguages(DefaultLanguages.All)
.AddPwdAuthentication(true); .AddPwdAuthentication(true);
var app = builder.Build(); var app = builder.Build();
using (var sc = app.Services.CreateScope()) using (var sc = app.Services.CreateScope())
@@ -160,3 +153,48 @@ app.UseSid();
app.UseCors(corsPolicyName); app.UseCors(corsPolicyName);
await app.RunAsync(); await app.RunAsync();
// ── Configuration model ───────────────────────────────────────────────────────
record IdpConfig(
ApiConfig Api,
ScopeConfig Scope,
ClientConfig Client,
List<UserConfig> Users);
record ApiConfig(
string Name,
string Audience);
record ScopeConfig(
string Name,
string Description,
List<ClaimMapperConfig> ClaimMappers);
record ClaimMapperConfig(
string TargetClaimPath,
string SourceUserAttribute,
string TokenClaimJsonType,
bool IsMultiValued = false);
record ClientConfig(
string ClientId,
string Name,
List<string> RedirectUris,
bool IsPublic = true);
record UserConfig(
string Login,
string Password,
string Name,
string Email,
string Firstname,
bool EmailVerified,
List<string> Roles,
Dictionary<string, string> Claims,
List<ConsentConfig> Consents);
record ConsentConfig(
string Realm,
string ClientId,
string Scope);
+5
View File
@@ -10,6 +10,11 @@
<PackageReference Include="SimpleIdServer.IdServer" Version="6.0.*-*" /> <PackageReference Include="SimpleIdServer.IdServer" Version="6.0.*-*" />
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" /> <PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="config\idp-config.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Resources\AccountsResource.Designer.cs"> <Compile Update="Resources\AccountsResource.Designer.cs">
<DependentUpon>AccountsResource.resx</DependentUpon> <DependentUpon>AccountsResource.resx</DependentUpon>
+64
View File
@@ -0,0 +1,64 @@
{
"realm": "master",
"api": {
"name": "BI Portal API",
"audience": "urn:bighand:api:bi:portal"
},
"scope": {
"name": "bi.portal",
"description": "BI Portal Scope",
"claimMappers": [
{
"targetClaimPath": "roles",
"sourceUserAttribute": "role",
"tokenClaimJsonType": "STRING",
"isMultiValued": true
},
{
"targetClaimPath": "tid",
"sourceUserAttribute": "tid",
"tokenClaimJsonType": "STRING",
"isMultiValued": false
},
{
"targetClaimPath": "upn",
"sourceUserAttribute": "email",
"tokenClaimJsonType": "STRING",
"isMultiValued": false
}
]
},
"client": {
"clientId": "212C9DB96C2A4B6DA0AFDB2222F6EEAA.bighand.com",
"name": "BI Portal",
"redirectUris": [
"http://localhost:4200/loggedin"
],
"isPublic": true
},
"users": [
{
"login": "administrator",
"password": "password",
"name": "Administrator",
"email": "adm@mail.com",
"firstname": "Administrator",
"emailVerified": true,
"roles": [
"BI.PORTAL_ADMIN",
"BI.TENANT_ADMIN"
],
"claims": {
"tid": "cbaa13c2-e95b-470a-bbcb-18911d5a6025",
"aud": "urn:bighand:api:bi:portal"
},
"consents": [
{
"realm": "master",
"clientId": "212C9DB96C2A4B6DA0AFDB2222F6EEAA.bighand.com",
"scope": "bi.portal"
}
]
}
]
}