Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks;
using Duende.IdentityServer.Models;
using IdentityExpress.Identity;
using Duende.IdentityServer.Services;
Expand All @@ -20,9 +21,6 @@ public class AccountControllerUnitTests
{
private readonly Mock<UserManager<IdentityExpressUser>> mockUserManager;
private readonly Mock<IIdentityServerInteractionService> mockInteraction;
private readonly Mock<IClientStore> mockClientStore;
private readonly Mock<IHttpContextAccessor> mockAccessor;
private readonly Mock<IAuthenticationSchemeProvider> mockSchemeProvider;
private readonly Mock<IEventService> mockEvents;
private readonly Mock<IAccountService> mockAccountService;
private readonly Mock<IUrlHelperFactory> mockUrlHelper;
Expand All @@ -34,11 +32,8 @@ public class AccountControllerUnitTests
public AccountControllerUnitTests()
{
mockInteraction = new Mock<IIdentityServerInteractionService>();
mockClientStore = new Mock<IClientStore>();
mockAccessor = new Mock<IHttpContextAccessor>();
var userStoreMock = new Mock<IUserStore<IdentityExpressUser>>();
mockUserManager = new Mock<UserManager<IdentityExpressUser>>(userStoreMock.Object, null, null, null, null, null, null, null, null);
mockSchemeProvider = new Mock<IAuthenticationSchemeProvider>();
mockEvents = new Mock<IEventService>();
mockAccountService = new Mock<IAccountService>();
mockUrlHelper = new Mock<IUrlHelperFactory>();
Expand Down Expand Up @@ -83,14 +78,14 @@ public async Task Login_WhenNotExternal_ShouldCallBuildLoginViewModelAsync()
Username = "LoginHunt",
};

mockAccountService.Setup(x => x.BuildLoginViewModelAsync(It.IsAny<string>()))
mockAccountService.Setup(x => x.BuildLoginViewModelAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(testLoginViewModel);

var sut = CreateSut();

var actual = await sut.Login(testReturnUrl);

mockAccountService.Verify(x => x.BuildLoginViewModelAsync(testReturnUrl));
mockAccountService.Verify(x => x.BuildLoginViewModelAsync(testReturnUrl, CancellationToken.None));

var viewResult = Assert.IsType<ViewResult>(actual);
Assert.Equal(viewResult.Model, testLoginViewModel);
Expand Down Expand Up @@ -132,7 +127,7 @@ public async Task Login_WhenModelPassedIn_WithButtonNotLogin_AndNullAuthContext_
ReturnUrl = "https://some.return.com",
};

mockInteraction.Setup(x => x.GetAuthorizationContextAsync(It.IsAny<string>()))
mockInteraction.Setup(x => x.GetAuthorizationContextAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(() => null);

var sut = CreateSut();
Expand All @@ -155,13 +150,13 @@ public async Task Login_WhenModelPassedIn_WithButtonNotLogin_AndAuthContextRetur

var authRequest = new AuthorizationRequest();

mockInteraction.Setup(x => x.GetAuthorizationContextAsync(It.IsAny<string>()))
mockInteraction.Setup(x => x.GetAuthorizationContextAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(() => authRequest);

var sut = CreateSut();
var actual = await sut.Login(testLoginModel, "cancel");

mockInteraction.Verify(x => x.DenyAuthorizationAsync(authRequest, AuthorizationError.AccessDenied, null));
mockInteraction.Verify(x => x.DenyAuthorizationAsync(authRequest, InteractionError.AccessDenied, CancellationToken.None, null));

var viewResult = Assert.IsType<RedirectResult>(actual);
Assert.Equal(testLoginModel.ReturnUrl, viewResult.Url);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using IdentityModel;
using Duende.IdentityServer.Models;
Expand Down Expand Up @@ -82,13 +83,13 @@ public async Task BuildLoggedOutViewModelAsync_WhenSuccessfulLogout_ShowCorrectL
};

LogoutRequest logoutRequest = new LogoutRequest(iFrameUrl, logoutMessge);
mockInteraction.Setup(x => x.GetLogoutContextAsync(logoutId))
mockInteraction.Setup(x => x.GetLogoutContextAsync(logoutId, It.IsAny<CancellationToken>()))
.ReturnsAsync(logoutRequest).Verifiable();

mockAccessor.SetupGet(x => x.HttpContext.User).Returns(user).Verifiable();

//act
var result = await CreateSut().BuildLoggedOutViewModelAsync(logoutId);
var result = await CreateSut().BuildLoggedOutViewModelAsync(logoutId, CancellationToken.None);

//assert
Assert.True(result.AutomaticRedirectAfterSignOut);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.*"/>
<PackageReference Include="FluentAssertions" Version="7.2.0" />
<PackageReference Include="IdentityModel.AspNetCore.OAuth2Introspection" Version="6.2.0"/>
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="10.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="10.0.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
<PackageReference Include="Moq" Version="4.20.72"/>
<PackageReference Include="xunit" Version="2.9.3"/>
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Stores;
Expand All @@ -8,7 +9,7 @@
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.Configuration;
using Moq;
using Rsk.AspNetCore.Authentication.Saml2p;
// using Rsk.AspNetCore.Authentication.Saml2p;
using Rsk.Samples.IdentityServer.AdminUiIntegration.Models;
using Rsk.Samples.IdentityServer.AdminUiIntegration.Services;
using Xunit;
Expand All @@ -25,7 +26,7 @@ public class ExternalProvidersServiceTests
{
new AuthenticationScheme("Cookies", null, typeof(CookieAuthenticationHandler)),
new AuthenticationScheme("rsk-oidc-scheme", "RSK OIDC Scheme", typeof(OpenIdConnectHandler)),
new AuthenticationScheme("rsk-saml-scheme", "RSK SAML Scheme", typeof(Saml2pAuthenticationHandler)),
// new AuthenticationScheme("rsk-saml-scheme", "RSK SAML Scheme", typeof(Saml2pAuthenticationHandler)),
};

private readonly List<IdentityProviderName> fakeDuendeSchemes = new List<IdentityProviderName>
Expand All @@ -40,7 +41,7 @@ public ExternalProvidersServiceTests()
authenticationSchemeProviderMock.Setup(x => x.GetAllSchemesAsync())
.ReturnsAsync(fakeAuthSchemesIncRsk);

identityProviderStoreMock.Setup(x => x.GetAllSchemeNamesAsync())
identityProviderStoreMock.Setup(x => x.GetAllSchemeNamesAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(fakeDuendeSchemes);
}

Expand All @@ -64,7 +65,7 @@ [Fact] public async Task GetAll_WhenDisabled_ShouldBeEmpty()

var sut = CreateSut();

var actual = await sut.GetAll();
var actual = await sut.GetAll(CancellationToken.None);

actual.Should().BeEmpty();
}
Expand All @@ -75,7 +76,7 @@ [Fact] public async Task GetAll_WhenDuende_ShouldOnlyReturnDuendeEnabledDynamicP

var sut = CreateSut();

var actual = await sut.GetAll();
var actual = await sut.GetAll(CancellationToken.None);

var expected = new List<ExternalProvider>
{
Expand All @@ -92,7 +93,7 @@ [Fact] public async Task GetAll_WhenRsk_ShouldOnlyReturnRskDynamicProviders()

var sut = CreateSut();

var actual = await sut.GetAll();
var actual = await sut.GetAll(CancellationToken.None);

var expected = new List<ExternalProvider>
{
Expand All @@ -110,11 +111,11 @@ public async Task GetScheme_WhenDisabled_ShouldNotReturnAnything()

var sut = CreateSut();

var actualDuende = await sut.GetScheme("duende-oidc-scheme");
var actualDuende = await sut.GetScheme("duende-oidc-scheme", CancellationToken.None);

actualDuende.Should().BeNull();

var actualRsk = await sut.GetScheme("rsk-oidc-scheme");
var actualRsk = await sut.GetScheme("rsk-oidc-scheme", CancellationToken.None);

actualRsk.Should().BeNull();
}
Expand All @@ -126,13 +127,13 @@ public async Task GetScheme_WhenDuende_ShouldNotReturnDuendeExternalProvider()

var sut = CreateSut();

var actualDuende = await sut.GetScheme("duende-oidc-scheme");
var actualDuende = await sut.GetScheme("duende-oidc-scheme", CancellationToken.None);

actualDuende.Should().NotBeNull();
actualDuende.AuthenticationScheme.Should().Be("duende-oidc-scheme");
actualDuende.DisplayName.Should().Be("Duende OIDC Scheme");

var actualRsk = await sut.GetScheme("rsk-saml-scheme");
var actualRsk = await sut.GetScheme("rsk-saml-scheme", CancellationToken.None);

actualRsk.Should().BeNull();
}
Expand All @@ -144,11 +145,11 @@ public async Task GetScheme_WhenRsk_ShouldNotReturnRskExternalProvider()

var sut = CreateSut();

var actualDuende = await sut.GetScheme("duende-oidc-scheme");
var actualDuende = await sut.GetScheme("duende-oidc-scheme", CancellationToken.None);

actualDuende.Should().BeNull();

var actualRsk = await sut.GetScheme("rsk-saml-scheme");
var actualRsk = await sut.GetScheme("rsk-saml-scheme", CancellationToken.None);

actualRsk.Should().NotBeNull();
actualRsk.AuthenticationScheme.Should().Be("rsk-saml-scheme");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Duende.IdentityServer;
using Duende.IdentityServer.Events;
Expand Down Expand Up @@ -66,9 +65,11 @@ public async Task<IActionResult> Login(string returnUrl)
{
bool externalLogin = Request.Cookies["Identity.External"] != null;

CancellationToken cancellationToken = HttpContext.RequestAborted;

// build a model so we know what to show on the login page
// if were told we a linking an external login then then we build a model
var vm = !externalLogin ? await accountService.BuildLoginViewModelAsync(returnUrl)
var vm = !externalLogin ? await accountService.BuildLoginViewModelAsync(returnUrl, cancellationToken)
: accountService.BuildLinkLoginViewModel(returnUrl);

if (!externalLogin && vm.IsExternalLoginOnly)
Expand All @@ -87,6 +88,8 @@ public async Task<IActionResult> Login(string returnUrl)
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model, string button)
{
CancellationToken cancellationToken = HttpContext.RequestAborted;

if (button != "login")
{
if (Request.Cookies["Identity.External"] != null)
Expand All @@ -95,13 +98,13 @@ public async Task<IActionResult> Login(LoginInputModel model, string button)
}

// the user clicked the "cancel" button
var context = await interaction.GetAuthorizationContextAsync(model.ReturnUrl);
AuthorizationRequest context = await interaction.GetAuthorizationContextAsync(model.ReturnUrl, cancellationToken);
if (context != null)
{
// 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, InteractionError.AccessDenied, cancellationToken);

// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
return Redirect(model.ReturnUrl);
Expand All @@ -119,7 +122,7 @@ public async Task<IActionResult> Login(LoginInputModel model, string button)

if (user != null && await userManager.CheckPasswordAsync(user, model.Password))
{
await events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName));
await events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName), cancellationToken);

