From 45c375bc24ada89e5fe3100d6facf069d6895c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BChler?= Date: Fri, 14 Apr 2023 10:34:49 +0200 Subject: [PATCH] fix(introspection): parse roles from claims if attached (#482) --- README.md | 5 +-- .../ApplicationBuilderExtensions.cs | 37 ++++++++++++++++++- .../ZitadelAuthenticationHandler.Test.cs | 15 ++++++++ tests/Zitadel.Test/TestData.cs | 1 + 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d96a8b6a..c30eeed9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # ZITADEL .NET -[![.NET Release](https://github.com/zitadel/zitadel-net/actions/workflows/dotnet-release.yml/badge.svg)](https://github.com/zitadel/zitadel-net/actions/workflows/dotnet-release.yml) -[![Code Security Testing](https://github.com/zitadel/zitadel-net/actions/workflows/security-analysis.yml/badge.svg)](https://github.com/zitadel/zitadel-net/actions/workflows/security-analysis.yml) +[![.NET Release](https://github.com/smartive/zitadel-net/actions/workflows/dotnet-release.yml/badge.svg)](https://github.com/smartive/zitadel-net/actions/workflows/dotnet-release.yml) [![Nuget](https://img.shields.io/nuget/v/Zitadel)](https://www.nuget.org/packages/Zitadel/) Welcome to the repository of the ZITADEL dotnet libraries. @@ -30,8 +29,6 @@ To set up the dev environment you merely only need to: - use `git submodule update --init` to initialize the submodules - install the [.NET SDK](https://dotnet.microsoft.com/download) -The build directory uses ["nuke"](https://nuke.build/) to build the libraries. - ##### License These libraries are licensed under the [Apache 2.0 License](LICENSE). diff --git a/src/Zitadel/Extensions/ApplicationBuilderExtensions.cs b/src/Zitadel/Extensions/ApplicationBuilderExtensions.cs index 3b3b9f66..0d6c0257 100644 --- a/src/Zitadel/Extensions/ApplicationBuilderExtensions.cs +++ b/src/Zitadel/Extensions/ApplicationBuilderExtensions.cs @@ -1,11 +1,11 @@ using System.Security.Claims; +using System.Text.Json; using IdentityModel.Client; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.OAuth.Claims; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Authentication.OpenIdConnect.Claims; using Microsoft.Extensions.DependencyInjection; -using Microsoft.IdentityModel.Tokens; using Zitadel.Authentication; using Zitadel.Authentication.Handler; using Zitadel.Authentication.Options; @@ -176,6 +176,41 @@ public static AuthenticationBuilder AddZitadelIntrospection( options.ForwardSignIn = zitadelOptions.ForwardSignIn; options.ForwardSignOut = zitadelOptions.ForwardSignOut; + options.Events.OnTokenValidated += context => + { + var roleClaims = context.Principal?.Claims.Where(c => c.Type == context.Options.RoleClaimType); + if (roleClaims is null) + { + return Task.CompletedTask; + } + + var roleIdentity = new ClaimsIdentity( + roleClaims + .Select( + c => JsonSerializer.Deserialize>>( + c.Value)) + .OfType>>() + .SelectMany( + dict => dict.SelectMany( + role => role.Value + .Select( + org => new Claim( + ZitadelClaimTypes.OrganizationRole(org.Key), + role.Key, + ClaimValueTypes.String, + context.Options.ClaimsIssuer)) + .Append( + new( + ClaimTypes.Role, + role.Key, + ClaimValueTypes.String, + context.Options.ClaimsIssuer))))); + + context.Principal?.AddIdentity(roleIdentity); + + return Task.CompletedTask; + }; + if (zitadelOptions.JwtProfile == null) { return; diff --git a/tests/Zitadel.Test/Authentication/ZitadelAuthenticationHandler.Test.cs b/tests/Zitadel.Test/Authentication/ZitadelAuthenticationHandler.Test.cs index a1be08d5..b12b698a 100644 --- a/tests/Zitadel.Test/Authentication/ZitadelAuthenticationHandler.Test.cs +++ b/tests/Zitadel.Test/Authentication/ZitadelAuthenticationHandler.Test.cs @@ -1,7 +1,9 @@ using System.Net; using System.Net.Http.Json; +using System.Security.Claims; using FluentAssertions; using Xunit; +using Zitadel.Authentication; using Zitadel.Test.WebFactories; namespace Zitadel.Test.Authentication; @@ -42,4 +44,17 @@ public async Task Should_Return_Data_With_Token() var result = await client.GetAsync("/authed"); result.StatusCode.Should().Be(HttpStatusCode.OK); } + + [Fact] + public async Task Should_Attach_Roles_To_Claims() + { + var client = _factory.CreateClient(); + client.DefaultRequestHeaders.Authorization = new("Bearer", TestData.PersonalAccessToken); + var result = await client.GetAsync("/authed"); + var parsed = await result.Content.ReadFromJsonAsync(); + parsed.Claims.Should().Contain(c => c.Key == ClaimTypes.Role); + parsed.Claims.Should().Contain(c => c.Key == ZitadelClaimTypes.OrganizationRole(TestData.OrgId)); + parsed.Claims.Should().Contain(c => c.Value == "test-1"); + parsed.Claims.Should().Contain(c => c.Value == "test-2"); + } } diff --git a/tests/Zitadel.Test/TestData.cs b/tests/Zitadel.Test/TestData.cs index 835fbbbf..93e8230f 100644 --- a/tests/Zitadel.Test/TestData.cs +++ b/tests/Zitadel.Test/TestData.cs @@ -6,6 +6,7 @@ public static class TestData { public const string ApiUrl = "https://zitadel-libraries-l8boqa.zitadel.cloud"; public const string PersonalAccessToken = "ge85fvmgTX4XAhjpF0XGpelB2vn9LZanJaqmUQDuf7iTpKVowb44LFl-86pqY2mfJCEoIOk"; + public const string OrgId = "170079925099823361"; public const string ApplicationJson = @" {