Add CT parameter to IConsentService and IResourceValidator, flow through all implementations and tests

This commit is contained in:
Damian Hickey 2026-02-20 19:16:34 +01:00
parent 5df903ae44
commit 81ec361709
16 changed files with 69 additions and 62 deletions

View file

@ -322,7 +322,7 @@ public class AuthorizeInteractionResponseGenerator : IAuthorizeInteractionRespon
throw new ArgumentException("Invalid PromptMode");
}
var consentRequired = await Consent.RequiresConsentAsync(request.Subject, request.Client, request.ValidatedResources.ParsedScopes);
var consentRequired = await Consent.RequiresConsentAsync(request.Subject, request.Client, request.ValidatedResources.ParsedScopes, default);
if (consentRequired && request.PromptModes.Contains(OidcConstants.PromptModes.None))
{
@ -399,7 +399,7 @@ public class AuthorizeInteractionResponseGenerator : IAuthorizeInteractionRespon
Logger.LogDebug("User indicated to remember consent for scopes: {scopes}", request.ValidatedResources.RawScopeValues);
}
await Consent.UpdateConsentAsync(request.Subject, request.Client, parsedScopes);
await Consent.UpdateConsentAsync(request.Subject, request.Client, parsedScopes, default);
}
}
}

View file

@ -63,7 +63,7 @@ public class DefaultBackchannelAuthenticationInteractionService : IBackchannelAu
Client = client,
Scopes = request.RequestedScopes,
ResourceIndicators = request.RequestedResourceIndicators,
});
}, ct);
return new BackchannelUserLoginRequest
{

View file

@ -51,6 +51,7 @@ public class DefaultConsentService : IConsentService
/// <param name="subject">The user.</param>
/// <param name="client">The client.</param>
/// <param name="parsedScopes">The parsed scopes.</param>
/// <param name="ct">The <see cref="CT"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// Boolean if consent is required.
/// </returns>
@ -59,7 +60,7 @@ public class DefaultConsentService : IConsentService
/// or
/// subject
/// </exception>
public virtual async Task<bool> RequiresConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable<ParsedScopeValue> parsedScopes)
public virtual async Task<bool> RequiresConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable<ParsedScopeValue> parsedScopes, CT ct)
{
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultConsentService.RequiresConsent");
@ -100,7 +101,7 @@ public class DefaultConsentService : IConsentService
return true;
}
var consent = await UserConsentStore.GetUserConsentAsync(subject.GetSubjectId(), client.ClientId, default);
var consent = await UserConsentStore.GetUserConsentAsync(subject.GetSubjectId(), client.ClientId, ct);
if (consent == null)
{
@ -111,7 +112,7 @@ public class DefaultConsentService : IConsentService
if (consent.Expiration.HasExpired(TimeProvider.GetUtcNow().UtcDateTime))
{
Logger.LogDebug("Consent found in consent store is expired, consent is required");
await UserConsentStore.RemoveUserConsentAsync(consent.SubjectId, consent.ClientId, default);
await UserConsentStore.RemoveUserConsentAsync(consent.SubjectId, consent.ClientId, ct);
return true;
}
@ -143,13 +144,14 @@ public class DefaultConsentService : IConsentService
/// <param name="client">The client.</param>
/// <param name="subject">The subject.</param>
/// <param name="parsedScopes">The parsed scopes.</param>
/// <param name="ct">The <see cref="CT"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">
/// client
/// or
/// subject
/// </exception>
public virtual async Task UpdateConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable<ParsedScopeValue> parsedScopes)
public virtual async Task UpdateConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable<ParsedScopeValue> parsedScopes, CT ct)
{
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultConsentService.UpdateConsent");
@ -179,13 +181,13 @@ public class DefaultConsentService : IConsentService
consent.Expiration = consent.CreationTime.AddSeconds(client.ConsentLifetime.Value);
}
await UserConsentStore.StoreUserConsentAsync(consent, default);
await UserConsentStore.StoreUserConsentAsync(consent, ct);
}
else
{
Logger.LogDebug("Client allows remembering consent, and no scopes provided. Removing consent from consent store for subject: {subject}", subject.GetSubjectId());
await UserConsentStore.RemoveUserConsentAsync(subjectId, clientId, default);
await UserConsentStore.RemoveUserConsentAsync(subjectId, clientId, ct);
}
}
}

