mirror of
https://github.com/DuendeSoftware/products
synced 2026-05-24 09:28:24 +00:00
Make CT required in IIdentityServerInteractionService and IBackchannelAuthenticationInteractionService.CompleteLoginRequestAsync, flow through implementations, callers, and tests
This commit is contained in:
parent
944920ff30
commit
f54d124340
22 changed files with 117 additions and 94 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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!);
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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!);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue