Make CT required in IIdentityServerInteractionService and IBackchannelAuthenticationInteractionService.CompleteLoginRequestAsync, flow through implementations, callers, and tests

This commit is contained in:
Damian Hickey 2026-02-20 23:26:10 +01:00
parent 944920ff30
commit f54d124340
22 changed files with 117 additions and 94 deletions

View file

@ -64,7 +64,7 @@ public class Index : PageModel
public async Task<IActionResult> OnPost()
{
// check if we are in the context of an authorization request
var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);
var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl, HttpContext.RequestAborted);
// the user clicked the "cancel" button
if (Input.Button != "login")
@ -77,7 +77,7 @@ public class Index : PageModel
// if the user cancels, send a result back into IdentityServer as if they
// denied the consent (even if this client does not require consent).
// this will send back an access denied OIDC error response to the client.
await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied);
await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied, HttpContext.RequestAborted);
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
if (context.IsNativeClient())
@ -158,7 +158,7 @@ public class Index : PageModel
ReturnUrl = returnUrl
};
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
var context = await _interaction.GetAuthorizationContextAsync(returnUrl, HttpContext.RequestAborted);
if (context?.IdP != null)
{
var scheme = await _schemeProvider.GetSchemeAsync(context.IdP);

View file

@ -47,7 +47,7 @@ public class Index : PageModel
}
else
{
var context = await _interaction.GetLogoutContextAsync(LogoutId);
var context = await _interaction.GetLogoutContextAsync(LogoutId, HttpContext.RequestAborted);
if (context?.ShowSignoutPrompt == false)
{
// it's safe to automatically sign-out
@ -72,7 +72,7 @@ public class Index : PageModel
// if there's no current logout context, we need to create one
// this captures necessary info from the current logged in user
// this can still return null if there is no context needed
LogoutId ??= await _interaction.CreateLogoutContextAsync();
LogoutId ??= await _interaction.CreateLogoutContextAsync(HttpContext.RequestAborted);
// delete local authentication cookie
await _signInManager.SignOutAsync();

View file

@ -100,7 +100,7 @@ public class Callback : PageModel
var returnUrl = result.Properties.Items["returnUrl"] ?? "~/";
// check if external login is in the context of an OIDC request
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
var context = await _interaction.GetAuthorizationContextAsync(returnUrl, HttpContext.RequestAborted);
await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id, user.UserName, true, context?.Client.ClientId), HttpContext.RequestAborted);
Duende.IdentityServer.UI.Pages.Telemetry.Metrics.UserLogin(context?.Client.ClientId, provider!);

View file

@ -39,7 +39,7 @@ public class Index : PageModel
public async Task<IActionResult> OnPost()
{
// check if we are in the context of an authorization request
var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);
var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl, HttpContext.RequestAborted);
// the user clicked the "cancel" button
if (Input.Button != "create")
@ -49,7 +49,7 @@ public class Index : PageModel
// if the user cancels, send a result back into IdentityServer as if they
// denied the consent (even if this client does not require consent).
// this will send back an access denied OIDC error response to the client.
await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied);
await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied, HttpContext.RequestAborted);
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
if (context.IsNativeClient())

View file