// only set explicit expiration here if user chooses "remember me".
// otherwise we rely upon expiration configured in cookie middleware.
Expand All @@ -143,11 +146,11 @@ public async Task<IActionResult> Login(LoginInputModel model, string button)
};

// issue authentication cookie with subject ID and username
await events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName));
await events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName), cancellationToken);
await HttpContext.SignInAsync(isuser, props);

// link external login if cookie exists
await LinkIfExternalLogin(user);
await LinkIfExternalLogin(user, cancellationToken);

// make sure the returnUrl is still valid, and if so redirect back to authorize endpoint or a local page
if (interaction.IsValidReturnUrl(model.ReturnUrl) || Url.IsLocalUrl(model.ReturnUrl))
Expand All @@ -158,13 +161,13 @@ public async Task<IActionResult> Login(LoginInputModel model, string button)
return Redirect("~/");
}

await events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials"));
await events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials"), cancellationToken);

ModelState.AddModelError("", "Invalid username or password");
}

// something went wrong, show form with error
var vm = await accountService.BuildLoginViewModelAsync(model);
var vm = await accountService.BuildLoginViewModelAsync(model, cancellationToken);
vm.LinkSetup = Request.Cookies["Identity.External"] != null;
return View(vm);
}
Expand Down Expand Up @@ -247,7 +250,8 @@ public async Task<IActionResult> ExternalLoginCallback()
};

