Skip to content

Commit 781949a

Browse files
authored
Addresses an unhandled exception path & nullability concerns (#1930)
Updates the `DefaultScopeParser` to return gracefully rather than throwing an exception (which is ultimately unhandled). Introduces a new unit test to cover `AuthorizedScopes` being null (as apposed to an empty collection).
1 parent 1726bbe commit 781949a

File tree

3 files changed

+68
-18
lines changed

3 files changed

+68
-18
lines changed

identity-server/src/IdentityServer/Validation/Default/DefaultScopeParser.cs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Duende Software. All rights reserved.
22
// See LICENSE in the project root for license information.
33

4-
54
using Duende.IdentityServer.Extensions;
65
using Microsoft.Extensions.Logging;
76

@@ -10,29 +9,24 @@ namespace Duende.IdentityServer.Validation;
109
/// <summary>
1110
/// Default implementation of IScopeParser.
1211
/// </summary>
13-
public class DefaultScopeParser : IScopeParser
12+
/// <param name="logger"></param>
13+
public class DefaultScopeParser(ILogger<DefaultScopeParser> logger) : IScopeParser
1414
{
15-
private readonly ILogger<DefaultScopeParser> _logger;
16-
17-
/// <summary>
18-
/// Ctor.
19-
/// </summary>
20-
/// <param name="logger"></param>
21-
public DefaultScopeParser(ILogger<DefaultScopeParser> logger)
22-
{
23-
_logger = logger;
24-
}
25-
2615
/// <inheritdoc/>
2716
public ParsedScopesResult ParseScopeValues(IEnumerable<string> scopeValues)
2817
{
2918
using var activity = Tracing.ValidationActivitySource.StartActivity("DefaultScopeParser.ParseScopeValues");
3019
activity?.SetTag(Tracing.Properties.Scope, scopeValues.ToSpaceSeparatedString());
3120

32-
if (scopeValues == null) throw new ArgumentNullException(nameof(scopeValues));
33-
3421
var result = new ParsedScopesResult();
3522

23+
if (scopeValues is null)
24+
{
25+
logger.LogError("A collection of scopes cannot be null.");
26+
result.Errors.Add(new ParsedScopeValidationError("null", "A collection of scopes cannot be null."));
27+
return result;
28+
}
29+
3630
foreach (var scopeValue in scopeValues)
3731
{
3832
var ctx = new ParseScopeContext(scopeValue);
@@ -52,7 +46,7 @@ public ParsedScopesResult ParseScopeValues(IEnumerable<string> scopeValues)
5246
}
5347
else
5448
{
55-
_logger.LogDebug("Scope parsing ignoring scope {scope}", scopeValue.SanitizeLogParameter());
49+
logger.LogDebug("Scope parsing ignoring scope {scope}", scopeValue.SanitizeLogParameter());
5650
}
5751
}
5852

identity-server/test/IdentityServer.UnitTests/Services/Default/DefaultRefreshTokenServiceTests.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Duende Software. All rights reserved.
22
// See LICENSE in the project root for license information.
33

4-
54
using System.Security.Claims;
65
using Duende.IdentityServer;
76
using Duende.IdentityServer.Models;
@@ -109,7 +108,6 @@ public async Task CreateRefreshToken_should_match_sliding_lifetime()
109108
refreshToken.Lifetime.ShouldBe(client.SlidingRefreshTokenLifetime);
110109
}
111110

112-
113111
[Fact]
114112
public async Task UpdateRefreshToken_one_time_use_should_create_new_token()
115113
{
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) Duende Software. All rights reserved.
2+
// See LICENSE in the project root for license information.
3+
4+
using System.Collections.Specialized;
5+
using System.Security.Claims;
6+
using Duende.IdentityModel;
7+
using Duende.IdentityServer;
8+
using Duende.IdentityServer.Models;
9+
using Duende.IdentityServer.Stores;
10+
using UnitTests.Validation.Setup;
11+
12+
namespace UnitTests.Validation.TokenRequest_Validation;
13+
14+
public class TokenRequestValidation_Invalid
15+
{
16+
private const string Category = "TokenRequest Validation - General - Invalid";
17+
18+
private readonly IClientStore _clients = Factory.CreateClientStore();
19+
20+
[Fact]
21+
[Trait("Category", Category)]
22+
public async Task Invalid_refresh_token_request_should_fail()
23+
{
24+
var subjectClaim = new Claim(JwtClaimTypes.Subject, "foo");
25+
26+
var refreshToken = new RefreshToken
27+
{
28+
ClientId = "roclient",
29+
Subject = new IdentityServerUser(subjectClaim.Value).CreatePrincipal(),
30+
AuthorizedScopes = null, // Passing a null value to AuthorizedScopes should be invalid
31+
Lifetime = 600,
32+
CreationTime = DateTime.UtcNow
33+
};
34+
35+
refreshToken.SetAccessToken(new Token("access_token")
36+
{
37+
Claims = [subjectClaim],
38+
ClientId = "roclient"
39+
});
40+
41+
var grants = Factory.CreateRefreshTokenStore();
42+
var handle = await grants.StoreRefreshTokenAsync(refreshToken);
43+
44+
var client = await _clients.FindEnabledClientByIdAsync("roclient");
45+
46+
var validator = Factory.CreateTokenRequestValidator(refreshTokenStore: grants);
47+
48+
var parameters = new NameValueCollection
49+
{
50+
{ OidcConstants.TokenRequest.GrantType, "refresh_token" },
51+
{ OidcConstants.TokenRequest.RefreshToken, handle }
52+
};
53+
54+
var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult());
55+
56+
result.IsError.ShouldBeTrue();
57+
}
58+
}

0 commit comments

Comments
 (0)