View file

@ -49,7 +49,7 @@ internal class DefaultDeviceFlowInteractionService : IDeviceFlowInteractionServi
{
Client = client,
Scopes = deviceAuth.RequestedScopes,
});
}, ct);
return new DeviceFlowAuthorizationRequest
{

View file

@ -21,10 +21,11 @@ public interface IConsentService
/// <param name="subject">The user.</param>
/// <param name="client">The client.</param>
/// <param name="parsedScopes">The parsed scopes.</param>
/// <param name="ct">The <see cref="CT"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// Boolean if consent is required.
/// </returns>
Task<bool> RequiresConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable<ParsedScopeValue> parsedScopes);
Task<bool> RequiresConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable<ParsedScopeValue> parsedScopes, CT ct);
/// <summary>
/// Updates the consent.
@ -32,6 +33,7 @@ public interface IConsentService
/// <param name="subject">The subject.</param>
/// <param name="client">The client.</param>
/// <param name="parsedScopes">The parsed scopes.</param>
/// <param name="ct">The <see cref="CT"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns></returns>
Task UpdateConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable<ParsedScopeValue> parsedScopes);
Task UpdateConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable<ParsedScopeValue> parsedScopes, CT ct);
}

View file

@ -551,7 +551,7 @@ internal class AuthorizeRequestValidator : IAuthorizeRequestValidator
Client = request.Client,
Scopes = request.RequestedScopes,
ResourceIndicators = resourceIndicators,
});
}, default);
if (!validatedResources.Succeeded)
{

View file

@ -165,7 +165,7 @@ internal class BackchannelAuthenticationRequestValidator : IBackchannelAuthentic
Client = _validatedRequest.Client,
Scopes = _validatedRequest.RequestedScopes,
ResourceIndicators = resourceIndicators,
});
}, default);
if (!validatedResources.Succeeded)
{

View file

@ -32,7 +32,7 @@ public class DefaultResourceValidator : IResourceValidator
}
/// <inheritdoc/>
public virtual async Task<ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request)
public virtual async Task<ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request, CT ct)
{
ArgumentNullException.ThrowIfNull(request);
using var activity = Tracing.ValidationActivitySource.StartActivity("DefaultResourceValidator.ValidateRequestedResources");
@ -55,7 +55,7 @@ public class DefaultResourceValidator : IResourceValidator
var scopeNames = parsedScopesResult.ParsedScopes.Select(x => x.ParsedName).Distinct().ToArray();
// todo: this API might want to pass resource indicators to better filter
var scopeResourcesFromStore = await _store.FindEnabledResourcesByScopeAsync(scopeNames, default);
var scopeResourcesFromStore = await _store.FindEnabledResourcesByScopeAsync(scopeNames, ct);
if (request.ResourceIndicators?.Any() == true)
{

View file

@ -148,7 +148,7 @@ internal class DeviceAuthorizationRequestValidator : IDeviceAuthorizationRequest
{
Client = request.Client,
Scopes = request.RequestedScopes
});
}, default);
if (!validatedResources.Succeeded)
{

View file

@ -465,7 +465,7 @@ internal class TokenRequestValidator : ITokenRequestValidator
Client = _validatedRequest.Client,
Scopes = _validatedRequest.AuthorizationCode.RequestedScopes,
ResourceIndicators = _validatedRequest.AuthorizationCode.RequestedResourceIndicators,
});
}, _ct);
if (!validatedResources.Succeeded)
{
@ -813,7 +813,7 @@ internal class TokenRequestValidator : ITokenRequestValidator
Client = _validatedRequest.Client,
Scopes = _validatedRequest.RefreshToken.AuthorizedScopes,
ResourceIndicators = resourceIndicators,
});
}, _ct);
if (!validatedResources.Succeeded)
{
@ -895,7 +895,7 @@ internal class TokenRequestValidator : ITokenRequestValidator
Client = _validatedRequest.Client,
Scopes = _validatedRequest.DeviceCode.AuthorizedScopes,
ResourceIndicators = null // not supported for device grant
});
}, _ct);
if (!validatedResources.Succeeded)
{
@ -984,7 +984,7 @@ internal class TokenRequestValidator : ITokenRequestValidator
Client = _validatedRequest.Client,
Scopes = _validatedRequest.BackChannelAuthenticationRequest.AuthorizedScopes,
ResourceIndicators = _validatedRequest.BackChannelAuthenticationRequest.RequestedResourceIndicators,
});
}, _ct);
if (!validatedResources.Succeeded)
{
@ -1157,7 +1157,7 @@ internal class TokenRequestValidator : ITokenRequestValidator
Client = _validatedRequest.Client,
Scopes = requestedScopes,
ResourceIndicators = resourceIndicators,
});
}, _ct);
if (!resourceValidationResult.Succeeded)
{

View file

@ -16,5 +16,7 @@ public interface IResourceValidator
/// <summary>
/// Validates the requested resources for the client.
/// </summary>
Task<ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request);
/// <param name="request">The resource validation request.</param>
/// <param name="ct">The <see cref="CT"/> used to propagate notifications that the operation should be canceled.</param>
Task<ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request, CT ct);
}