@ -60,7 +60,7 @@ public class Index : PageModel
public async Task<IActionResult> OnPost()
{
// check if we are in the context of an authorization request
var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);
var context = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl, HttpContext.RequestAborted);
// the user clicked the "cancel" button
if (Input.Button != "login")
@ -73,7 +73,7 @@ public class Index : PageModel
// if the user cancels, send a result back into IdentityServer as if they
// denied the consent (even if this client does not require consent).
// this will send back an access denied OIDC error response to the client.
await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied);
await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied, HttpContext.RequestAborted);
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
if (context.IsNativeClient())
@ -168,7 +168,7 @@ public class Index : PageModel
ReturnUrl = returnUrl
};
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
var context = await _interaction.GetAuthorizationContextAsync(returnUrl, HttpContext.RequestAborted);
if (context?.IdP != null)
{
var scheme = await _schemeProvider.GetSchemeAsync(context.IdP);

View file

@ -41,7 +41,7 @@ public class Index : PageModel
}
else
{
var context = await _interaction.GetLogoutContextAsync(LogoutId);
var context = await _interaction.GetLogoutContextAsync(LogoutId, HttpContext.RequestAborted);
if (context?.ShowSignoutPrompt == false)
{
// it's safe to automatically sign-out
@ -66,7 +66,7 @@ public class Index : PageModel
// if there's no current logout context, we need to create one
// this captures necessary info from the current logged in user
// this can still return null if there is no context needed
LogoutId ??= await _interaction.CreateLogoutContextAsync();
LogoutId ??= await _interaction.CreateLogoutContextAsync(HttpContext.RequestAborted);
// delete local authentication cookie
await HttpContext.SignOutAsync();

View file

@ -20,7 +20,7 @@ public class LoggedOut : PageModel
public async Task OnGet(string? logoutId)
{
// get context information (client name, post logout redirect URI and iframe for federated signout)
var logout = await _interactionService.GetLogoutContextAsync(logoutId);
var logout = await _interactionService.GetLogoutContextAsync(logoutId, HttpContext.RequestAborted);
View = new LoggedOutViewModel
{

View file

@ -108,7 +108,7 @@ public class Consent : PageModel
if (result != null)
{
// communicate outcome of consent back to identityserver
await _interaction.CompleteLoginRequestAsync(result);
await _interaction.CompleteLoginRequestAsync(result, HttpContext.RequestAborted);
return RedirectToPage("/Ciba/All");
}

View file

@ -54,7 +54,7 @@ public class Index : PageModel
public async Task<IActionResult> OnPost()
{
// validate return url is still valid
var request = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl);
var request = await _interaction.GetAuthorizationContextAsync(Input.ReturnUrl, HttpContext.RequestAborted);
if (request == null)
{
return RedirectToPage("/Home/Error/Index");
@ -111,7 +111,7 @@ public class Index : PageModel
ArgumentNullException.ThrowIfNull(Input.ReturnUrl, nameof(Input.ReturnUrl));
// communicate outcome of consent back to identityserver
await _interaction.GrantConsentAsync(request, grantedConsent);
await _interaction.GrantConsentAsync(request, grantedConsent, HttpContext.RequestAborted);
// redirect back to authorization endpoint
if (request.IsNativeClient() == true)
@ -136,7 +136,7 @@ public class Index : PageModel
{
ArgumentNullException.ThrowIfNull(returnUrl);
var request = await _interaction.GetAuthorizationContextAsync(returnUrl);
var request = await _interaction.GetAuthorizationContextAsync(returnUrl, HttpContext.RequestAborted);
if (request != null)
{
View = CreateConsentViewModel(request);

View file

@ -103,7 +103,7 @@ public class Callback : PageModel
var returnUrl = result.Properties.Items["returnUrl"] ?? "~/";
// check if external login is in the context of an OIDC request
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
var context = await _interaction.GetAuthorizationContextAsync(returnUrl, HttpContext.RequestAborted);
await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.Client.ClientId), HttpContext.RequestAborted);
Telemetry.Metrics.UserLogin(context?.Client.ClientId, provider!);

View file

@ -35,7 +35,7 @@ public class Index : PageModel
public async Task OnGet()
{
var grants = await _interaction.GetAllUserGrantsAsync();
var grants = await _interaction.GetAllUserGrantsAsync(HttpContext.RequestAborted);
var list = new List<GrantViewModel>();
foreach (var grant in grants)
@ -73,7 +73,7 @@ public class Index : PageModel
public async Task<IActionResult> OnPost()
{
await _interaction.RevokeUserConsentAsync(ClientId);
await _interaction.RevokeUserConsentAsync(ClientId, HttpContext.RequestAborted);
await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), ClientId), HttpContext.RequestAborted);
Telemetry.Metrics.GrantsRevoked(ClientId);

View file

@ -25,7 +25,7 @@ public class Index : PageModel
public async Task OnGet(string? errorId)
{
// retrieve error details from identityserver
var message = await _interaction.GetErrorContextAsync(errorId);
var message = await _interaction.GetErrorContextAsync(errorId, HttpContext.RequestAborted);
if (message != null)
{
View.Error = message;

View file

@ -117,19 +117,19 @@ public class DefaultBackchannelAuthenticationInteractionService : IBackchannelAu
}
/// <inheritdoc/>
public async Task CompleteLoginRequestAsync(CompleteBackchannelLoginRequest completionRequest)
public async Task CompleteLoginRequestAsync(CompleteBackchannelLoginRequest completionRequest, CT ct)
{
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultBackchannelAuthenticationInteractionService.CompleteLoginRequest");
ArgumentNullException.ThrowIfNull(completionRequest);
var request = await _requestStore.GetByInternalIdAsync(completionRequest.InternalId, default);
var request = await _requestStore.GetByInternalIdAsync(completionRequest.InternalId, ct);
if (request == null)
{
throw new InvalidOperationException("Invalid backchannel authentication request id.");
}
var subject = completionRequest.Subject ?? await _session.GetUserAsync(default);
var subject = completionRequest.Subject ?? await _session.GetUserAsync(ct);
if (subject == null)
{
throw new InvalidOperationException("Invalid subject.");
@ -141,7 +141,7 @@ public class DefaultBackchannelAuthenticationInteractionService : IBackchannelAu
}
var sid = (completionRequest.Subject == null) ?
await _session.GetSessionIdAsync(default) :
await _session.GetSessionIdAsync(ct) :
completionRequest.SessionId;
if (completionRequest.ScopesValuesConsented != null)
@ -170,7 +170,7 @@ public class DefaultBackchannelAuthenticationInteractionService : IBackchannelAu
request.AuthorizedScopes = completionRequest.ScopesValuesConsented;
request.Description = completionRequest.Description;
await _requestStore.UpdateByInternalIdAsync(completionRequest.InternalId, request, default);
await _requestStore.UpdateByInternalIdAsync(completionRequest.InternalId, request, ct);
_logger.LogDebug("Successful update for backchannel authentication request id {id}", completionRequest.InternalId);
}

View file

@ -44,11 +44,12 @@ internal class DefaultIdentityServerInteractionService : IIdentityServerInteract
_logger = logger;
}
public async Task<AuthorizationRequest> GetAuthorizationContextAsync(string returnUrl)
/// <inheritdoc/>
public async Task<AuthorizationRequest> GetAuthorizationContextAsync(string returnUrl, CT ct)
{
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultIdentityServerInteractionService.GetAuthorizationContext");
var result = await _returnUrlParser.ParseAsync(returnUrl, default);
var result = await _returnUrlParser.ParseAsync(returnUrl, ct);
if (result != null)
{
@ -62,33 +63,35 @@ internal class DefaultIdentityServerInteractionService : IIdentityServerInteract
return result;
}
public async Task<LogoutRequest> GetLogoutContextAsync(string logoutId)
/// <inheritdoc/>
public async Task<LogoutRequest> GetLogoutContextAsync(string logoutId, CT ct)
{
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultIdentityServerInteractionService.GetLogoutContext");
var msg = await _logoutMessageStore.ReadAsync(logoutId, default);
var msg = await _logoutMessageStore.ReadAsync(logoutId, ct);
var iframeUrl = await _context.HttpContext.GetIdentityServerSignoutFrameCallbackUrlAsync(msg?.Data);
return new LogoutRequest(iframeUrl, msg?.Data);
}
public async Task<string> CreateLogoutContextAsync()
/// <inheritdoc/>
public async Task<string> CreateLogoutContextAsync(CT ct)
{
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultIdentityServerInteractionService.CreateLogoutContext");
var user = await _userSession.GetUserAsync(default);
var user = await _userSession.GetUserAsync(ct);
if (user != null)
{
var clientIds = await _userSession.GetClientListAsync(default);
var clientIds = await _userSession.GetClientListAsync(ct);
if (clientIds.Any())
{
var sid = await _userSession.GetSessionIdAsync(default);
var sid = await _userSession.GetSessionIdAsync(ct);
var msg = new Message<LogoutMessage>(new LogoutMessage
{
SubjectId = user.GetSubjectId(),
SessionId = sid,
ClientIds = clientIds
}, _timeProvider.GetUtcNow().UtcDateTime);
var id = await _logoutMessageStore.WriteAsync(msg, default);
var id = await _logoutMessageStore.WriteAsync(msg, ct);
return id;
}
}
@ -96,13 +99,14 @@ internal class DefaultIdentityServerInteractionService : IIdentityServerInteract
return null;
}
public async Task<ErrorMessage> GetErrorContextAsync(string errorId)
/// <inheritdoc/>
public async Task<ErrorMessage> GetErrorContextAsync(string errorId, CT ct)
{
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultIdentityServerInteractionService.GetErrorContext");
if (errorId != null)
{
var result = await _errorMessageStore.ReadAsync(errorId, default);
var result = await _errorMessageStore.ReadAsync(errorId, ct);
var data = result?.Data;
if (data != null)
{
@ -120,13 +124,14 @@ internal class DefaultIdentityServerInteractionService : IIdentityServerInteract
return null;
}
public async Task GrantConsentAsync(AuthorizationRequest request, ConsentResponse consent, string subject = null)
/// <inheritdoc/>
public async Task GrantConsentAsync(AuthorizationRequest request, ConsentResponse consent, CT ct, string subject = null)
{
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultIdentityServerInteractionService.GrantConsent");
if (subject == null)
{
var user = await _userSession.GetUserAsync(default);
var user = await _userSession.GetUserAsync(ct);
subject = user?.GetSubjectId();
}
@ -136,10 +141,11 @@ internal class DefaultIdentityServerInteractionService : IIdentityServerInteract
}
var consentRequest = new ConsentRequest(request, subject);
await _consentMessageStore.WriteAsync(consentRequest.Id, new Message<ConsentResponse>(consent, _timeProvider.GetUtcNow().UtcDateTime), default);
await _consentMessageStore.WriteAsync(consentRequest.Id, new Message<ConsentResponse>(consent, _timeProvider.GetUtcNow().UtcDateTime), ct);
}
public Task DenyAuthorizationAsync(AuthorizationRequest request, AuthorizationError error, string errorDescription = null)
/// <inheritdoc/>
public Task DenyAuthorizationAsync(AuthorizationRequest request, AuthorizationError error, CT ct, string errorDescription = null)
{
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultIdentityServerInteractionService.DenyAuthorization");
@ -148,7 +154,7 @@ internal class DefaultIdentityServerInteractionService : IIdentityServerInteract
Error = error,
ErrorDescription = errorDescription
};
return GrantConsentAsync(request, response);
return GrantConsentAsync(request, response, ct);
}
public bool IsValidReturnUrl(string returnUrl)
@ -169,42 +175,45 @@ internal class DefaultIdentityServerInteractionService : IIdentityServerInteract
return result;
}
public async Task<IEnumerable<Grant>> GetAllUserGrantsAsync()
/// <inheritdoc/>
public async Task<IEnumerable<Grant>> GetAllUserGrantsAsync(CT ct)
{
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultIdentityServerInteractionService.GetAllUserGrants");
var user = await _userSession.GetUserAsync(default);
var user = await _userSession.GetUserAsync(ct);
if (user != null)
{
var subject = user.GetSubjectId();
return await _grants.GetAllGrantsAsync(subject, default);
return await _grants.GetAllGrantsAsync(subject, ct);
}
return Enumerable.Empty<Grant>();
}
public async Task RevokeUserConsentAsync(string clientId)
/// <inheritdoc/>
public async Task RevokeUserConsentAsync(string clientId, CT ct)
{
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultIdentityServerInteractionService.RevokeUserConsent");
var user = await _userSession.GetUserAsync(default);
var user = await _userSession.GetUserAsync(ct);
if (user != null)
{
var subject = user.GetSubjectId();
await _grants.RemoveAllGrantsAsync(subject, clientId);
await _grants.RemoveAllGrantsAsync(subject, clientId, ct: ct);
}
}
public async Task RevokeTokensForCurrentSessionAsync()
/// <inheritdoc/>
public async Task RevokeTokensForCurrentSessionAsync(CT ct)
{
using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultIdentityServerInteractionService.RevokeTokensForCurrentSession");
var user = await _userSession.GetUserAsync(default);
var user = await _userSession.GetUserAsync(ct);
if (user != null)
{
var subject = user.GetSubjectId();
var sessionId = await _userSession.GetSessionIdAsync(default);
await _grants.RemoveAllGrantsAsync(subject, sessionId: sessionId);
var sessionId = await _userSession.GetSessionIdAsync(ct);
await _grants.RemoveAllGrantsAsync(subject, sessionId: sessionId, ct: ct);
}
}
}

View file

@ -27,7 +27,9 @@ public interface IBackchannelAuthenticationInteractionService
/// <summary>
/// Completes the login request with the provided response for the current user or the subject passed.
/// </summary>
Task CompleteLoginRequestAsync(CompleteBackchannelLoginRequest completionRequest);
/// <param name="completionRequest">The completion request.</param>
/// <param name="ct">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
Task CompleteLoginRequestAsync(CompleteBackchannelLoginRequest completionRequest, CT ct);
}
/// <summary>

View file

@ -17,7 +17,8 @@ public interface IIdentityServerInteractionService
/// Gets the authorization context.
/// </summary>
/// <param name="returnUrl">The return URL.</param>
Task<AuthorizationRequest?> GetAuthorizationContextAsync(string? returnUrl);
/// <param name="ct">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
Task<AuthorizationRequest?> GetAuthorizationContextAsync(string? returnUrl, CT ct);
/// <summary>
/// Indicates if the returnUrl is a valid URL for redirect after login or consent.
@ -29,27 +30,31 @@ public interface IIdentityServerInteractionService
/// Gets the error context.
/// </summary>
/// <param name="errorId">The error identifier.</param>
Task<ErrorMessage?> GetErrorContextAsync(string? errorId);
/// <param name="ct">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
Task<ErrorMessage?> GetErrorContextAsync(string? errorId, CT ct);
/// <summary>
/// Gets the logout context.
/// </summary>
/// <param name="logoutId">The logout identifier.</param>
Task<LogoutRequest> GetLogoutContextAsync(string? logoutId);
/// <param name="ct">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
Task<LogoutRequest> GetLogoutContextAsync(string? logoutId, CT ct);
/// <summary>
/// Used to create a logoutId if there is not one presently.
/// </summary>
/// <param name="ct">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
/// <returns></returns>
Task<string?> CreateLogoutContextAsync();
Task<string?> CreateLogoutContextAsync(CT ct);
/// <summary>
/// Informs IdentityServer of the user's consent.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="consent">The consent.</param>
/// <param name="ct">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
/// <param name="subject">The subject.</param>
Task GrantConsentAsync(AuthorizationRequest request, ConsentResponse consent, string? subject = null);
Task GrantConsentAsync(AuthorizationRequest request, ConsentResponse consent, CT ct, string? subject = null);
/// <summary>
/// Triggers error back to the client for the authorization request.
@ -57,22 +62,26 @@ public interface IIdentityServerInteractionService
/// </summary>
/// <param name="request">The request.</param>
/// <param name="error"></param>
/// <param name="ct">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
/// <param name="errorDescription"></param>
Task DenyAuthorizationAsync(AuthorizationRequest request, AuthorizationError error, string? errorDescription = null);
Task DenyAuthorizationAsync(AuthorizationRequest request, AuthorizationError error, CT ct, string? errorDescription = null);
/// <summary>
/// Returns a collection representing all of the user's consents and grants.
/// </summary>
Task<IEnumerable<Grant>> GetAllUserGrantsAsync();
/// <param name="ct">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
Task<IEnumerable<Grant>> GetAllUserGrantsAsync(CT ct);
/// <summary>
/// Revokes all a user's consents and grants for a given client, or for all clients if clientId is null.
/// </summary>
/// <param name="clientId">The client identifier.</param>
Task RevokeUserConsentAsync(string? clientId);
/// <param name="ct">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
Task RevokeUserConsentAsync(string? clientId, CT ct);
/// <summary>
/// Revokes all of a user's consents and grants for clients the user has signed into during their current session.
/// </summary>
Task RevokeTokensForCurrentSessionAsync();
/// <param name="ct">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
Task RevokeTokensForCurrentSessionAsync(CT ct);
}

View file

@ -251,7 +251,7 @@ public class IdentityServerPipeline
CreateAccountWasCalled = true;
var interaction = ctx.RequestServices.GetRequiredService<IIdentityServerInteractionService>();
CreateAccountReturnUrl = ctx.Request.Query[Options.UserInteraction.CreateAccountReturnUrlParameter].FirstOrDefault();
CreateAccountRequest = await interaction.GetAuthorizationContextAsync(CreateAccountReturnUrl);
CreateAccountRequest = await interaction.GetAuthorizationContextAsync(CreateAccountReturnUrl, ctx.RequestAborted);
await IssueLoginCookie(ctx);
}
@ -259,7 +259,7 @@ public class IdentityServerPipeline
{
var interaction = ctx.RequestServices.GetRequiredService<IIdentityServerInteractionService>();
LoginReturnUrl = ctx.Request.Query[Options.UserInteraction.LoginReturnUrlParameter].FirstOrDefault();
LoginRequest = await interaction.GetAuthorizationContextAsync(LoginReturnUrl);
LoginRequest = await interaction.GetAuthorizationContextAsync(LoginReturnUrl, ctx.RequestAborted);
}
private async Task IssueLoginCookie(HttpContext ctx)
@ -290,7 +290,7 @@ public class IdentityServerPipeline
private async Task ReadLogoutRequest(HttpContext ctx)
{
var interaction = ctx.RequestServices.GetRequiredService<IIdentityServerInteractionService>();
LogoutRequest = await interaction.GetLogoutContextAsync(ctx.Request.Query["logoutId"].FirstOrDefault());
LogoutRequest = await interaction.GetLogoutContextAsync(ctx.Request.Query["logoutId"].FirstOrDefault(), ctx.RequestAborted);
}
public bool ConsentWasCalled { get; set; }
@ -306,14 +306,14 @@ public class IdentityServerPipeline
private async Task ReadConsentMessage(HttpContext ctx)
{
var interaction = ctx.RequestServices.GetRequiredService<IIdentityServerInteractionService>();
ConsentRequest = await interaction.GetAuthorizationContextAsync(ctx.Request.Query["returnUrl"].FirstOrDefault());
ConsentRequest = await interaction.GetAuthorizationContextAsync(ctx.Request.Query["returnUrl"].FirstOrDefault(), ctx.RequestAborted);
}
private async Task CreateConsentResponse(HttpContext ctx)
{
if (ConsentRequest != null && ConsentResponse != null)
{
var interaction = ctx.RequestServices.GetRequiredService<IIdentityServerInteractionService>();
await interaction.GrantConsentAsync(ConsentRequest, ConsentResponse);
await interaction.GrantConsentAsync(ConsentRequest, ConsentResponse, ctx.RequestAborted);
ConsentResponse = null;
var url = ctx.Request.Query[Options.UserInteraction.ConsentReturnUrlParameter].FirstOrDefault();
@ -331,7 +331,7 @@ public class IdentityServerPipeline
{
CustomWasCalled = true;
var interaction = ctx.RequestServices.GetRequiredService<IIdentityServerInteractionService>();
CustomRequest = await interaction.GetAuthorizationContextAsync(ctx.Request.Query[Options.UserInteraction.ConsentReturnUrlParameter].FirstOrDefault());
CustomRequest = await interaction.GetAuthorizationContextAsync(ctx.Request.Query[Options.UserInteraction.ConsentReturnUrlParameter].FirstOrDefault(), ctx.RequestAborted);
}
public bool ErrorWasCalled { get; set; }
@ -347,7 +347,7 @@ public class IdentityServerPipeline
private async Task ReadErrorMessage(HttpContext ctx)
{
var interaction = ctx.RequestServices.GetRequiredService<IIdentityServerInteractionService>();
ErrorMessage = await interaction.GetErrorContextAsync(ctx.Request.Query["errorId"].FirstOrDefault());
ErrorMessage = await interaction.GetErrorContextAsync(ctx.Request.Query["errorId"].FirstOrDefault(), ctx.RequestAborted);
}
/* helpers */

View file

@ -157,7 +157,7 @@ public class CibaTokenEndpointTests
IdentityProvider = IdentityServerConstants.LocalIdentityProvider,
}
.CreatePrincipal()
});
}, _ct);
// token request
@ -264,7 +264,7 @@ public class CibaTokenEndpointTests
IdentityProvider = IdentityServerConstants.LocalIdentityProvider,
}
.CreatePrincipal()
});
}, _ct);
// token request
@ -327,7 +327,7 @@ public class CibaTokenEndpointTests
IdentityProvider = IdentityServerConstants.LocalIdentityProvider,
}
.CreatePrincipal()
});
}, _ct);
// token request
@ -390,7 +390,7 @@ public class CibaTokenEndpointTests
IdentityProvider = IdentityServerConstants.LocalIdentityProvider,
}
.CreatePrincipal()
});
}, _ct);
// token request
@ -453,7 +453,7 @@ public class CibaTokenEndpointTests
IdentityProvider = IdentityServerConstants.LocalIdentityProvider,
}
.CreatePrincipal()
});
}, _ct);
// token request
@ -522,7 +522,7 @@ public class CibaTokenEndpointTests
IdentityProvider = IdentityServerConstants.LocalIdentityProvider,
}
.CreatePrincipal()
});
}, _ct);
// token request

