diff --git a/Directory.Build.props b/Directory.Build.props
index 9a4744e5..51ca78d5 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,10 +1,10 @@
- net8.0
+ net9.0
- 8.0.4
- 8.0.4
+ 8.0.5-preview-net9
+ 8.0.5-preview-net9
HigginsSoft.$(MSBuildProjectName)
$(MSBuildProjectName)
enable
diff --git a/Directory.Build.targets b/Directory.Build.targets
index a6f1625a..b82d9ece 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -8,7 +8,7 @@
BeforeTargets="ProcessFrameworkReferences">
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2b0e2c7b..3f0f6c02 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -2,11 +2,11 @@
true
true
- 8.0.0
- 8.0.1
- 8.0.0
- 8.0.0
- 8.0.0
+ 9.0.0
+ 9.0.0
+ 9.0.0
+ 9.0.0
+ 9.0.0
8.0.0-preview.2.23619.3
2.59.0
8.0.0
@@ -23,17 +23,17 @@
-
+
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
@@ -47,7 +47,7 @@
-
+
@@ -59,7 +59,7 @@
-
+
@@ -71,10 +71,10 @@
-
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -95,24 +95,24 @@
-
-
+
+
-
+
-
-
+
+
-
+
@@ -144,7 +144,7 @@
-
+
@@ -160,17 +160,17 @@
-
+
-
-
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
\ No newline at end of file
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index db27fa11..ac681abc 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -6,9 +6,13 @@ and this project adheres to [Semantic Versioning 2](http://semver.org/).
## [Unreleased] - 2024-02-17
+- NET 9.0 Support
+- Updated IdentityModel to 7 and updated OpenIdConnect to 8.1.2
+- Removed CollectionUtilities.IsNullOrEmpty dependecy because of obselence in new version of Microsoft.IdentityModel.Tokens
- Current templates and quickstarts being added to seperate template and quickstart repositories to continue previous version functionality.
- DotNet tool to install template currently under development.
--
+
+
## [8.0.4] - 2024-02-17
Identity Server 8.0.4 is a security release that addresses hundreds of security vulnerabilities in the IdentityServer8 code base. We recommend that you update to this version.
diff --git a/global.json b/global.json
index f3365c41..20f482a8 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,5 @@
{
"sdk": {
- "version": "8.0.100"
+ "version": "9.0.100"
}
}
\ No newline at end of file
diff --git a/src/EntityFramework.Storage/test/IntegrationTests/DbContexts/ClientDbContextTests.cs b/src/EntityFramework.Storage/test/IntegrationTests/DbContexts/ClientDbContextTests.cs
index 283bbe56..101834cc 100644
--- a/src/EntityFramework.Storage/test/IntegrationTests/DbContexts/ClientDbContextTests.cs
+++ b/src/EntityFramework.Storage/test/IntegrationTests/DbContexts/ClientDbContextTests.cs
@@ -23,7 +23,7 @@ public class ClientDbContextTests : IntegrationTest fixture) : base(fixture)
{
- foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList())
+ foreach (var options in TestDatabaseProviders)
{
using (var context = new ConfigurationDbContext(options, StoreOptions))
context.Database.EnsureCreated();
diff --git a/src/EntityFramework.Storage/test/IntegrationTests/IntegrationTest.cs b/src/EntityFramework.Storage/test/IntegrationTests/IntegrationTest.cs
index c17dc35f..757b330a 100644
--- a/src/EntityFramework.Storage/test/IntegrationTests/IntegrationTest.cs
+++ b/src/EntityFramework.Storage/test/IntegrationTests/IntegrationTest.cs
@@ -60,7 +60,7 @@ static IntegrationTest()
protected IntegrationTest(DatabaseProviderFixture fixture)
{
- fixture.Options = TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList();
+ fixture.Options = (TestDatabaseProviders as IEnumerable>).ToList();
fixture.StoreOptions = StoreOptions;
}
}
diff --git a/src/EntityFramework.Storage/test/IntegrationTests/Stores/ClientStoreTests.cs b/src/EntityFramework.Storage/test/IntegrationTests/Stores/ClientStoreTests.cs
index ac5a895c..110259a4 100644
--- a/src/EntityFramework.Storage/test/IntegrationTests/Stores/ClientStoreTests.cs
+++ b/src/EntityFramework.Storage/test/IntegrationTests/Stores/ClientStoreTests.cs
@@ -26,7 +26,7 @@ public class ClientStoreTests : IntegrationTest fixture) : base(fixture)
{
- foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions) y)).ToList())
+ foreach (var options in TestDatabaseProviders)
{
using (var context = new ConfigurationDbContext(options, StoreOptions))
{
diff --git a/src/EntityFramework.Storage/test/IntegrationTests/Stores/DeviceFlowStoreTests.cs b/src/EntityFramework.Storage/test/IntegrationTests/Stores/DeviceFlowStoreTests.cs
index 1f96c47b..4b2ac2d7 100644
--- a/src/EntityFramework.Storage/test/IntegrationTests/Stores/DeviceFlowStoreTests.cs
+++ b/src/EntityFramework.Storage/test/IntegrationTests/Stores/DeviceFlowStoreTests.cs
@@ -31,7 +31,7 @@ public class DeviceFlowStoreTests : IntegrationTest fixture) : base(fixture)
{
- foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList())
+ foreach (var options in TestDatabaseProviders)
{
using (var context = new PersistedGrantDbContext(options, StoreOptions))
context.Database.EnsureCreated();
diff --git a/src/EntityFramework.Storage/test/IntegrationTests/Stores/PersistedGrantStoreTests.cs b/src/EntityFramework.Storage/test/IntegrationTests/Stores/PersistedGrantStoreTests.cs
index 11c16d9d..c0157544 100644
--- a/src/EntityFramework.Storage/test/IntegrationTests/Stores/PersistedGrantStoreTests.cs
+++ b/src/EntityFramework.Storage/test/IntegrationTests/Stores/PersistedGrantStoreTests.cs
@@ -26,7 +26,7 @@ public class PersistedGrantStoreTests : IntegrationTest fixture) : base(fixture)
{
- foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList())
+ foreach (var options in TestDatabaseProviders)
{
using (var context = new PersistedGrantDbContext(options, StoreOptions))
context.Database.EnsureCreated();
diff --git a/src/EntityFramework.Storage/test/IntegrationTests/Stores/ResourceStoreTests.cs b/src/EntityFramework.Storage/test/IntegrationTests/Stores/ResourceStoreTests.cs
index eb4882f0..cbb398b9 100644
--- a/src/EntityFramework.Storage/test/IntegrationTests/Stores/ResourceStoreTests.cs
+++ b/src/EntityFramework.Storage/test/IntegrationTests/Stores/ResourceStoreTests.cs
@@ -25,7 +25,7 @@ public class ScopeStoreTests : IntegrationTest fixture) : base(fixture)
{
- foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList())
+ foreach (var options in TestDatabaseProviders)
{
using (var context = new ConfigurationDbContext(options, StoreOptions))
context.Database.EnsureCreated();
diff --git a/src/EntityFramework.Storage/test/IntegrationTests/TokenCleanup/TokenCleanupTests.cs b/src/EntityFramework.Storage/test/IntegrationTests/TokenCleanup/TokenCleanupTests.cs
index 5d131ffa..46a28c19 100644
--- a/src/EntityFramework.Storage/test/IntegrationTests/TokenCleanup/TokenCleanupTests.cs
+++ b/src/EntityFramework.Storage/test/IntegrationTests/TokenCleanup/TokenCleanupTests.cs
@@ -30,7 +30,7 @@ public class TokenCleanupTests : IntegrationTest fixture) : base(fixture)
{
- foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList())
+ foreach (var options in TestDatabaseProviders)
{
using (var context = new PersistedGrantDbContext(options, StoreOptions))
{
diff --git a/src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/IntegrationTest.cs b/src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/IntegrationTest.cs
index 71abbef8..d653da21 100644
--- a/src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/IntegrationTest.cs
+++ b/src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/IntegrationTest.cs
@@ -60,7 +60,7 @@ static IntegrationTest()
protected IntegrationTest(DatabaseProviderFixture fixture)
{
- fixture.Options = TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList();
+ fixture.Options = (TestDatabaseProviders as IEnumerable>).ToList();
fixture.StoreOptions = StoreOptions;
}
}
diff --git a/src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/Services/CorsPolicyServiceTests.cs b/src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/Services/CorsPolicyServiceTests.cs
index d4b16db8..46459162 100644
--- a/src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/Services/CorsPolicyServiceTests.cs
+++ b/src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/Services/CorsPolicyServiceTests.cs
@@ -27,7 +27,7 @@ public class CorsPolicyServiceTests : IntegrationTest fixture) : base(fixture)
{
- foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList())
+ foreach (var options in TestDatabaseProviders)
{
using (var context = new ConfigurationDbContext(options, StoreOptions))
context.Database.EnsureCreated();
diff --git a/src/IdentityServer8/src/Extensions/TokenExtensions.cs b/src/IdentityServer8/src/Extensions/TokenExtensions.cs
index b045ca68..a1942867 100644
--- a/src/IdentityServer8/src/Extensions/TokenExtensions.cs
+++ b/src/IdentityServer8/src/Extensions/TokenExtensions.cs
@@ -44,7 +44,7 @@ public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock,
var amrClaims = token.Claims.Where(x => x.Type == JwtClaimTypes.AuthenticationMethod).ToArray();
var scopeClaims = token.Claims.Where(x => x.Type == JwtClaimTypes.Scope).ToArray();
var jsonClaims = token.Claims.Where(x => x.ValueType == IdentityServerConstants.ClaimValueTypes.Json).ToList();
-
+
// add confirmation claim if present (it's JSON valued)
if (token.Confirmation.IsPresent())
{
@@ -79,7 +79,7 @@ public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock,
var amrValues = amrClaims.Select(x => x.Value).Distinct().ToArray();
payload.Add(JwtClaimTypes.AuthenticationMethod, amrValues);
}
-
+
// deal with json types
// calling ToArray() to trigger JSON parsing once and so later
// collection identity comparisons work for the anonymous type
@@ -121,7 +121,7 @@ public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock,
var newArr = new List();
foreach (var arrays in group)
{
- var arr = (JArray)arrays.JsonValue;
+ var arr = (JArray) arrays.JsonValue;
newArr.AddRange(arr);
}
diff --git a/src/IdentityServer8/src/ResponseHandling/Default/DiscoveryResponseGenerator.cs b/src/IdentityServer8/src/ResponseHandling/Default/DiscoveryResponseGenerator.cs
index 0bdce2f3..c31e8f39 100644
--- a/src/IdentityServer8/src/ResponseHandling/Default/DiscoveryResponseGenerator.cs
+++ b/src/IdentityServer8/src/ResponseHandling/Default/DiscoveryResponseGenerator.cs
@@ -331,7 +331,7 @@ where scope.ShowInDiscoveryDocument
}
// custom entries
- if (!Options.Discovery.CustomEntries.IsNullOrEmpty())
+ if (!Options.Discovery.CustomEntries.EnumerableIsNullOrEmpty())
{
foreach ((string key, object value) in Options.Discovery.CustomEntries)
{
diff --git a/src/IdentityServer8/src/Services/Default/DefaultKeyMaterialService.cs b/src/IdentityServer8/src/Services/Default/DefaultKeyMaterialService.cs
index caed7439..fc5d6f8c 100644
--- a/src/IdentityServer8/src/Services/Default/DefaultKeyMaterialService.cs
+++ b/src/IdentityServer8/src/Services/Default/DefaultKeyMaterialService.cs
@@ -39,7 +39,7 @@ public async Task GetSigningCredentialsAsync(IEnumerable CreateTokenAsync(Token token)
var header = await CreateHeaderAsync(token);
var payload = await CreatePayloadAsync(token);
- return await CreateJwtAsync(new JwtSecurityToken(header, payload));
+ return await CreateJwtAsync(header.Header, header.Credentials, payload);
}
///
@@ -78,7 +79,7 @@ public virtual async Task CreateTokenAsync(Token token)
///
/// The token.
/// The JWT header
- protected virtual async Task CreateHeaderAsync(Token token)
+ protected virtual async Task<(Dictionary Header, SigningCredentials Credentials)> CreateHeaderAsync(Token token)
{
var credential = await Keys.GetSigningCredentialsAsync(token.AllowedSigningAlgorithms);
@@ -87,7 +88,11 @@ protected virtual async Task CreateHeaderAsync(Token token)
throw new InvalidOperationException("No signing credential is configured. Can't create JWT token");
}
- var header = new JwtHeader(credential);
+ var header = new Dictionary
+ {
+ { "alg", credential.Algorithm },
+ { "kid", credential.Key.KeyId }
+ };
// emit x5t claim for backwards compatibility with v4 of MS JWT library
if (credential.Key is X509SecurityKey x509Key)
@@ -109,7 +114,7 @@ protected virtual async Task CreateHeaderAsync(Token token)
}
}
- return header;
+ return (header, credential);
}
///
@@ -126,11 +131,29 @@ protected virtual Task CreatePayloadAsync(Token token)
///
/// Applies the signature to the JWT
///
- /// The JWT object.
+ /// The JWT header.
+ /// The JWT payload.
/// The signed JWT
- protected virtual Task CreateJwtAsync(JwtSecurityToken jwt)
+ protected virtual Task CreateJwtAsync(Dictionary header, SigningCredentials signingCredentials, JwtPayload payload)
{
- var handler = new JwtSecurityTokenHandler();
- return Task.FromResult(handler.WriteToken(jwt));
+ var handler = new JsonWebTokenHandler();
+ var descriptor = new SecurityTokenDescriptor
+ {
+ Claims = payload,
+ Issuer = payload.TryGetValue("iss", out var issuer) ? issuer.ToString() : null,
+ Expires = payload.TryGetValue("exp", out var exp) ? DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(exp)).UtcDateTime : null,
+ SigningCredentials = signingCredentials
+ };
+ if (header.TryGetValue("typ", out var typ))
+ descriptor.TokenType = typ.ToString();
+
+ if (payload.TryGetValue("aud", out var audObj) && audObj is List audiences)
+ audiences.ForEach(descriptor.Audiences.Add);
+
+ var jwt = handler.CreateToken(descriptor);
+
+ return Task.FromResult(jwt);
}
+
+
}
diff --git a/src/IdentityServer8/src/Test/TestUserStore.cs b/src/IdentityServer8/src/Test/TestUserStore.cs
index e0ba90b0..85a66e57 100644
--- a/src/IdentityServer8/src/Test/TestUserStore.cs
+++ b/src/IdentityServer8/src/Test/TestUserStore.cs
@@ -103,11 +103,6 @@ public TestUser AutoProvisionUser(string provider, string userId, List cl
{
filtered.Add(new Claim(JwtClaimTypes.Name, claim.Value));
}
- // if the JWT handler has an outbound mapping to an OIDC claim use that
- else if (JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.ContainsKey(claim.Type))
- {
- filtered.Add(new Claim(JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap[claim.Type], claim.Value));
- }
// copy the claim as-is
else
{
diff --git a/src/IdentityServer8/src/Validation/Default/JwtBearerClientAssertionSecretParser.cs b/src/IdentityServer8/src/Validation/Default/JwtBearerClientAssertionSecretParser.cs
index 2fff5f4c..55913758 100644
--- a/src/IdentityServer8/src/Validation/Default/JwtBearerClientAssertionSecretParser.cs
+++ b/src/IdentityServer8/src/Validation/Default/JwtBearerClientAssertionSecretParser.cs
@@ -10,6 +10,8 @@ The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
+using Microsoft.IdentityModel.JsonWebTokens;
+
namespace IdentityServer8.Validation;
///
@@ -104,7 +106,7 @@ private string GetClientIdFromToken(string token)
{
try
{
- var jwt = new JwtSecurityToken(token);
+ var jwt = new JsonWebToken(token);
return jwt.Subject;
}
catch (Exception e)
diff --git a/src/IdentityServer8/src/Validation/Default/JwtRequestValidator.cs b/src/IdentityServer8/src/Validation/Default/JwtRequestValidator.cs
index fdcd2b9a..97e17790 100644
--- a/src/IdentityServer8/src/Validation/Default/JwtRequestValidator.cs
+++ b/src/IdentityServer8/src/Validation/Default/JwtRequestValidator.cs
@@ -10,6 +10,7 @@ The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
namespace IdentityServer8.Validation;
@@ -21,14 +22,11 @@ public class JwtRequestValidator
{
private readonly string _audienceUri;
private readonly IHttpContextAccessor _httpContextAccessor;
-
+
///
/// JWT handler
///
- protected JwtSecurityTokenHandler Handler = new JwtSecurityTokenHandler
- {
- MapInboundClaims = false
- };
+ protected JsonWebTokenHandler Handler = new JsonWebTokenHandler();
///
/// The audience URI to use
@@ -50,7 +48,7 @@ protected string AudienceUri
/// The logger
///
protected readonly ILogger Logger;
-
+
///
/// The optione
///
@@ -62,7 +60,7 @@ protected string AudienceUri
public JwtRequestValidator(IHttpContextAccessor contextAccessor, IdentityServerOptions options, ILogger logger)
{
_httpContextAccessor = contextAccessor;
-
+
Options = options;
Logger = logger;
}
@@ -106,10 +104,10 @@ public virtual async Task ValidateAsync(Client clien
return fail;
}
- JwtSecurityToken jwtSecurityToken;
+ JsonWebToken jwtToken;
try
{
- jwtSecurityToken = await ValidateJwtAsync(jwtTokenString, trustedKeys, client);
+ jwtToken = await ValidateJwtAsync(jwtTokenString, trustedKeys, client);
}
catch (Exception e)
{
@@ -117,14 +115,14 @@ public virtual async Task ValidateAsync(Client clien
return fail;
}
- if (jwtSecurityToken.Payload.ContainsKey(OidcConstants.AuthorizeRequest.Request) ||
- jwtSecurityToken.Payload.ContainsKey(OidcConstants.AuthorizeRequest.RequestUri))
+ // Check for prohibited claims
+ if (jwtToken.Claims.Any(c => c.Type == OidcConstants.AuthorizeRequest.Request || c.Type == OidcConstants.AuthorizeRequest.RequestUri))
{
Logger.LogError("JWT payload must not contain request or request_uri");
return fail;
}
- var payload = await ProcessPayloadAsync(jwtSecurityToken);
+ var payload = await ProcessPayloadAsync(jwtToken);
var result = new JwtRequestValidationResult
{
@@ -153,7 +151,7 @@ protected virtual Task> GetKeysAsync(Client client)
/// The keys
/// The client
///
- protected virtual Task ValidateJwtAsync(string jwtTokenString, IEnumerable keys, Client client)
+ protected virtual async Task ValidateJwtAsync(string jwtTokenString, IEnumerable keys, Client client)
{
var tokenValidationParameters = new TokenValidationParameters
{
@@ -175,9 +173,15 @@ protected virtual Task ValidateJwtAsync(string jwtTokenString,
tokenValidationParameters.ValidTypes = new[] { JwtClaimTypes.JwtTypes.AuthorizationRequest };
}
- Handler.ValidateToken(jwtTokenString, tokenValidationParameters, out var token);
-
- return Task.FromResult((JwtSecurityToken)token);
+ var validationResult = await Handler.ValidateTokenAsync(jwtTokenString, tokenValidationParameters);
+
+ if (!validationResult.IsValid)
+ {
+ throw new SecurityTokenValidationException(validationResult.Exception?.Message ?? "JWT validation failed.");
+ }
+
+ var jwt = Handler.ReadJsonWebToken(jwtTokenString);
+ return jwt;
}
///
@@ -185,27 +189,38 @@ protected virtual Task ValidateJwtAsync(string jwtTokenString,
///
/// The JWT token
///
- protected virtual Task> ProcessPayloadAsync(JwtSecurityToken token)
+ protected virtual Task> ProcessPayloadAsync(JsonWebToken token)
{
// filter JWT validation values
var payload = new Dictionary();
- foreach (var key in token.Payload.Keys)
+
+ foreach (var claim in token.Claims)
{
- if (!Constants.Filters.JwtRequestClaimTypesFilter.Contains(key))
+ if (!Constants.Filters.JwtRequestClaimTypesFilter.Contains(claim.Type))
{
- var value = token.Payload[key];
+ var value = claim.Value;
- switch (value)
+ try
+ {
+ // Try to parse the value as JSON
+ var parsedJson = JToken.Parse(value);
+ switch (parsedJson)
+ {
+ case JObject jobj:
+ payload.Add(claim.Type, jobj.ToString(Formatting.None));
+ break;
+ case JArray jarr:
+ payload.Add(claim.Type, jarr.ToString(Formatting.None));
+ break;
+ default:
+ payload.Add(claim.Type, value);
+ break;
+ }
+ }
+ catch (JsonReaderException)
{
- case string s:
- payload.Add(key, s);
- break;
- case JObject jobj:
- payload.Add(key, jobj.ToString(Formatting.None));
- break;
- case JArray jarr:
- payload.Add(key, jarr.ToString(Formatting.None));
- break;
+ // If parsing fails, treat as a string
+ payload.Add(claim.Type, value);
}
}
}
diff --git a/src/IdentityServer8/src/Validation/Default/PrivateKeyJwtSecretValidator.cs b/src/IdentityServer8/src/Validation/Default/PrivateKeyJwtSecretValidator.cs
index 3f2a8d53..20b42387 100644
--- a/src/IdentityServer8/src/Validation/Default/PrivateKeyJwtSecretValidator.cs
+++ b/src/IdentityServer8/src/Validation/Default/PrivateKeyJwtSecretValidator.cs
@@ -10,7 +10,9 @@ The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
+using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames;
namespace IdentityServer8.Validation;
@@ -24,7 +26,7 @@ public class PrivateKeyJwtSecretValidator : ISecretValidator
private readonly ILogger _logger;
private const string Purpose = nameof(PrivateKeyJwtSecretValidator);
-
+
///
/// Instantiates an instance of private_key_jwt secret validator
///
@@ -86,7 +88,7 @@ public async Task ValidateAsync(IEnumerable secr
string.Concat(_contextAccessor.HttpContext.GetIdentityServerIssuerUri().EnsureTrailingSlash(),
Constants.ProtocolRoutePaths.Token)
};
-
+
var tokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKeys = trustedKeys,
@@ -100,34 +102,43 @@ public async Task ValidateAsync(IEnumerable secr
RequireSignedTokens = true,
RequireExpirationTime = true,
-
+
ClockSkew = TimeSpan.FromMinutes(5)
};
try
{
- var handler = new JwtSecurityTokenHandler();
- handler.ValidateToken(jwtTokenString, tokenValidationParameters, out var token);
+ var handler = new JsonWebTokenHandler();
+ var validateResult = await handler.ValidateTokenAsync(jwtTokenString, tokenValidationParameters);
- var jwtToken = (JwtSecurityToken)token;
+ var jwtToken = (JsonWebToken) validateResult.SecurityToken;
if (jwtToken.Subject != jwtToken.Issuer)
{
_logger.LogError("Both 'sub' and 'iss' in the client assertion token must have a value of client_id.");
return fail;
}
-
- var exp = jwtToken.Payload.Exp;
- if (!exp.HasValue)
+
+ var expClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Exp);
+
+ if (expClaim == null)
{
_logger.LogError("exp is missing.");
return fail;
}
-
- var jti = jwtToken.Payload.Jti;
- if (jti.IsMissing())
+
+ if (!long.TryParse(expClaim.Value, out var exp))
+ {
+ _logger.LogError("Invalid exp value.");
+ return fail;
+ }
+
+ var jtiClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Jti);
+
+ if (jtiClaim == null || string.IsNullOrEmpty(jtiClaim.Value))
{
_logger.LogError("jti is missing.");
return fail;
}
+ var jti = jtiClaim.Value;
if (await _replayCache.ExistsAsync(Purpose, jti))
{
@@ -136,7 +147,7 @@ public async Task ValidateAsync(IEnumerable secr
}
else
{
- await _replayCache.AddAsync(Purpose, jti, DateTimeOffset.FromUnixTimeSeconds(exp.Value).AddMinutes(5));
+ await _replayCache.AddAsync(Purpose, jti, DateTimeOffset.FromUnixTimeSeconds(exp).AddMinutes(5));
}
return success;
diff --git a/src/IdentityServer8/src/Validation/Default/TokenValidator.cs b/src/IdentityServer8/src/Validation/Default/TokenValidator.cs
index 665ff315..92ae15ae 100644
--- a/src/IdentityServer8/src/Validation/Default/TokenValidator.cs
+++ b/src/IdentityServer8/src/Validation/Default/TokenValidator.cs
@@ -10,6 +10,7 @@ The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
namespace IdentityServer8.Validation;
@@ -237,7 +238,7 @@ public async Task ValidateAccessTokenAsync(string token,
private async Task ValidateJwtAsync(string jwt, IEnumerable validationKeys, bool validateLifetime = true, string audience = null)
{
- var handler = new JwtSecurityTokenHandler();
+ var handler = new JsonWebTokenHandler();
handler.InboundClaimTypeMap.Clear();
var parameters = new TokenValidationParameters
@@ -258,15 +259,17 @@ private async Task ValidateJwtAsync(string jwt, IEnumerab
try
{
- var id = handler.ValidateToken(jwt, parameters, out var securityToken);
- var jwtSecurityToken = securityToken as JwtSecurityToken;
+ var result = await handler.ValidateTokenAsync(jwt, parameters);
+ var securityToken = result.SecurityToken;
+ var id = result.ClaimsIdentity;
+ var jwtSecurityToken = securityToken as JsonWebToken;
// if no audience is specified, we make at least sure that it is an access token
if (audience.IsMissing())
{
if (_options.AccessTokenJwtType.IsPresent())
{
- var type = jwtSecurityToken.Header.Typ;
+ var type = jwtSecurityToken.Typ;
if (!string.Equals(type, _options.AccessTokenJwtType))
{
return new TokenValidationResult
@@ -405,7 +408,7 @@ private string GetClientIdFromJwt(string token)
{
try
{
- var jwt = new JwtSecurityToken(token);
+ var jwt = new JsonWebToken(token);
var clientId = jwt.Audiences.FirstOrDefault();
return clientId;
diff --git a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ClientAssertionClient.cs b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ClientAssertionClient.cs
index 3ee6bedb..78503987 100644
--- a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ClientAssertionClient.cs
+++ b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ClientAssertionClient.cs
@@ -17,6 +17,7 @@ copies or substantial portions of the Software.
using IdentityServer.IntegrationTests.Common;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
@@ -246,24 +247,28 @@ private string CreateToken(string clientId, DateTime? nowOverride = null)
var certificate = TestCert.Load();
var now = nowOverride ?? DateTime.UtcNow;
- var token = new JwtSecurityToken(
- clientId,
- TokenEndpoint,
- new List()
- {
- new Claim("jti", Guid.NewGuid().ToString()),
- new Claim(JwtClaimTypes.Subject, clientId),
- new Claim(JwtClaimTypes.IssuedAt, new DateTimeOffset(now).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
- },
- now,
- now.AddMinutes(1),
- new SigningCredentials(
- new X509SecurityKey(certificate),
- SecurityAlgorithms.RsaSha256
- )
- );
-
- var tokenHandler = new JwtSecurityTokenHandler();
- return tokenHandler.WriteToken(token);
+ var tokenHandler = new JsonWebTokenHandler();
+
+ var claims = new List
+ {
+ new Claim("jti", Guid.NewGuid().ToString()),
+ new Claim(JwtClaimTypes.Subject, clientId),
+ new Claim(JwtClaimTypes.IssuedAt, new DateTimeOffset(now).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
+ };
+
+ var tokenDescriptor = new SecurityTokenDescriptor
+ {
+ Issuer = clientId,
+ Audience = TokenEndpoint,
+ Claims = claims.ToDictionary(c => c.Type, c => (object)c.Value),
+ NotBefore = now,
+ Expires = now.AddMinutes(1),
+ SigningCredentials = new SigningCredentials(
+ new X509SecurityKey(certificate),
+ SecurityAlgorithms.RsaSha256
+ )
+ };
+
+ return tokenHandler.CreateToken(tokenDescriptor);
}
}
diff --git a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/CustomTokenRequestValidatorClient.cs b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/CustomTokenRequestValidatorClient.cs
index 9719075e..bb979818 100644
--- a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/CustomTokenRequestValidatorClient.cs
+++ b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/CustomTokenRequestValidatorClient.cs
@@ -125,7 +125,7 @@ private Dictionary GetFields(JsonElement json)
}
private void ValidateCustomFields(TokenResponse response)
{
- var fields = GetFields(response.Json);
+ var fields = GetFields(response.Json.Value);
fields["custom"].ToString().Should().Be("custom");
}
diff --git a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/CustomTokenResponseClients.cs b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/CustomTokenResponseClients.cs
index 8e8721ae..2873c4f6 100644
--- a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/CustomTokenResponseClients.cs
+++ b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/CustomTokenResponseClients.cs
@@ -287,13 +287,13 @@ private CustomResponseDto GetDto(JsonElement responseObject)
private Dictionary GetFieldsD(TokenResponse response)
{
- return response.Json.ToObject>();
+ return response.Json?.Deserialize>() ?? [];
}
private Dictionary GetFields(TokenResponse response)
{
- return GetFields(response.Json);
+ return GetFields(response.Json.Value);
}
private Dictionary GetFields(JsonElement json)
diff --git a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ExtensionGrantClient.cs b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ExtensionGrantClient.cs
index 95b61c17..1964fc5a 100644
--- a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ExtensionGrantClient.cs
+++ b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ExtensionGrantClient.cs
@@ -16,6 +16,7 @@ copies or substantial portions of the Software.
using IdentityServer.IntegrationTests.Clients.Setup;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
+using Microsoft.IdentityModel.JsonWebTokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IdentityModel.Tokens.Jwt;
@@ -468,8 +469,11 @@ public async Task Impersonate_client_should_succeed()
response.AccessToken.Should().Contain(".");
- var jwt = new JwtSecurityToken(response.AccessToken);
- jwt.Payload["client_id"].Should().Be("impersonated_client_id");
+ var handler = new JsonWebTokenHandler();
+ var jwt = handler.ReadJsonWebToken(response.AccessToken);
+
+ // Payload'dan client_id değerini kontrol et
+ jwt.Claims.First(c => c.Type == "client_id").Value.Should().Be("impersonated_client_id");
}
[Fact]
diff --git a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/UserInfoClient.cs b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/UserInfoClient.cs
index 1f6600b6..d7a83219 100644
--- a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/UserInfoClient.cs
+++ b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/UserInfoClient.cs
@@ -203,7 +203,7 @@ public async Task Complex_json_should_be_correct()
});
//roles = ((JArray)userInfo.Json["role"]).Select(x => x.ToString()).ToArray();
- roles = userInfo.Json.TryGetStringArray("role").ToArray();
+ roles = userInfo.Json.Value.TryGetStringArray("role").ToArray();
roles.Length.Should().Be(2);
roles.Should().Contain("Geek");
roles.Should().Contain("Developer");
diff --git a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Basic/CodeFlowTests.cs b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Basic/CodeFlowTests.cs
index 1cc61ec9..5a428745 100644
--- a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Basic/CodeFlowTests.cs
+++ b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Basic/CodeFlowTests.cs
@@ -23,6 +23,7 @@ copies or substantial portions of the Software.
using IdentityServer8.Configuration;
using IdentityServer8.Models;
using IdentityServer8.Test;
+using Microsoft.IdentityModel.JsonWebTokens;
using Xunit;
namespace IdentityServer.IntegrationTests.Conformance.Basic;
@@ -114,7 +115,7 @@ public async Task No_state_should_not_result_in_shash()
tokenResult.ExpiresIn.Should().BeGreaterThan(0);
tokenResult.IdentityToken.Should().NotBeNull();
- var token = new JwtSecurityToken(tokenResult.IdentityToken);
+ var token = new JsonWebToken(tokenResult.IdentityToken);
var s_hash = token.Claims.FirstOrDefault(c => c.Type == "s_hash");
s_hash.Should().BeNull();
@@ -163,7 +164,7 @@ public async Task State_should_result_in_shash()
tokenResult.ExpiresIn.Should().BeGreaterThan(0);
tokenResult.IdentityToken.Should().NotBeNull();
- var token = new JwtSecurityToken(tokenResult.IdentityToken);
+ var token = new JsonWebToken(tokenResult.IdentityToken);
var s_hash = token.Claims.FirstOrDefault(c => c.Type == "s_hash");
s_hash.Should().NotBeNull();
diff --git a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Basic/ResponseTypeResponseModeTests.cs b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Basic/ResponseTypeResponseModeTests.cs
index db96a8ab..da05bf0c 100644
--- a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Basic/ResponseTypeResponseModeTests.cs
+++ b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Basic/ResponseTypeResponseModeTests.cs
@@ -101,7 +101,7 @@ public async Task Request_with_response_type_code_supported()
// to follow the RFC to address open redirect in original OAuth RFC
[Fact]
[Trait("Category", Category)]
- public async Task Request_missing_response_type_rejected()
+ public async Task Request_unsupported_response_type_rejected()
{
await _mockPipeline.LoginAsync("bob");
@@ -110,7 +110,7 @@ public async Task Request_missing_response_type_rejected()
var url = _mockPipeline.CreateAuthorizeUrl(
clientId: "code_client",
- responseType: null, // missing
+ responseType: "__wrong__", //unsupported
scope: "openid",
redirectUri: "https://code_client/callback",
state: state,
diff --git a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs
index 4021a23f..56e59dea 100644
--- a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs
+++ b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs
@@ -26,6 +26,7 @@ copies or substantial portions of the Software.
using IdentityServer8.Configuration;
using IdentityServer8.Models;
using IdentityServer8.Test;
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
using Xunit;
@@ -181,21 +182,24 @@ public JwtRequestAuthorizeTests()
string CreateRequestJwt(string issuer, string audience, SigningCredentials credential, Claim[] claims, bool setJwtTyp = false)
{
- var handler = new JwtSecurityTokenHandler();
- handler.OutboundClaimTypeMap.Clear();
+ var handler = new JsonWebTokenHandler();
+
+ var claimsDictionary = claims.ToDictionary(c => c.Type, c => (object) c.Value);
+
+ var descriptor = new SecurityTokenDescriptor
+ {
+ Issuer = issuer,
+ Audience = audience,
+ SigningCredentials = credential,
+ Claims = claimsDictionary
+ };
+ if (setJwtTyp)
+ descriptor.TokenType = JwtClaimTypes.JwtTypes.AuthorizationRequest;
- var token = handler.CreateJwtSecurityToken(
- issuer: issuer,
- audience: audience,
- signingCredentials: credential,
- subject: Identity.Create("pwd", claims));
+ var token = handler.CreateToken(descriptor);
- if (setJwtTyp)
- {
- token.Header["typ"] = JwtClaimTypes.JwtTypes.AuthorizationRequest;
- }
- return handler.WriteToken(token);
+ return token;
}
[Fact]
@@ -539,16 +543,16 @@ public async Task authorize_should_accept_complex_objects_in_request_object()
var someObj2 = JsonSerializer.Deserialize(_mockPipeline.LoginRequest.Parameters["someObj"], someObj.GetType());
someObj.Should().BeEquivalentTo(someObj2);
_mockPipeline.LoginRequest.Parameters["someArr"].Should().NotBeNull();
- var someArr2 =JsonSerializer.Deserialize(_mockPipeline.LoginRequest.Parameters["someArr"]);
+ var someArr2 = JsonSerializer.Deserialize(_mockPipeline.LoginRequest.Parameters["someArr"]);
someArr2.Should().Contain(new[] { "a", "c", "b" });
someArr2.Length.Should().Be(3);
_mockPipeline.LoginRequest.RequestObjectValues.Count.Should().Be(13);
_mockPipeline.LoginRequest.RequestObjectValues["someObj"].Should().NotBeNull();
- someObj2 =JsonSerializer.Deserialize(_mockPipeline.LoginRequest.RequestObjectValues["someObj"], someObj.GetType());
+ someObj2 = JsonSerializer.Deserialize(_mockPipeline.LoginRequest.RequestObjectValues["someObj"], someObj.GetType());
someObj.Should().BeEquivalentTo(someObj2);
_mockPipeline.LoginRequest.RequestObjectValues["someArr"].Should().NotBeNull();
- someArr2 =JsonSerializer.Deserialize(_mockPipeline.LoginRequest.Parameters["someArr"]);
+ someArr2 = JsonSerializer.Deserialize(_mockPipeline.LoginRequest.Parameters["someArr"]);
someArr2.Should().Contain(new[] { "a", "c", "b" });
someArr2.Length.Should().Be(3);
}
diff --git a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Introspection/IntrospectionTests.cs b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Introspection/IntrospectionTests.cs
index 630a317f..bcf671a9 100644
--- a/src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Introspection/IntrospectionTests.cs
+++ b/src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Introspection/IntrospectionTests.cs
@@ -188,7 +188,7 @@ public async Task Response_data_should_be_valid_using_single_scope()
Token = tokenResponse.AccessToken
});
- var values = introspectionResponse.Json.ToObject>();
+ var values = introspectionResponse.Json?.Deserialize>() ?? [];
values["aud"].ValueKind.Should().Be(JsonValueKind.String);
values["iss"].ValueKind.Should().Be(JsonValueKind.String);
@@ -227,7 +227,7 @@ public async Task Response_data_with_user_authentication_should_be_valid_using_s
Token = tokenResponse.AccessToken
});
- var values = introspectionResponse.Json.ToObject>();
+ var values = introspectionResponse.Json?.Deserialize>() ?? [];
values["aud"].ValueKind.Should().Be(JsonValueKind.String);
values["iss"].ValueKind.Should().Be(JsonValueKind.String);
@@ -266,7 +266,7 @@ public async Task Response_data_should_be_valid_using_multiple_scopes_multiple_a
Token = tokenResponse.AccessToken
});
- var values = introspectionResponse.Json.ToObject>();
+ var values = introspectionResponse.Json?.Deserialize>() ?? [];
values["aud"].GetType().Name.Should().Be("JsonElement");
@@ -317,7 +317,7 @@ public async Task Response_data_should_be_valid_using_multiple_scopes_single_aud
Token = tokenResponse.AccessToken
});
- var values = introspectionResponse.Json.ToObject>();
+ var values = introspectionResponse.Json?.Deserialize>() ?? [];
values["aud"].ValueKind.Should().Be(JsonValueKind.String);
values["aud"].ValueKind.Should().Be(JsonValueKind.String);
values["iss"].ValueKind.Should().Be(JsonValueKind.String);
diff --git a/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AccessTokenValidation.cs b/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AccessTokenValidation.cs
index 99097ed9..5592644e 100644
--- a/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AccessTokenValidation.cs
+++ b/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AccessTokenValidation.cs
@@ -31,12 +31,7 @@ public class AccessTokenValidation
private IClientStore _clients = Factory.CreateClientStore();
private IdentityServerOptions _options = new IdentityServerOptions();
- private StubClock _clock = new StubClock();
-
- static AccessTokenValidation()
- {
- JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
- }
+ private StubClock _clock = new StubClock();
private DateTime now;
public DateTime UtcNow
@@ -196,7 +191,7 @@ public async Task JWT_Token_with_scopes_have_expected_claims(bool flag)
result.Jwt.Should().NotBeNullOrEmpty();
result.Client.ClientId.Should().Be("roclient");
- result.Claims.Count().Should().Be(8);
+ result.Claims.Count().Should().Be(9);
var scopes = result.Claims.Where(c => c.Type == "scope").Select(c => c.Value).ToArray();
scopes.Count().Should().Be(2);
scopes[0].Should().Be("read");
diff --git a/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/IdentityTokenValidation.cs b/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/IdentityTokenValidation.cs
index d6e0ae69..94fa15c2 100644
--- a/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/IdentityTokenValidation.cs
+++ b/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/IdentityTokenValidation.cs
@@ -21,12 +21,7 @@ namespace IdentityServer.UnitTests.Validation
{
public class IdentityTokenValidation
{
- private const string Category = "Identity token validation";
-
- static IdentityTokenValidation()
- {
- JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
- }
+ private const string Category = "Identity token validation";
[Fact]
[Trait("Category", Category)]
diff --git a/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/ClientAssertionSecretParsing.cs b/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/ClientAssertionSecretParsing.cs
index cbf01dac..c8a751df 100644
--- a/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/ClientAssertionSecretParsing.cs
+++ b/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/ClientAssertionSecretParsing.cs
@@ -21,6 +21,8 @@ copies or substantial portions of the Software.
using IdentityServer8.Validation;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
+using Microsoft.IdentityModel.JsonWebTokens;
+using Microsoft.IdentityModel.Tokens;
using Xunit;
namespace IdentityServer.UnitTests.Validation.Secrets
@@ -53,8 +55,15 @@ public async void Valid_ClientAssertion()
{
var context = new DefaultHttpContext();
- var token = new JwtSecurityToken(issuer: "issuer", claims: new[] { new Claim("sub", "client") });
- var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
+ var tokenHandler = new JsonWebTokenHandler();
+
+ var tokenDescriptor = new SecurityTokenDescriptor
+ {
+ Issuer = "issuer",
+ Claims = new Dictionary{ ["sub"] = "client" },
+ };
+
+ var tokenString = tokenHandler.CreateToken(tokenDescriptor);
var body = "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=" + tokenString;
diff --git a/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/PrivateKeyJwtSecretValidation.cs b/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/PrivateKeyJwtSecretValidation.cs
index 9cd5af49..48a478ef 100644
--- a/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/PrivateKeyJwtSecretValidation.cs
+++ b/src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/PrivateKeyJwtSecretValidation.cs
@@ -27,6 +27,7 @@ copies or substantial portions of the Software.
using IdentityServer8.Stores;
using IdentityServer8.Validation;
using Microsoft.Extensions.Logging;
+using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using Xunit;
@@ -42,38 +43,47 @@ public PrivateKeyJwtSecretValidation()
_validator = new PrivateKeyJwtSecretValidator(
new MockHttpContextAccessor(
new IdentityServerOptions()
- {
- IssuerUri = "https://idsrv3.com"
- }
+ {
+ IssuerUri = "https://idsrv3.com"
+ }
),
- new DefaultReplayCache(new TestCache()),
+ new DefaultReplayCache(new TestCache()),
new LoggerFactory().CreateLogger()
);
_clients = new InMemoryClientStore(ClientValidationTestClients.Get());
}
- private JwtSecurityToken CreateToken(string clientId, DateTime? nowOverride = null)
+ private JsonWebToken CreateToken(string clientId, DateTime? nowOverride = null)
{
var certificate = TestCert.Load();
var now = nowOverride ?? DateTime.UtcNow;
- var token = new JwtSecurityToken(
- clientId,
- "https://idsrv3.com/connect/token",
- new List()
- {
- new Claim("jti", Guid.NewGuid().ToString()),
- new Claim(JwtClaimTypes.Subject, clientId),
- new Claim(JwtClaimTypes.IssuedAt, new DateTimeOffset(now).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
- },
- now,
- now.AddMinutes(1),
- new SigningCredentials(
- new X509SecurityKey(certificate),
- SecurityAlgorithms.RsaSha256
- )
- );
- return token;
+ var tokenHandler = new JsonWebTokenHandler();
+
+ var claims = new Dictionary
+ {
+ { "jti", Guid.NewGuid().ToString() },
+ { JwtClaimTypes.Subject, clientId },
+ { JwtClaimTypes.IssuedAt, new DateTimeOffset(now).ToUnixTimeSeconds() }
+ };
+
+ var tokenDescriptor = new SecurityTokenDescriptor
+ {
+ Issuer = clientId,
+ Audience = "https://idsrv3.com/connect/token",
+ Claims = claims,
+ Expires = now.AddMinutes(1),
+ SigningCredentials = new SigningCredentials(
+ new X509SecurityKey(certificate),
+ SecurityAlgorithms.RsaSha256
+ )
+ };
+
+ // Token string olarak oluşturuluyor
+ var tokenString = tokenHandler.CreateToken(tokenDescriptor);
+
+ // String token'ı JsonWebToken nesnesine dönüştürüp döndür
+ return tokenHandler.ReadJsonWebToken(tokenString);
}
[Fact]
@@ -86,7 +96,7 @@ public async Task Invalid_Certificate_X5t_Only_Requires_Full_Certificate()
var secret = new ParsedSecret
{
Id = clientId,
- Credential = new JwtSecurityTokenHandler().WriteToken(token),
+ Credential = $"{token.EncodedHeader}.{token.EncodedPayload}.{token.EncodedSignature}",
Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer
};
@@ -101,10 +111,11 @@ public async Task Invalid_Certificate_Thumbprint()
var clientId = "certificate_invalid";
var client = await _clients.FindEnabledClientByIdAsync(clientId);
+ var token = CreateToken(clientId);
var secret = new ParsedSecret
{
Id = clientId,
- Credential = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId)),
+ Credential = $"{token.EncodedHeader}.{token.EncodedPayload}.{token.EncodedSignature}",
Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer
};
@@ -119,10 +130,11 @@ public async Task Valid_Certificate_Base64()
var clientId = "certificate_base64_valid";
var client = await _clients.FindEnabledClientByIdAsync(clientId);
+ var token = CreateToken(clientId);
var secret = new ParsedSecret
{
Id = clientId,
- Credential = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId)),
+ Credential = $"{token.EncodedHeader}.{token.EncodedPayload}.{token.EncodedSignature}",
Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer
};
@@ -130,24 +142,24 @@ public async Task Valid_Certificate_Base64()
result.Success.Should().BeTrue();
}
-
+
[Fact]
public async Task Invalid_Replay()
{
var clientId = "certificate_base64_valid";
var client = await _clients.FindEnabledClientByIdAsync(clientId);
- var token = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId));
-
+
+ var token = CreateToken(clientId);
var secret = new ParsedSecret
{
Id = clientId,
- Credential = token,
+ Credential = $"{token.EncodedHeader}.{token.EncodedPayload}.{token.EncodedSignature}",
Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer
};
var result = await _validator.ValidateAsync(client.ClientSecrets, secret);
result.Success.Should().BeTrue();
-
+
result = await _validator.ValidateAsync(client.ClientSecrets, secret);
result.Success.Should().BeFalse();
}
@@ -158,10 +170,11 @@ public async Task Invalid_Certificate_Base64()
var clientId = "certificate_base64_invalid";
var client = await _clients.FindEnabledClientByIdAsync(clientId);
+ var token = CreateToken(clientId);
var secret = new ParsedSecret
{
Id = clientId,
- Credential = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId)),
+ Credential = $"{token.EncodedHeader}.{token.EncodedPayload}.{token.EncodedSignature}",
Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer
};
@@ -177,12 +190,27 @@ public async Task Invalid_Issuer()
var client = await _clients.FindEnabledClientByIdAsync(clientId);
var token = CreateToken(clientId);
- token.Payload.Remove(JwtClaimTypes.Issuer);
- token.Payload.Add(JwtClaimTypes.Issuer, "invalid");
+
+ // "iss" claim'ini manipüle et
+ var claims = token.Claims.ToList();
+ claims.RemoveAll(c => c.Type == JwtClaimTypes.Issuer);
+ claims.Add(new Claim(JwtClaimTypes.Issuer, "invalid"));
+
+ var modifiedToken = new JsonWebTokenHandler().CreateToken(new SecurityTokenDescriptor
+ {
+ Issuer = clientId,
+ Audience = "https://idsrv3.com/connect/token",
+ Claims = claims.ToDictionary(c => c.Type, c => (object) c.Value),
+ SigningCredentials = new SigningCredentials(
+ new X509SecurityKey(TestCert.Load()),
+ SecurityAlgorithms.RsaSha256
+ )
+ });
+
var secret = new ParsedSecret
{
Id = clientId,
- Credential = new JwtSecurityTokenHandler().WriteToken(token),
+ Credential = modifiedToken,
Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer
};
@@ -198,12 +226,27 @@ public async Task Invalid_Subject()
var client = await _clients.FindEnabledClientByIdAsync(clientId);
var token = CreateToken(clientId);
- token.Payload.Remove(JwtClaimTypes.Subject);
- token.Payload.Add(JwtClaimTypes.Subject, "invalid");
+
+ // "sub" claim'ini manipüle et
+ var claims = token.Claims.ToList();
+ claims.RemoveAll(c => c.Type == JwtClaimTypes.Subject);
+ claims.Add(new Claim(JwtClaimTypes.Subject, "invalid"));
+
+ var modifiedToken = new JsonWebTokenHandler().CreateToken(new SecurityTokenDescriptor
+ {
+ Issuer = clientId,
+ Audience = "https://idsrv3.com/connect/token",
+ Claims = claims.ToDictionary(c => c.Type, c => (object) c.Value),
+ SigningCredentials = new SigningCredentials(
+ new X509SecurityKey(TestCert.Load()),
+ SecurityAlgorithms.RsaSha256
+ )
+ });
+
var secret = new ParsedSecret
{
Id = clientId,
- Credential = new JwtSecurityTokenHandler().WriteToken(token),
+ Credential = modifiedToken,
Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer
};
@@ -212,6 +255,7 @@ public async Task Invalid_Subject()
result.Success.Should().BeFalse();
}
+
[Fact]
public async Task Invalid_Expired_Token()
{
@@ -222,7 +266,7 @@ public async Task Invalid_Expired_Token()
var secret = new ParsedSecret
{
Id = clientId,
- Credential = new JwtSecurityTokenHandler().WriteToken(token),
+ Credential = $"{token.EncodedHeader}.{token.EncodedPayload}.{token.EncodedSignature}",
Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer
};
@@ -237,13 +281,35 @@ public async Task Invalid_Unsigned_Token()
var clientId = "certificate_valid";
var client = await _clients.FindEnabledClientByIdAsync(clientId);
- var token = CreateToken(clientId);
- token.Header.Remove("alg");
- token.Header.Add("alg", "none");
+ // Unsigned token için header ve claims oluştur
+ var header = new Dictionary
+ {
+ { "alg", "none" }
+ };
+
+ var claims = new Dictionary
+ {
+ { "jti", Guid.NewGuid().ToString() },
+ { JwtClaimTypes.Subject, clientId },
+ { JwtClaimTypes.Issuer, clientId },
+ { JwtClaimTypes.IssuedAt, new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds() }
+ };
+
+ // Header ve Claims JSON string'e dönüştürülüyor
+ var headerJson = System.Text.Json.JsonSerializer.Serialize(header);
+ var claimsJson = System.Text.Json.JsonSerializer.Serialize(claims);
+
+ // JSON string Base64Url formatına dönüştürülüyor
+ var encodedHeader = Base64Url.Encode(System.Text.Encoding.UTF8.GetBytes(headerJson));
+ var encodedClaims = Base64Url.Encode(System.Text.Encoding.UTF8.GetBytes(claimsJson));
+
+ // İmzasız JWT oluşturuluyor
+ var token = $"{encodedHeader}.{encodedClaims}.";
+
var secret = new ParsedSecret
{
Id = clientId,
- Credential = new JwtSecurityTokenHandler().WriteToken(token),
+ Credential = token,
Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer
};
@@ -251,5 +317,7 @@ public async Task Invalid_Unsigned_Token()
result.Success.Should().BeFalse();
}
+
+
}
}
diff --git a/version.json b/version.json
index 06202593..bd0d0882 100644
--- a/version.json
+++ b/version.json
@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
- "version": "8.0.4-beta.4",
+ "version": "8.0.5-preview-net9",
"nugetPackageVersion": {
"semVer": 2, // optional. Set to either 1 or 2 to control how the NuGet package version string is generated. Default is 1.
"precision": "revision"