View file

@ -12,5 +12,5 @@ internal class MockResourceValidator : IResourceValidator
public Task<IEnumerable<ParsedScopeValue>> ParseRequestedScopesAsync(IEnumerable<string> scopeValues) => Task.FromResult(scopeValues.Select(x => new ParsedScopeValue(x)));
public Task<ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request) => Task.FromResult(Result);
public Task<ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request, CT ct) => Task.FromResult(Result);
}

View file

@ -13,13 +13,13 @@ public class MockConsentService : IConsentService
{
public bool RequiresConsentResult { get; set; }
public Task<bool> RequiresConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable<ParsedScopeValue> parsedScopes) => Task.FromResult(RequiresConsentResult);
public Task<bool> RequiresConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable<ParsedScopeValue> parsedScopes, CT ct) => Task.FromResult(RequiresConsentResult);
public ClaimsPrincipal ConsentSubject { get; set; }
public Client ConsentClient { get; set; }
public IEnumerable<string> ConsentScopes { get; set; }
public Task UpdateConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable<ParsedScopeValue> parsedScopes)
public Task UpdateConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable<ParsedScopeValue> parsedScopes, CT ct)
{
ConsentSubject = subject;
ConsentClient = client;

View file

@ -12,5 +12,5 @@ internal class MockResourceValidator : IResourceValidator
public Task<IEnumerable<ParsedScopeValue>> ParseRequestedScopesAsync(IEnumerable<string> scopeValues) => Task.FromResult(scopeValues.Select(x => new ParsedScopeValue(x)));
public Task<ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request) => Task.FromResult(Result);
public Task<ResourceValidationResult> ValidateRequestedResourcesAsync(ResourceValidationRequest request, CT ct) => Task.FromResult(Result);
}

View file