View file

@ -86,7 +86,7 @@ public class DynamicProvidersTests
app.MapGet("/account/logout", async ctx =>
{
var isis = ctx.RequestServices.GetRequiredService<IIdentityServerInteractionService>();
var logoutCtx = await isis.GetLogoutContextAsync(ctx.Request.Query["logoutId"]);
var logoutCtx = await isis.GetLogoutContextAsync(ctx.Request.Query["logoutId"], ctx.RequestAborted);
Idp1FrontChannelLogoutUri = logoutCtx.SignOutIFrameUrl;
await ctx.SignOutAsync();
});

View file

@ -108,7 +108,7 @@ public class DefaultBackchannelAuthenticationInteractionServiceTests
AdditionalClaims = { new Claim("foo", "bar") },
AuthenticationMethods = { "phone", "pin" }
}.CreatePrincipal()
});
}, _ct);
var item = _mockStore.Items[requestId];
item.IsComplete.ShouldBeTrue();
@ -136,7 +136,7 @@ public class DefaultBackchannelAuthenticationInteractionServiceTests
};
var requestId = await _mockStore.CreateRequestAsync(req, _ct);
var f = async () => await _subject.CompleteLoginRequestAsync(null);
var f = async () => await _subject.CompleteLoginRequestAsync(null, _ct);
await f.ShouldThrowAsync<ArgumentNullException>();
}
@ -165,7 +165,7 @@ public class DefaultBackchannelAuthenticationInteractionServiceTests
AdditionalClaims = { new Claim("foo", "bar") },
AuthenticationMethods = { "phone", "pin" }
}.CreatePrincipal()
});
}, _ct);
var exception = await f.ShouldThrowAsync<InvalidOperationException>();
exception.Message.ShouldBe("More scopes consented than originally requested.");
}
@ -195,7 +195,7 @@ public class DefaultBackchannelAuthenticationInteractionServiceTests
AdditionalClaims = { new Claim("foo", "bar") },
AuthenticationMethods = { "phone", "pin" }
}.CreatePrincipal()
});
}, _ct);
var exception = await f.ShouldThrowAsync<InvalidOperationException>();
exception.Message.ShouldBe("User's subject id: 'invalid' does not match subject id for backchannel authentication request: '123'.");
}
@ -224,7 +224,7 @@ public class DefaultBackchannelAuthenticationInteractionServiceTests
// AdditionalClaims = { new Claim("foo", "bar") },
// AuthenticationMethods = { "phone", "pin" }
//}.CreatePrincipal()
});
}, _ct);
var exception = await f.ShouldThrowAsync<InvalidOperationException>();
exception.Message.ShouldBe("Invalid subject.");
}
@ -253,7 +253,7 @@ public class DefaultBackchannelAuthenticationInteractionServiceTests
AdditionalClaims = { new Claim("foo", "bar") },
AuthenticationMethods = { "phone", "pin" }
}.CreatePrincipal()
});
}, _ct);
var exception = await f.ShouldThrowAsync<InvalidOperationException>();
exception.Message.ShouldBe("Invalid backchannel authentication request id.");
}
@ -286,7 +286,7 @@ public class DefaultBackchannelAuthenticationInteractionServiceTests
ScopesValuesConsented = new string[] { "scope1", "scope2" },
SessionId = "ignored",
//Subject =
});
}, _ct);
var item = _mockStore.Items[requestId];
item.SessionId.ShouldBe("session id");
@ -324,7 +324,7 @@ public class DefaultBackchannelAuthenticationInteractionServiceTests
AdditionalClaims = { new Claim("foo", "bar") },
AuthenticationMethods = { "phone", "pin" }
}.CreatePrincipal()
});
}, _ct);
var item = _mockStore.Items[requestId];
item.Subject.HasClaim("idp", "local").ShouldBeTrue();

View file

@ -16,6 +16,8 @@ namespace UnitTests.Services.Default;
public class DefaultIdentityServerInteractionServiceTests
{
private readonly CT _ct = TestContext.Current.CancellationToken;
private DefaultIdentityServerInteractionService _subject;
private IdentityServerOptions _options = new IdentityServerOptions();
@ -62,7 +64,7 @@ public class DefaultIdentityServerInteractionServiceTests
_mockUserSession.SessionId = null;
_mockLogoutMessageStore.Messages.Add("id", new Message<LogoutMessage>(new LogoutMessage() { SessionId = "session" }));
var context = await _subject.GetLogoutContextAsync("id");
var context = await _subject.GetLogoutContextAsync("id", _ct);
context.SignOutIFrameUrl.ShouldBeNull();
}
@ -77,7 +79,7 @@ public class DefaultIdentityServerInteractionServiceTests
_mockUserSession.SessionId = "session";
_mockUserSession.User = new IdentityServerUser("123").CreatePrincipal();
var context = await _subject.GetLogoutContextAsync(null);
var context = await _subject.GetLogoutContextAsync(null, _ct);
context.SignOutIFrameUrl.ShouldBeNull();
}
@ -94,7 +96,7 @@ public class DefaultIdentityServerInteractionServiceTests
_mockUserSession.SessionId = "session";
_mockUserSession.User = new IdentityServerUser("123").CreatePrincipal();
var context = await _subject.GetLogoutContextAsync(null);
var context = await _subject.GetLogoutContextAsync(null, _ct);
context.SignOutIFrameUrl.ShouldNotBeNull();
}
@ -105,7 +107,7 @@ public class DefaultIdentityServerInteractionServiceTests
_mockUserSession.SessionId = null;
_mockLogoutMessageStore.Messages.Add("id", new Message<LogoutMessage>(new LogoutMessage()));
var context = await _subject.GetLogoutContextAsync("id");
var context = await _subject.GetLogoutContextAsync("id", _ct);
context.SignOutIFrameUrl.ShouldBeNull();
}
@ -113,7 +115,7 @@ public class DefaultIdentityServerInteractionServiceTests
[Fact]
public async Task CreateLogoutContextAsync_without_session_should_not_create_session()
{
var context = await _subject.CreateLogoutContextAsync();
var context = await _subject.CreateLogoutContextAsync(_ct);
context.ShouldBeNull();
_mockLogoutMessageStore.Messages.ShouldBeEmpty();
@ -126,7 +128,7 @@ public class DefaultIdentityServerInteractionServiceTests
_mockUserSession.User = new IdentityServerUser("123").CreatePrincipal();
_mockUserSession.SessionId = "session";
var context = await _subject.CreateLogoutContextAsync();
var context = await _subject.CreateLogoutContextAsync(_ct);
context.ShouldNotBeNull();
_mockLogoutMessageStore.Messages.ShouldNotBeEmpty();
@ -138,6 +140,7 @@ public class DefaultIdentityServerInteractionServiceTests
var act = () => _subject.GrantConsentAsync(
new AuthorizationRequest(),
new ConsentResponse() { ScopesValuesConsented = new[] { "openid" } },
_ct,
null);
var exception = await act.ShouldThrowAsync<ArgumentNullException>();
@ -152,7 +155,7 @@ public class DefaultIdentityServerInteractionServiceTests
Client = new Client { ClientId = "client" },
ValidatedResources = _resourceValidationResult
};
await _subject.GrantConsentAsync(req, new ConsentResponse { Error = AuthorizationError.AccessDenied }, null);
await _subject.GrantConsentAsync(req, new ConsentResponse { Error = AuthorizationError.AccessDenied }, _ct, null);
}
[Fact]
@ -165,7 +168,7 @@ public class DefaultIdentityServerInteractionServiceTests
Client = new Client { ClientId = "client" },
ValidatedResources = _resourceValidationResult
};
await _subject.GrantConsentAsync(req, new ConsentResponse(), null);
await _subject.GrantConsentAsync(req, new ConsentResponse(), _ct, null);
_mockConsentStore.Messages.ShouldNotBeEmpty();
var consentRequest = new ConsentRequest(req, "bob");

View file

@ -67,7 +67,7 @@ public class IsLocalUrlTests
{
var interactionService = new DefaultIdentityServerInteractionService(null, null, null, null, null, null, null,
GetReturnUrlParser(), new LoggerFactory().CreateLogger<DefaultIdentityServerInteractionService>());
var actual = await interactionService.GetAuthorizationContextAsync(returnUrl);
var actual = await interactionService.GetAuthorizationContextAsync(returnUrl, _ct);
if (expected)
{
actual.ShouldNotBeNull();