// issue local authentication cookie for user
await events.RaiseAsync(new UserLoginSuccessEvent(provider, userId, user.Id, user.UserName));
CancellationToken cancellationToken = HttpContext.RequestAborted;
await events.RaiseAsync(new UserLoginSuccessEvent(provider, userId, user.Id, user.UserName), cancellationToken);
await HttpContext.SignInAsync(isuser, props);

// delete temporary cookie used during external authentication
Expand All @@ -262,8 +266,10 @@ public async Task<IActionResult> ExternalLoginCallback()
[HttpGet]
public async Task<IActionResult> Logout(string logoutId)
{
CancellationToken cancellationToken = HttpContext.RequestAborted;

// build a model so the logout page knows what to display
var vm = await accountService.BuildLogoutViewModelAsync(logoutId);
var vm = await accountService.BuildLogoutViewModelAsync(logoutId, cancellationToken);

if (vm.ShowLogoutPrompt == false)
{
Expand All @@ -282,8 +288,10 @@ public async Task<IActionResult> Logout(string logoutId)
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout(LogoutInputModel model)
{
CancellationToken cancellationToken = HttpContext.RequestAborted;

// build a model so the logged out page knows what to display
var vm = await accountService.BuildLoggedOutViewModelAsync(model.LogoutId);
var vm = await accountService.BuildLoggedOutViewModelAsync(model.LogoutId, cancellationToken);

var user = HttpContext.User;
if (user?.Identity.IsAuthenticated == true)
Expand All @@ -292,7 +300,7 @@ public async Task<IActionResult> Logout(LogoutInputModel model)
await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme);

// raise the logout event
await events.RaiseAsync(new UserLogoutSuccessEvent(user.GetSubjectId(), user.GetDisplayName()));
await events.RaiseAsync(new UserLogoutSuccessEvent(user.GetSubjectId(), user.GetDisplayName()), cancellationToken);
}

// check if we need to trigger sign-out at an upstream identity provider
Expand Down Expand Up @@ -356,7 +364,7 @@ public async Task<IActionResult> Register([FromForm] RegisterInputModel model)
return View(vm);
}