@ -71,7 +71,7 @@ public class DefaultConsentServiceTests
{
_client.AllowRememberConsent = false;
await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") });
await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }, _ct);
var consent = await _userConsentStore.GetUserConsentAsync(_user.GetSubjectId(), _client.ClientId, _ct);
consent.ShouldBeNull();
@ -80,7 +80,7 @@ public class DefaultConsentServiceTests
[Fact]
public async Task UpdateConsentAsync_should_persist_consent()
{
await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") });
await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }, _ct);
var consent = await _userConsentStore.GetUserConsentAsync(_user.GetSubjectId(), _client.ClientId, _ct);
consent.Scopes.Count().ShouldBe(2);
@ -91,9 +91,9 @@ public class DefaultConsentServiceTests
[Fact]
public async Task UpdateConsentAsync_empty_scopes_should_remove_consent()
{
await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") });
await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }, _ct);
await _subject.UpdateConsentAsync(_user, _client, new ParsedScopeValue[] { });
await _subject.UpdateConsentAsync(_user, _client, new ParsedScopeValue[] { }, _ct);
var consent = await _userConsentStore.GetUserConsentAsync(_user.GetSubjectId(), _client.ClientId, _ct);
consent.ShouldBeNull();
@ -104,7 +104,7 @@ public class DefaultConsentServiceTests
{
_client.RequireConsent = false;
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") });
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }, _ct);
result.ShouldBeFalse();
}
@ -114,7 +114,7 @@ public class DefaultConsentServiceTests
{
_client.AllowRememberConsent = false;
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") });
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }, _ct);
result.ShouldBeTrue();
}
@ -122,7 +122,7 @@ public class DefaultConsentServiceTests
[Fact]
public async Task RequiresConsentAsync_no_scopes_should_not_require_consent()
{
var result = await _subject.RequiresConsentAsync(_user, _client, new ParsedScopeValue[] { });
var result = await _subject.RequiresConsentAsync(_user, _client, new ParsedScopeValue[] { }, _ct);
result.ShouldBeFalse();
}
@ -130,7 +130,7 @@ public class DefaultConsentServiceTests
[Fact]
public async Task RequiresConsentAsync_offline_access_should_require_consent()
{
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("offline_access") });
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("offline_access") }, _ct);
result.ShouldBeTrue();
}
@ -138,7 +138,7 @@ public class DefaultConsentServiceTests
[Fact]
public async Task RequiresConsentAsync_no_prior_consent_should_require_consent()
{
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") });
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }, _ct);
result.ShouldBeTrue();
}
@ -146,9 +146,9 @@ public class DefaultConsentServiceTests
[Fact]
public async Task RequiresConsentAsync_prior_consent_should_not_require_consent()
{
await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") });
await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }, _ct);
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") });
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }, _ct);
result.ShouldBeFalse();
}
@ -156,9 +156,9 @@ public class DefaultConsentServiceTests
[Fact]
public async Task RequiresConsentAsync_prior_consent_with_more_scopes_should_not_require_consent()
{
await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2"), new ParsedScopeValue("scope3") });
await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2"), new ParsedScopeValue("scope3") }, _ct);
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope2") });
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope2") }, _ct);
result.ShouldBeFalse();
}
@ -166,9 +166,9 @@ public class DefaultConsentServiceTests
[Fact]
public async Task RequiresConsentAsync_prior_consent_with_too_few_scopes_should_require_consent()
{
await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope2"), new ParsedScopeValue("scope3") });
await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope2"), new ParsedScopeValue("scope3") }, _ct);
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") });
var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }, _ct);
result.ShouldBeTrue();
}
@ -181,12 +181,12 @@ public class DefaultConsentServiceTests
var scopes = new[] { new ParsedScopeValue("foo"), new ParsedScopeValue("bar") };
_client.ConsentLifetime = 2;
await _subject.UpdateConsentAsync(_user, _client, scopes);
await _subject.UpdateConsentAsync(_user, _client, scopes, _ct);
now = now.AddSeconds(3);
_timeProvider.SetUtcNow(now);
var result = await _subject.RequiresConsentAsync(_user, _client, scopes);
var result = await _subject.RequiresConsentAsync(_user, _client, scopes, _ct);
result.ShouldBeTrue();
}
@ -199,12 +199,12 @@ public class DefaultConsentServiceTests
var scopes = new[] { new ParsedScopeValue("foo"), new ParsedScopeValue("bar") };
_client.ConsentLifetime = 2;
await _subject.UpdateConsentAsync(_user, _client, scopes);
await _subject.UpdateConsentAsync(_user, _client, scopes, _ct);
now = now.AddSeconds(3);
_timeProvider.SetUtcNow(now);
await _subject.RequiresConsentAsync(_user, _client, scopes);
await _subject.RequiresConsentAsync(_user, _client, scopes, _ct);
var result = await _userConsentStore.GetUserConsentAsync(_user.GetSubjectId(), _client.ClientId, _ct);

View file

@ -103,6 +103,7 @@ public class ResourceValidation
};
private IResourceStore _subject;
private readonly CT _ct = TestContext.Current.CancellationToken;
public ResourceValidation() => _subject = new InMemoryResourcesStore(_identityResources, _apiResources, _scopes);
@ -117,7 +118,7 @@ public class ResourceValidation
{
Client = _restrictedClient,
Scopes = new[] { "offline_access" }
});
}, _ct);
result.Succeeded.ShouldBeFalse();
result.InvalidScopes.ShouldContain("offline_access");
@ -132,7 +133,7 @@ public class ResourceValidation
{
Client = _restrictedClient,
Scopes = new[] { "openid", "scope1" }
});
}, _ct);
result.Succeeded.ShouldBeTrue();
result.InvalidScopes.ShouldBeEmpty();
@ -148,7 +149,7 @@ public class ResourceValidation
{
Client = _restrictedClient,
Scopes = new[] { "openid", "email", "scope1", "unknown" }
});
}, _ct);
result.Succeeded.ShouldBeFalse();
result.InvalidScopes.ShouldContain("unknown");
@ -160,7 +161,7 @@ public class ResourceValidation
{
Client = _restrictedClient,
Scopes = new[] { "openid", "scope1", "scope2" }
});
}, _ct);
result.Succeeded.ShouldBeFalse();
result.InvalidScopes.ShouldContain("scope2");
@ -171,7 +172,7 @@ public class ResourceValidation
{
Client = _restrictedClient,
Scopes = new[] { "openid", "email", "scope1" }
});
}, _ct);
result.Succeeded.ShouldBeFalse();
result.InvalidScopes.ShouldContain("email");
@ -187,7 +188,7 @@ public class ResourceValidation
{
Client = _restrictedClient,
Scopes = new[] { "openid", "scope1", "disabled_scope" }
});
}, _ct);
result.Succeeded.ShouldBeFalse();
result.InvalidScopes.ShouldContain("disabled_scope");
@ -202,7 +203,7 @@ public class ResourceValidation
{
Client = _restrictedClient,
Scopes = new[] { "openid", "scope1" }
});
}, _ct);
result.Succeeded.ShouldBeTrue();
result.InvalidScopes.ShouldBeEmpty();
@ -217,7 +218,7 @@ public class ResourceValidation
{
Client = _restrictedClient,
Scopes = new[] { "openid", "email", "scope1", "scope2" }
});
}, _ct);
result.Succeeded.ShouldBeFalse();
result.InvalidScopes.ShouldContain("email");
@ -233,7 +234,7 @@ public class ResourceValidation
{
Client = _restrictedClient,
Scopes = new[] { "openid", "scope1" }
});
}, _ct);
result.Succeeded.ShouldBeTrue();
result.Resources.IdentityResources.Select(x => x.Name).ShouldBe(["openid"]);
@ -250,7 +251,7 @@ public class ResourceValidation
{
Client = _restrictedClient,
Scopes = new[] { "scope1" }
});
}, _ct);
result.Succeeded.ShouldBeTrue();
result.Resources.IdentityResources.ShouldBeEmpty();
@ -267,7 +268,7 @@ public class ResourceValidation
{
Client = _restrictedClient,
Scopes = new[] { "openid" }
});
}, _ct);
result.Succeeded.ShouldBeTrue();
result.Resources.IdentityResources.Select(x => x.Name).ShouldContain("openid");
@ -291,7 +292,7 @@ public class ResourceValidation
{
Client = new Client { AllowedScopes = { "s" } },
Scopes = new[] { "s" }
});
}, _ct);
result.Succeeded.ShouldBeTrue();
result.Resources.ApiResources.Count.ShouldBe(2);
@ -312,7 +313,7 @@ public class ResourceValidation
Client = _resourceClient,
Scopes = new[] { "scope1", "offline_access" },
ResourceIndicators = new[] { "isolated1" },
});
}, _ct);
result.Succeeded.ShouldBeTrue();
result.Resources.ApiResources.Select(x => x.Name).ShouldBe(["resource1", "isolated1"]);
@ -329,7 +330,7 @@ public class ResourceValidation
{
Client = _resourceClient,
Scopes = new[] { "scope1" },
});
}, _ct);
result.Succeeded.ShouldBeTrue();
result.Resources.ApiResources.Select(x => x.Name).ShouldBe(["resource1"]);
@ -346,7 +347,7 @@ public class ResourceValidation
Client = _resourceClient,
Scopes = new[] { "scope1" },
ResourceIndicators = new[] { "invalid" }
});
}, _ct);
result.Succeeded.ShouldBeFalse();
result.InvalidScopes.ShouldBeEmpty();
@ -363,7 +364,7 @@ public class ResourceValidation
Client = _resourceClient,
Scopes = new[] { "scope1" },
ResourceIndicators = new[] { "resource3" }
});
}, _ct);
result.Succeeded.ShouldBeFalse();
result.InvalidScopes.ShouldBeEmpty();