diff --git a/authorization-dotnet/DecodeJwt/App/App.csproj b/authorization-dotnet/DecodeJwt/App/App.csproj index bb496081c7..ac8d17ed1d 100644 --- a/authorization-dotnet/DecodeJwt/App/App.csproj +++ b/authorization-dotnet/DecodeJwt/App/App.csproj @@ -2,13 +2,13 @@ Exe - net8.0 + net10.0 enable enable - - + + diff --git a/authorization-dotnet/DecodeJwt/App/JwtUtils.cs b/authorization-dotnet/DecodeJwt/App/JwtUtils.cs index c7174aa835..d0b70f0540 100644 --- a/authorization-dotnet/DecodeJwt/App/JwtUtils.cs +++ b/authorization-dotnet/DecodeJwt/App/JwtUtils.cs @@ -1,5 +1,7 @@ -using System.IdentityModel.Tokens.Jwt; -using IdentityModel.Client; +using System.Buffers.Text; +using System.Text; +using Duende.IdentityModel.Client; +using Microsoft.IdentityModel.JsonWebTokens; namespace App; @@ -20,25 +22,25 @@ public static class JwtUtils { Address = discoveryDocument.TokenEndpoint, ClientId = "m2m", ClientSecret = "secret", Scope = "api" }); } - if (tokenResponse.IsError) + if (tokenResponse.IsError) { throw new Exception(tokenResponse.Error); } - + return tokenResponse.AccessToken; } - public static JwtSecurityToken ConvertJwtStringToJwtSecurityToken(string? jwt) + public static JsonWebToken ConvertJwtStringToJsonWebToken(string jwt) { - var handler = new JwtSecurityTokenHandler(); - var token = handler.ReadJwtToken(jwt); - + var handler = new JsonWebTokenHandler(); + var token = handler.ReadJsonWebToken(jwt); + return token; } - public static DecodedToken DecodeJwt(JwtSecurityToken token) + public static DecodedToken DecodeJwt(JsonWebToken token) { - var keyId = token.Header.Kid; + var keyId = token.Kid; var audience = token.Audiences.ToList(); var claims = token.Claims.Select(claim => (claim.Type, claim.Value)).ToList(); @@ -48,12 +50,21 @@ public static DecodedToken DecodeJwt(JwtSecurityToken token) audience, claims, token.ValidTo, - token.SignatureAlgorithm, - token.RawData, + token.Alg, + token.EncodedToken, token.Subject, token.ValidFrom, token.EncodedHeader, token.EncodedPayload ); } + + public static (string Header, string Payload) DecodeJwtWithoutLibrary(string jwt) + { + var parts = jwt.Split('.'); + var header = Encoding.UTF8.GetString(Base64Url.DecodeFromChars(parts[0])); + var payload = Encoding.UTF8.GetString(Base64Url.DecodeFromChars(parts[1])); + + return (header, payload); + } } diff --git a/authorization-dotnet/DecodeJwt/App/Program.cs b/authorization-dotnet/DecodeJwt/App/Program.cs index 350180ffd1..f9a75b3bfa 100644 --- a/authorization-dotnet/DecodeJwt/App/Program.cs +++ b/authorization-dotnet/DecodeJwt/App/Program.cs @@ -1,8 +1,12 @@ using App; -var accessToken = await JwtUtils.GetAccessTokenFromDuendeIdentityServerAsync(); +var accessToken = await JwtUtils.GetAccessTokenFromDuendeIdentityServerAsync() + ?? throw new InvalidOperationException("The token endpoint returned no access token."); Console.WriteLine(accessToken); -var jwt = JwtUtils.ConvertJwtStringToJwtSecurityToken(accessToken); +var jwt = JwtUtils.ConvertJwtStringToJsonWebToken(accessToken); Console.WriteLine(jwt); var jwtData = JwtUtils.DecodeJwt(jwt); Console.WriteLine(jwtData); +var (header, payload) = JwtUtils.DecodeJwtWithoutLibrary(accessToken); +Console.WriteLine(header); +Console.WriteLine(payload); diff --git a/authorization-dotnet/DecodeJwt/Tests/DecodeJwtTest.cs b/authorization-dotnet/DecodeJwt/Tests/DecodeJwtTest.cs index be826fd12f..5845f24081 100644 --- a/authorization-dotnet/DecodeJwt/Tests/DecodeJwtTest.cs +++ b/authorization-dotnet/DecodeJwt/Tests/DecodeJwtTest.cs @@ -3,7 +3,7 @@ namespace Tests; public class DecodeJwtTest { [Fact] - public async Task WhenJwtIsRetrievedFromServer_ThenItShouldNotBeNull() + public async Task WhenJwtIsRetrievedFromServer_ThenItShouldNotBeNull_Live() { var jwt = await JwtUtils.GetAccessTokenFromDuendeIdentityServerAsync(); @@ -14,11 +14,21 @@ public async Task WhenJwtIsRetrievedFromServer_ThenItShouldNotBeNull() [ClassData(typeof(JwtTestData))] public void WhenJwtIsValid_ThenItShouldDecode(string token) { - var jwt = JwtUtils.ConvertJwtStringToJwtSecurityToken(token); + var jwt = JwtUtils.ConvertJwtStringToJsonWebToken(token); var jwtData = JwtUtils.DecodeJwt(jwt); Assert.NotNull(jwtData.Issuer); Assert.NotEmpty(jwtData.Audience); Assert.NotEmpty(jwtData.Claims); } -} \ No newline at end of file + + [Theory] + [ClassData(typeof(JwtTestData))] + public void WhenJwtIsDecodedWithoutLibrary_ThenHeaderAndPayloadAreJson(string token) + { + var (header, payload) = JwtUtils.DecodeJwtWithoutLibrary(token); + + Assert.Contains("\"alg\":\"RS256\"", header); + Assert.Contains("\"iss\":\"https://demo.duendesoftware.com\"", payload); + } +} diff --git a/authorization-dotnet/DecodeJwt/Tests/Tests.csproj b/authorization-dotnet/DecodeJwt/Tests/Tests.csproj index 5884ed7ca2..c878fa5b95 100644 --- a/authorization-dotnet/DecodeJwt/Tests/Tests.csproj +++ b/authorization-dotnet/DecodeJwt/Tests/Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable enable @@ -10,13 +10,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all