private async Task<IdentityResult> LinkIfExternalLogin(IdentityExpressUser localUser)
private async Task<IdentityResult> LinkIfExternalLogin(IdentityExpressUser localUser, CancellationToken cancellationToken)
{
// get external identity from external scheme cookie
var result = await HttpContext.AuthenticateAsync("Identity.External");
Expand All @@ -373,7 +381,7 @@ private async Task<IdentityResult> LinkIfExternalLogin(IdentityExpressUser local
var userId = userIdClaim.Value;
var providerScheme = result.Properties.Items["scheme"];

var provider = await externalProviderService.GetScheme(providerScheme);
var provider = await externalProviderService.GetScheme(providerScheme, cancellationToken);
var outcome = await userManager.AddLoginAsync(localUser, new UserLoginInfo(provider.AuthenticationScheme, userId, provider.DisplayName));
await HttpContext.SignOutAsync("Identity.External");
return outcome;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.


using System.Threading;
using System.Threading.Tasks;
using Duende.IdentityServer.Extensions;
using Duende.IdentityServer.Services;
Expand Down Expand Up @@ -37,7 +38,8 @@ public ConsentController(
[HttpGet]
public async Task<IActionResult> Index(string returnUrl)
{
var vm = await consent.BuildViewModelAsync(returnUrl);
CancellationToken cancellationToken = HttpContext.RequestAborted;
var vm = await consent.BuildViewModelAsync(returnUrl, cancellationToken);
if (vm != null)
{
return View("Index", vm);
Expand All @@ -53,7 +55,8 @@ public async Task<IActionResult> Index(string returnUrl)
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index(ConsentInputModel model)
{
var result = await consent.ProcessConsent(model, User.GetSubjectId());
CancellationToken cancellationToken = HttpContext.RequestAborted;
var result = await consent.ProcessConsent(model, User.GetSubjectId(), cancellationToken);

if (result.IsRedirect)
{
Expand Down
Loading