Versatile OpenID Connect stack for ASP.NET Core and Microsoft.Owin (compatible with ASP.NET 4.6.1)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3560 lines
158 KiB

/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System.Buffers.Binary;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Moq;
using Xunit;
namespace OpenIddict.Core.Tests;
public class OpenIddictApplicationManagerTests
{
[Fact]
public void Constructor_ThrowsAnExceptionForNullCache()
{
// Arrange
var cache = (IOpenIddictApplicationCache<CustomApplication>) null!;
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(
() => new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store));
Assert.Equal("cache", exception.ParamName);
}
[Fact]
public void Constructor_ThrowsAnExceptionForNullLogger()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = (ILogger<OpenIddictApplicationManager<CustomApplication>>) null!;
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(
() => new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store));
Assert.Equal("logger", exception.ParamName);
}
[Fact]
public void Constructor_ThrowsAnExceptionForNullOptions()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = (IOptionsMonitor<OpenIddictCoreOptions>) null!;
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(
() => new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store));
Assert.Equal("options", exception.ParamName);
}
[Fact]
public void Constructor_ThrowsAnExceptionForNullStore()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = (IOpenIddictApplicationStore<CustomApplication>) null!;
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(
() => new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store));
Assert.Equal("store", exception.ParamName);
}
[Fact]
public async Task CountAsync_CallsStoreMethod()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.CountAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(42);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var count = await manager.CountAsync();
// Assert
Assert.Equal(42, count);
store.Verify(store => store.CountAsync(It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task CountAsync_WithQuery_ThrowsAnExceptionForNullQuery()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.CountAsync<CustomApplication>(query: null!).AsTask());
Assert.Equal("query", exception.ParamName);
}
[Fact]
public async Task CreateAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.CreateAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task CreateAsync_WithSecret_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.CreateAsync(application: null!, secret: "secret").AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task CreateAsync_WithDescriptor_ThrowsAnExceptionForNullDescriptor()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.CreateAsync(descriptor: null!).AsTask());
Assert.Equal("descriptor", exception.ParamName);
}
[Fact]
public async Task DeleteAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.DeleteAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task DeleteAsync_RemovesApplicationFromCache_WhenCachingIsEnabled()
{
// Arrange
var cache = new Mock<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = false });
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
var application = new CustomApplication();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache.Object, logger, options, store.Object);
// Act
await manager.DeleteAsync(application);
// Assert
cache.Verify(cache => cache.RemoveAsync(application, It.IsAny<CancellationToken>()), Times.Once());
store.Verify(store => store.DeleteAsync(application, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task DeleteAsync_DoesNotRemoveFromCache_WhenCachingIsDisabled()
{
// Arrange
var cache = new Mock<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = true });
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
var application = new CustomApplication();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache.Object, logger, options, store.Object);
// Act
await manager.DeleteAsync(application);
// Assert
cache.Verify(cache => cache.RemoveAsync(It.IsAny<CustomApplication>(), It.IsAny<CancellationToken>()), Times.Never());
store.Verify(store => store.DeleteAsync(application, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task FindByClientIdAsync_ThrowsAnExceptionForNullIdentifier()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.FindByClientIdAsync(identifier: null!).AsTask());
Assert.Equal("identifier", exception.ParamName);
}
[Fact]
public async Task FindByClientIdAsync_ThrowsAnExceptionForEmptyIdentifier()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentException>(
() => manager.FindByClientIdAsync(identifier: string.Empty).AsTask());
Assert.Equal("identifier", exception.ParamName);
}
[Fact]
public async Task FindByClientIdAsync_UsesCache_WhenCachingIsEnabled()
{
// Arrange
var application = new CustomApplication();
var cache = new Mock<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = false });
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
cache.Setup(cache => cache.FindByClientIdAsync("client_id", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client_id");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache.Object, logger, options, store.Object);
// Act
var result = await manager.FindByClientIdAsync("client_id");
// Assert
Assert.Same(application, result);
cache.Verify(cache => cache.FindByClientIdAsync("client_id", It.IsAny<CancellationToken>()), Times.Once());
store.Verify(store => store.FindByClientIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
}
[Fact]
public async Task FindByClientIdAsync_UsesStore_WhenCachingIsDisabled()
{
// Arrange
var application = new CustomApplication();
var cache = new Mock<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = true });
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.FindByClientIdAsync("client_id", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client_id");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache.Object, logger, options, store.Object);
// Act
var result = await manager.FindByClientIdAsync("client_id");
// Assert
Assert.Same(application, result);
cache.Verify(cache => cache.FindByClientIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
store.Verify(store => store.FindByClientIdAsync("client_id", It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task FindByIdAsync_ThrowsAnExceptionForNullIdentifier()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.FindByIdAsync(identifier: null!).AsTask());
Assert.Equal("identifier", exception.ParamName);
}
[Fact]
public async Task FindByIdAsync_ThrowsAnExceptionForEmptyIdentifier()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentException>(
() => manager.FindByIdAsync(identifier: string.Empty).AsTask());
Assert.Equal("identifier", exception.ParamName);
}
[Fact]
public async Task FindByIdAsync_UsesCache_WhenCachingIsEnabled()
{
// Arrange
var application = new CustomApplication();
var cache = new Mock<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = false });
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
cache.Setup(cache => cache.FindByIdAsync("id", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
store.Setup(store => store.GetIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("id");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache.Object, logger, options, store.Object);
// Act
var result = await manager.FindByIdAsync("id");
// Assert
Assert.Same(application, result);
cache.Verify(cache => cache.FindByIdAsync("id", It.IsAny<CancellationToken>()), Times.Once());
store.Verify(store => store.FindByIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
}
[Fact]
public async Task FindByIdAsync_UsesStore_WhenCachingIsDisabled()
{
// Arrange
var application = new CustomApplication();
var cache = new Mock<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = true });
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.FindByIdAsync("id", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
store.Setup(store => store.GetIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("id");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache.Object, logger, options, store.Object);
// Act
var result = await manager.FindByIdAsync("id");
// Assert
Assert.Same(application, result);
cache.Verify(cache => cache.FindByIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
store.Verify(store => store.FindByIdAsync("id", It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public void FindByPostLogoutRedirectUriAsync_ThrowsAnExceptionForNullUri()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() =>
manager.FindByPostLogoutRedirectUriAsync(uri: null!));
Assert.Equal("uri", exception.ParamName);
}
[Fact]
public void FindByPostLogoutRedirectUriAsync_ThrowsAnExceptionForEmptyUri()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = Assert.Throws<ArgumentException>(() =>
manager.FindByPostLogoutRedirectUriAsync(uri: string.Empty));
Assert.Equal("uri", exception.ParamName);
}
[Fact]
public async Task FindByPostLogoutRedirectUriAsync_UsesCache_WhenCachingIsEnabled()
{
// Arrange
var applications = new[] { new CustomApplication() };
var cache = new Mock<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = false });
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
cache.Setup(cache => cache.FindByPostLogoutRedirectUriAsync("https://www.fabrikam.com/callback/logout", It.IsAny<CancellationToken>()))
.Returns(applications.ToAsyncEnumerable());
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(applications[0], It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://www.fabrikam.com/callback/logout"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache.Object, logger, options, store.Object);
// Act
var result = await manager.FindByPostLogoutRedirectUriAsync("https://www.fabrikam.com/callback/logout").ToListAsync();
// Assert
Assert.Same(applications[0], result[0]);
cache.Verify(cache => cache.FindByPostLogoutRedirectUriAsync("https://www.fabrikam.com/callback/logout", It.IsAny<CancellationToken>()), Times.Once());
store.Verify(store => store.FindByPostLogoutRedirectUriAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
}
[Fact]
public async Task FindByPostLogoutRedirectUriAsync_UsesStore_WhenCachingIsDisabled()
{
// Arrange
var applications = new[] { new CustomApplication() };
var cache = new Mock<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = true });
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.FindByPostLogoutRedirectUriAsync("https://www.fabrikam.com/callback/logout", It.IsAny<CancellationToken>()))
.Returns(applications.ToAsyncEnumerable());
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(applications[0], It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://www.fabrikam.com/callback/logout"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache.Object, logger, options, store.Object);
// Act
var result = await manager.FindByPostLogoutRedirectUriAsync("https://www.fabrikam.com/callback/logout").ToListAsync();
// Assert
Assert.Same(applications[0], result[0]);
cache.Verify(cache => cache.FindByPostLogoutRedirectUriAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
store.Verify(store => store.FindByPostLogoutRedirectUriAsync("https://www.fabrikam.com/callback/logout", It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public void FindByRedirectUriAsync_ThrowsAnExceptionForNullUri()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() =>
manager.FindByRedirectUriAsync(uri: null!));
Assert.Equal("uri", exception.ParamName);
}
[Fact]
public void FindByRedirectUriAsync_ThrowsAnExceptionForEmptyUri()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = Assert.Throws<ArgumentException>(() =>
manager.FindByRedirectUriAsync(uri: string.Empty));
Assert.Equal("uri", exception.ParamName);
}
[Fact]
public async Task FindByRedirectUriAsync_UsesCache_WhenCachingIsEnabled()
{
// Arrange
var applications = new[] { new CustomApplication() };
var cache = new Mock<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = false });
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
cache.Setup(cache => cache.FindByRedirectUriAsync("https://www.fabrikam.com/callback", It.IsAny<CancellationToken>()))
.Returns(applications.ToAsyncEnumerable());
store.Setup(store => store.GetRedirectUrisAsync(applications[0], It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://www.fabrikam.com/callback"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache.Object, logger, options, store.Object);
// Act
var result = await manager.FindByRedirectUriAsync("https://www.fabrikam.com/callback").ToListAsync();
// Assert
Assert.Same(applications[0], result[0]);
cache.Verify(cache => cache.FindByRedirectUriAsync("https://www.fabrikam.com/callback", It.IsAny<CancellationToken>()), Times.Once());
store.Verify(store => store.FindByRedirectUriAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
}
[Fact]
public async Task FindByRedirectUriAsync_UsesStore_WhenCachingIsDisabled()
{
// Arrange
var applications = new[] { new CustomApplication() };
var cache = new Mock<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions { DisableEntityCaching = true });
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.FindByRedirectUriAsync("https://www.fabrikam.com/callback", It.IsAny<CancellationToken>()))
.Returns(applications.ToAsyncEnumerable());
store.Setup(store => store.GetRedirectUrisAsync(applications[0], It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://www.fabrikam.com/callback"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache.Object, logger, options, store.Object);
// Act
var result = await manager.FindByRedirectUriAsync("https://www.fabrikam.com/callback").ToListAsync();
// Assert
Assert.Same(applications[0], result[0]);
cache.Verify(cache => cache.FindByRedirectUriAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
store.Verify(store => store.FindByRedirectUriAsync("https://www.fabrikam.com/callback", It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task GetApplicationTypeAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetApplicationTypeAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetAsync_WithQuery_ThrowsAnExceptionForNullQuery()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetAsync<CustomApplication>(query: null!).AsTask());
Assert.Equal("query", exception.ParamName);
}
[Fact]
public async Task GetAsync_WithQueryAndState_ThrowsAnExceptionForNullQuery()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetAsync<object, CustomApplication>(query: null!, state: null!).AsTask());
Assert.Equal("query", exception.ParamName);
}
[Fact]
public async Task GetClientIdAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetClientIdAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetClientIdAsync_ReturnsClientIdFromStore()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("my-client-id");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var clientId = await manager.GetClientIdAsync(application);
// Assert
Assert.Equal("my-client-id", clientId);
store.Verify(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task GetClientTypeAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetClientTypeAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetClientTypeAsync_ReturnsClientTypeFromStore()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("public");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var clientType = await manager.GetClientTypeAsync(application);
// Assert
Assert.Equal("public", clientType);
store.Verify(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task GetConsentTypeAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetConsentTypeAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetDisplayNameAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetDisplayNameAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetDisplayNameAsync_ReturnsDisplayNameFromStore()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetDisplayNameAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("My Application");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var displayName = await manager.GetDisplayNameAsync(application);
// Assert
Assert.Equal("My Application", displayName);
store.Verify(store => store.GetDisplayNameAsync(application, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task GetDisplayNamesAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetDisplayNamesAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetIdAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetIdAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetIdAsync_ReturnsIdentifierFromStore()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("unique-id");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var id = await manager.GetIdAsync(application);
// Assert
Assert.Equal("unique-id", id);
store.Verify(store => store.GetIdAsync(application, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task GetJsonWebKeySetAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetJsonWebKeySetAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetLocalizedDisplayNameAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetLocalizedDisplayNameAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetLocalizedDisplayNameAsync_WithCulture_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetLocalizedDisplayNameAsync(application: null!, CultureInfo.CurrentCulture).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetPermissionsAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetPermissionsAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetPostLogoutRedirectUrisAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetPostLogoutRedirectUrisAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetPropertiesAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetPropertiesAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetPublicKeyInfrastructureTlsClientAuthenticationPolicyAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
var policy = new X509ChainPolicy();
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetPublicKeyInfrastructureTlsClientAuthenticationPolicyAsync(application: null!, policy).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetRedirectUrisAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetRedirectUrisAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetRequirementsAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetRequirementsAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetSelfSignedTlsClientAuthenticationPolicyAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
var policy = new X509ChainPolicy();
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetSelfSignedTlsClientAuthenticationPolicyAsync(application: null!, policy).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task GetSettingsAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.GetSettingsAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task HasApplicationTypeAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.HasApplicationTypeAsync(application: null!, "web").AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task HasApplicationTypeAsync_ThrowsAnExceptionForNullType()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.HasApplicationTypeAsync(application, type: null!).AsTask());
Assert.Equal("type", exception.ParamName);
}
[Fact]
public async Task HasApplicationTypeAsync_ReturnsTrueWhenTypeMatches()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("web");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.HasApplicationTypeAsync(application, "web");
// Assert
Assert.True(result);
}
[Fact]
public async Task HasClientTypeAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.HasClientTypeAsync(application: null!, "public").AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task HasClientTypeAsync_ThrowsAnExceptionForNullType()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.HasClientTypeAsync(application, type: null!).AsTask());
Assert.Equal("type", exception.ParamName);
}
[Theory]
[InlineData("public", "public", true)]
[InlineData("confidential", "public", false)]
public async Task HasClientTypeAsync_ReturnsExpectedResult(string storedType, string testedType, bool expected)
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(storedType);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.HasClientTypeAsync(application, testedType);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public async Task HasConsentTypeAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.HasConsentTypeAsync(application: null!, "explicit").AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task HasConsentTypeAsync_ThrowsAnExceptionForNullType()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.HasConsentTypeAsync(application, type: null!).AsTask());
Assert.Equal("type", exception.ParamName);
}
[Fact]
public async Task HasConsentTypeAsync_ReturnsTrueWhenTypeMatches()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetConsentTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("explicit");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.HasConsentTypeAsync(application, "explicit");
// Assert
Assert.True(result);
}
[Fact]
public async Task HasPermissionAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.HasPermissionAsync(application: null!, "ept:token").AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task HasPermissionAsync_ThrowsAnExceptionForNullPermission()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.HasPermissionAsync(application, permission: null!).AsTask());
Assert.Equal("permission", exception.ParamName);
}
[Fact]
public async Task HasRequirementAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.HasRequirementAsync(application: null!, "requirement").AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task HasRequirementAsync_ThrowsAnExceptionForNullRequirement()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.HasRequirementAsync(application, requirement: null!).AsTask());
Assert.Equal("requirement", exception.ParamName);
}
[Fact]
public async Task HasRequirementAsync_ReturnsTrueWhenRequirementExists()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetRequirementsAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["requirement1", "requirement2"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.HasRequirementAsync(application, "requirement1");
// Assert
Assert.True(result);
}
[Fact]
public async Task ListAsync_ReturnsAllApplications()
{
// Arrange
var applications = new[] { new CustomApplication(), new CustomApplication() };
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.ListAsync(It.IsAny<int?>(), It.IsAny<int?>(), It.IsAny<CancellationToken>()))
.Returns(applications.ToAsyncEnumerable());
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = new List<CustomApplication>();
await foreach (var app in manager.ListAsync())
{
results.Add(app);
}
// Assert
Assert.Equal(2, results.Count);
store.Verify(store => store.ListAsync(It.IsAny<int?>(), It.IsAny<int?>(), It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task ObfuscateClientSecretAsync_ThrowsAnExceptionForNullSecret()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ObfuscateClientSecretAsync(secret: null!).AsTask());
Assert.Equal("secret", exception.ParamName);
}
[Fact]
public async Task ObfuscateClientSecretAsync_ThrowsAnExceptionForEmptySecret()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentException>(
() => manager.ObfuscateClientSecretAsync(secret: string.Empty).AsTask());
Assert.Equal("secret", exception.ParamName);
}
[Fact]
public async Task ObfuscateClientSecretAsync_ReturnsBase64EncodedHash()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var hash = await manager.ObfuscateClientSecretAsync("my-secret");
var payload = Convert.FromBase64String(hash);
Assert.NotEmpty(payload);
}
[Fact]
public async Task ObfuscateClientSecretAsync_WritesFormatMarker()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var hash = await manager.ObfuscateClientSecretAsync("my-secret");
var payload = Convert.FromBase64String(hash);
Assert.Equal(0x01, payload[0]);
}
[Fact]
public async Task ObfuscateClientSecretAsync_UsesSha512ByDefault()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var hash = await manager.ObfuscateClientSecretAsync("my-secret");
var payload = Convert.FromBase64String(hash);
Assert.Equal(2u, BinaryPrimitives.ReadUInt32BigEndian(payload.AsSpan(1, 4)));
}
[Fact]
public async Task ObfuscateClientSecretAsync_UsesSha256WhenConfigured()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions
{
ClientSecretKeyDerivationHashAlgorithm = HashAlgorithmName.SHA256
});
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var hash = await manager.ObfuscateClientSecretAsync("my-secret");
var payload = Convert.FromBase64String(hash);
Assert.Equal(1u, BinaryPrimitives.ReadUInt32BigEndian(payload.AsSpan(1, 4)));
}
[Fact]
public async Task ObfuscateClientSecretAsync_UsesSha1WhenConfigured()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions
{
ClientSecretKeyDerivationHashAlgorithm = HashAlgorithmName.SHA1
});
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var hash = await manager.ObfuscateClientSecretAsync("my-secret");
var payload = Convert.FromBase64String(hash);
Assert.Equal(0u, BinaryPrimitives.ReadUInt32BigEndian(payload.AsSpan(1, 4)));
}
[Fact]
public async Task ObfuscateClientSecretAsync_ThrowsForUnsupportedAlgorithm()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions
{
ClientSecretKeyDerivationHashAlgorithm = HashAlgorithmName.SHA384
});
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act and assert
await Assert.ThrowsAsync<InvalidOperationException>(
() => manager.ObfuscateClientSecretAsync("my-secret").AsTask());
}
[Fact]
public async Task ObfuscateClientSecretAsync_WritesConfiguredIterationCount()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions
{
ClientSecretKeyDerivationIterations = 50_000
});
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var hash = await manager.ObfuscateClientSecretAsync("my-secret");
// Assert
var payload = Convert.FromBase64String(hash);
Assert.Equal(50_000u, BinaryPrimitives.ReadUInt32BigEndian(payload.AsSpan(5, 4)));
}
[Fact]
public async Task ObfuscateClientSecretAsync_WritesSaltOfConfiguredLength()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions
{
ClientSecretKeyDerivationSaltLength = 128 // bits → 16 bytes
});
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var hash = await manager.ObfuscateClientSecretAsync("my-secret");
var payload = Convert.FromBase64String(hash);
Assert.Equal(16u, BinaryPrimitives.ReadUInt32BigEndian(payload.AsSpan(9, 4)));
}
[Fact]
public async Task ObfuscateClientSecretAsync_ProducesDifferentHashesForSameSecret()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
var hash1 = await manager.ObfuscateClientSecretAsync("my-secret");
var hash2 = await manager.ObfuscateClientSecretAsync("my-secret");
Assert.NotEqual(hash1, hash2);
}
[Fact]
public async Task ObfuscateClientSecretAsync_ProducesHashValidatableByValidateClientSecretAsync()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var hash = await manager.ObfuscateClientSecretAsync("my-secret");
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("my-secret", hash);
// Assert
Assert.True(isValid);
Assert.False(isRehashRequired);
}
[Fact]
public async Task PopulateAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
var descriptor = new OpenIddictApplicationDescriptor();
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.PopulateAsync(application: null!, descriptor).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task PopulateAsync_ThrowsAnExceptionForNullDescriptor()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.PopulateAsync(application, descriptor: null!).AsTask());
Assert.Equal("descriptor", exception.ParamName);
}
[Fact]
public async Task UpdateAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.UpdateAsync(application: null!).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task UpdateAsync_WithDescriptor_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
var descriptor = new OpenIddictApplicationDescriptor();
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.UpdateAsync(application: null!, descriptor).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task UpdateAsync_WithSecret_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.UpdateAsync(application: null!, secret: "secret").AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public void ValidateAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(
() => manager.ValidateAsync(application: null!));
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task ValidateAsync_ReturnsErrorWhenClientIdIsEmpty()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(string.Empty);
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(string.Empty);
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2036));
}
[Fact]
public async Task ValidateAsync_ReturnsErrorWhenClientIdIsAlreadyUsed()
{
// Arrange
var application = new CustomApplication();
var other = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.FindByClientIdAsync("client-id", It.IsAny<CancellationToken>()))
.ReturnsAsync(other);
store.Setup(store => store.GetIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("application-id");
store.Setup(store => store.GetIdAsync(other, It.IsAny<CancellationToken>()))
.ReturnsAsync("other-application-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(string.Empty);
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2111));
}
[Fact]
public async Task ValidateAsync_ReturnsErrorWhenClientTypeIsEmpty()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(string.Empty);
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2050));
}
[Fact]
public async Task ValidateAsync_ReturnsErrorWhenClientTypeIsNotSupported()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("unsupported-type");
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2112));
}
[Fact]
public async Task ValidateAsync_ReturnsErrorWhenPublicApplicationHasClientSecret()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("some-secret");
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2114));
}
[Fact]
public async Task ValidateAsync_ReturnsErrorWhenConfidentialApplicationHasNoSecretAndNoSigningKey()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(string.Empty);
store.Setup(store => store.GetJsonWebKeySetAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync((JsonWebKeySet?) null);
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2113));
}
[Fact]
public async Task ValidateAsync_ReturnsErrorWhenConfidentialApplicationHasJwksWithoutSigningKey()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(string.Empty);
var jwks = new JsonWebKeySet();
jwks.Keys.Add(new JsonWebKey { Kty = JsonWebAlgorithmsKeyTypes.Octet, Use = JsonWebKeyUseNames.Sig });
store.Setup(store => store.GetJsonWebKeySetAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(jwks);
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2113));
}
[Fact]
public async Task ValidateAsync_ReturnsErrorWhenRedirectUriIsEmpty()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("hashed-secret");
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([string.Empty]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2061));
}
[Fact]
public async Task ValidateAsync_ReturnsErrorWhenRedirectUriIsNotAbsolute()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("hashed-secret");
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["not-an-absolute-uri"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2062));
}
[Fact]
public async Task ValidateAsync_ReturnsErrorWhenRedirectUriContainsFragment()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("hashed-secret");
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://client.contoso.com/callback#fragment"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2115));
}
[Fact]
public async Task ValidateAsync_ReturnsErrorWhenRedirectUriContainsIssParameter()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("hashed-secret");
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://client.contoso.com/callback?iss=https%3A%2F%2Fissuer.contoso.com"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.Contains(results, result => result.ErrorMessage == SR.FormatID2134(Parameters.Iss));
}
[Fact]
public async Task ValidateAsync_ReturnsErrorWhenPostLogoutRedirectUriContainsFragment()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("hashed-secret");
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://client.contoso.com/logout#fragment"]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.Contains(results, result => result.ErrorMessage == SR.GetResourceString(SR.ID2115));
}
[Fact]
public async Task ValidateAsync_ReturnsNoErrorsForValidConfidentialApplicationWithSecret()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("hashed-secret");
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://client.contoso.com/callback"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.DoesNotContain(results, static result => result != ValidationResult.Success);
}
[Fact]
public async Task ValidateAsync_ReturnsNoErrorsForValidConfidentialApplicationWithRsaSigningKey()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(string.Empty);
var jwks = new JsonWebKeySet();
jwks.Keys.Add(new JsonWebKey { Kty = JsonWebAlgorithmsKeyTypes.RSA, Use = JsonWebKeyUseNames.Sig });
store.Setup(store => store.GetJsonWebKeySetAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(jwks);
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.DoesNotContain(results, static result => result != ValidationResult.Success);
}
[Fact]
public async Task ValidateAsync_ReturnsNoErrorsForValidPublicApplication()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(string.Empty);
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://client.contoso.com/callback"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var results = await manager.ValidateAsync(application).ToListAsync();
// Assert
Assert.DoesNotContain(results, static result => result != ValidationResult.Success);
}
[Fact]
public async Task ValidateClientSecretAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ValidateClientSecretAsync(application: null!, "secret").AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task ValidateClientSecretAsync_ThrowsAnExceptionForNullSecret()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ValidateClientSecretAsync(application, secret: null!).AsTask());
Assert.Equal("secret", exception.ParamName);
}
[Fact]
public async Task ValidateClientSecretAsync_ThrowsAnExceptionForEmptySecret()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentException>(
() => manager.ValidateClientSecretAsync(application, secret: string.Empty).AsTask());
Assert.Equal("secret", exception.ParamName);
}
[Fact]
public async Task ValidateClientSecretAsync_ReturnsFalseForPublicClients()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Public);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidateClientSecretAsync(application, "any-secret");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidateClientSecretAsync_ReturnsFalseWhenStoredSecretIsNull()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync((string?) null);
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidateClientSecretAsync(application, "any-secret");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidateClientSecretAsync_ReturnsFalseWhenStoredSecretIsEmpty()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(string.Empty);
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidateClientSecretAsync(application, "any-secret");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidateClientSecretAsync_ReturnsFalseWhenSecretDoesNotMatch()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
var hashedSecret = "AQAAAAIAAYagAAAAEOlzsJpxnNJRh9HnCQNEzZMPLqTYYqH5WJ5+DJMwL+BhiLUGjL2SQnfRU8bQv2K2Sg==";
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(hashedSecret);
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidateClientSecretAsync(application, "wrong-secret");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidateClientSecretAsync_PerformsRehashWhenRequired()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
var storedHash = "AQAAAAEAACcQAAAAIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fMMgkjB4tHCgxxjPemQnSkFqO0dtrtE3xiqISZNQJfWNqm+Flbr/sNlQCAWRy0XrIxw6HU3B+YB5aHDlJhCTw2w==";
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions
{
DisableAutomaticClientSecretRehashing = false
});
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(storedHash);
store.Setup(store => store.FindByClientIdAsync("client-id", It.IsAny<CancellationToken>()))
.ReturnsAsync((CustomApplication?) null);
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.UpdateAsync(application, It.IsAny<CancellationToken>()))
.Returns(ValueTask.CompletedTask);
store.Setup(store => store.SetClientSecretAsync(application, It.IsAny<string?>(), It.IsAny<CancellationToken>()))
.Returns(ValueTask.CompletedTask);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
await manager.ValidateClientSecretAsync(application, "test-secret");
// Assert
store.Verify(store => store.UpdateAsync(application, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task ValidateClientSecretAsync_SkipsRehashWhenDisabled()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions
{
DisableAutomaticClientSecretRehashing = true
});
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
var oldHash = "AQAAAAEACicQAAAAEJPkfY9tVqKqHQlLPqNT0ksqN8C3LqY4RqBYQaJHhWPQXuYhWFpN9pLkR8vZxQ==";
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(oldHash);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidateClientSecretAsync(application, "test-secret");
// Assert
store.Verify(store => store.UpdateAsync(application, It.IsAny<CancellationToken>()), Times.Never());
}
[Fact]
public async Task ValidateClientSecretAsync_ContinuesOnRehashFailure()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions
{
DisableAutomaticClientSecretRehashing = false
});
store.Setup(store => store.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientTypes.Confidential);
store.Setup(store => store.GetClientIdAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("client-id");
store.Setup(store => store.GetClientSecretAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync("AQAAAAEAACcQAAAAIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fMMgkjB4tHCgxxjPemQnSkFqO0dtrtE3xiqISZNQJfWNqm+Flbr/sNlQCAWRy0XrIxw6HU3B+YB5aHDlJhCTw2w==");
store.Setup(store => store.FindByClientIdAsync("client-id", It.IsAny<CancellationToken>()))
.ReturnsAsync((CustomApplication?) null);
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync([]);
store.Setup(store => store.UpdateAsync(application, It.IsAny<CancellationToken>()))
.Returns(new ValueTask(Task.FromException(new InvalidOperationException("Concurrency conflict"))));
store.Setup(store => store.SetClientSecretAsync(application, It.IsAny<string?>(), It.IsAny<CancellationToken>()))
.Returns(ValueTask.CompletedTask);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidateClientSecretAsync(application, "test-secret");
// Assert
Assert.True(result);
store.Verify(store => store.UpdateAsync(application, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ThrowsAnExceptionForNullSecret()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ValidateClientSecretAsync(secret: null!, "comparand").AsTask());
Assert.Equal("secret", exception.ParamName);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ThrowsAnExceptionForEmptySecret()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentException>(
() => manager.ValidateClientSecretAsync(secret: string.Empty, "comparand").AsTask());
Assert.Equal("secret", exception.ParamName);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ThrowsAnExceptionForNullComparand()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ValidateClientSecretAsync("secret", comparand: null!).AsTask());
Assert.Equal("comparand", exception.ParamName);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ThrowsAnExceptionForEmptyComparand()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentException>(
() => manager.ValidateClientSecretAsync("secret", comparand: string.Empty).AsTask());
Assert.Equal("comparand", exception.ParamName);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsFalseForInvalidBase64Comparand()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("secret", "not-valid-base64!!!");
// Assert
Assert.False(isValid);
Assert.False(isRehashRequired);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsFalseForEmptyPayload()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("secret", Convert.ToBase64String([0x01]));
// Assert
Assert.False(isValid);
Assert.False(isRehashRequired);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsFalseForInvalidFormatMarker()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
var payload = new byte[20];
payload[0] = 0x02;
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("secret", Convert.ToBase64String(payload));
// Assert
Assert.False(isValid);
Assert.False(isRehashRequired);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsFalseForUnknownAlgorithmVersion()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
var payload = new byte[50];
payload[0] = 0x01;
BinaryPrimitives.WriteUInt32BigEndian(payload.AsSpan(1, 4), 99);
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("secret", Convert.ToBase64String(payload));
// Assert
Assert.False(isValid);
Assert.False(isRehashRequired);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsFalseForTooFewIterations()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
var payload = new byte[50];
payload[0] = 0x01;
BinaryPrimitives.WriteUInt32BigEndian(payload.AsSpan(1, 4), 1); // SHA256
BinaryPrimitives.WriteUInt32BigEndian(payload.AsSpan(5, 4), 9_999);
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("secret", Convert.ToBase64String(payload));
// Assert
Assert.False(isValid);
Assert.False(isRehashRequired);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsFalseForTooManyIterations()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
var payload = new byte[50];
payload[0] = 0x01;
BinaryPrimitives.WriteUInt32BigEndian(payload.AsSpan(1, 4), 1); // SHA256
BinaryPrimitives.WriteUInt32BigEndian(payload.AsSpan(5, 4), 10_000_001);
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("secret", Convert.ToBase64String(payload));
// Assert
Assert.False(isValid);
Assert.False(isRehashRequired);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsFalseForTooShortSalt()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
var payload = new byte[50];
payload[0] = 0x01;
BinaryPrimitives.WriteUInt32BigEndian(payload.AsSpan(1, 4), 1); // SHA256
BinaryPrimitives.WriteUInt32BigEndian(payload.AsSpan(5, 4), 100_000);
BinaryPrimitives.WriteUInt32BigEndian(payload.AsSpan(9, 4), 15);
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("secret", Convert.ToBase64String(payload));
// Assert
Assert.False(isValid);
Assert.False(isRehashRequired);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsFalseWhenSecretDoesNotMatch()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("wrong-secret",
"AQAAAAIAAYagAAAAIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fa7sVq4Aib4PoDsWFuIX7V4NFLHw5zf2TM+3M6JSc3S2ae5j2Njof7cWUUqaG4esxvbG6aKTTQMogVSejRPMbhQ==");
// Assert
Assert.False(isValid);
Assert.False(isRehashRequired);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsTrueWithoutRehashWhenOptionsMatch()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("my-secret",
"AQAAAAIAAYagAAAAIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fhmvJW1GK8OLHa33ut1TfRoG5BojsxFN8Oq8lnF9CTGu/Ti7ItixBhZaDxxb5lejILz/Ob+33P0h2Zax9+FTeWg==");
// Assert
Assert.True(isValid);
Assert.False(isRehashRequired);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsTrueWithRehashWhenAlgorithmChanged()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("my-secret",
"AQAAAAEAAYagAAAAIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fcc2TaLOEfZnHMySJlkQmmM1nnq4c6jFhE65SjmqPATGuvpSybeRzlk8584bUg3qjJbxxJ1oALKzppT0V6sPYQQ==");
// Assert
Assert.True(isValid);
Assert.True(isRehashRequired);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsTrueWithRehashWhenIterationsChanged()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("my-secret",
"AQAAAAIAAMNQAAAAIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fhUaA0RyMXFhPzsknq1++eBXKuNGp6nPquCMb4NVu9st1Qj6cDZXnnHmS2FXgc/2AUe8+QJ35JGY3SW3gJjzQUA==");
// Assert
Assert.True(isValid);
Assert.True(isRehashRequired);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsTrueWithRehashWhenSaltLengthChanged()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("my-secret",
"AQAAAAIAAYagAAAAEAABAgMEBQYHCAkKCwwNDg9/NCTaaTU2IRS086JD6lOexCek5paQ+YtfkDinqaRBpu6sdgeztT00H36B5hWW787rjVDMg9IpYdZ7iPiQZZZu");
// Assert
Assert.True(isValid);
Assert.True(isRehashRequired);
}
[Fact]
public async Task ValidateClientSecretAsync_ProtectedMethod_ReturnsTrueWithRehashWhenOutputLengthChanged()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>(
mock => mock.CurrentValue == new OpenIddictCoreOptions());
var manager = new CustomApplicationManagerWithProtectedAccess(cache, logger, options, store);
// Act
var (isValid, isRehashRequired) = await manager.ValidateClientSecretAsync("my-secret",
"AQAAAAIAAYagAAAAIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fhmvJW1GK8OLHa33ut1TfRg==");
// Assert
Assert.True(isValid);
Assert.True(isRehashRequired);
}
[Fact]
public async Task ValidatePostLogoutRedirectUriAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ValidatePostLogoutRedirectUriAsync(application: null!, "https://localhost").AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task ValidatePostLogoutRedirectUriAsync_ThrowsAnExceptionForNullUri()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ValidatePostLogoutRedirectUriAsync(application, uri: null!).AsTask());
Assert.Equal("uri", exception.ParamName);
}
[Fact]
public async Task ValidatePostLogoutRedirectUriAsync_ThrowsAnExceptionForEmptyUri()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentException>(
() => manager.ValidatePostLogoutRedirectUriAsync(application, uri: string.Empty).AsTask());
Assert.Equal("uri", exception.ParamName);
}
[Fact]
public async Task ValidatePostLogoutRedirectUriAsync_ReturnsFalseWhenUriNotRegistered()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://contoso.com/logout"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidatePostLogoutRedirectUriAsync(application, "https://other.com/logout");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidatePostLogoutRedirectUriAsync_ReturnsTrueWhenUriMatchesExactly()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://contoso.com/logout"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidatePostLogoutRedirectUriAsync(application, "https://contoso.com/logout");
// Assert
Assert.True(result);
}
[Fact]
public async Task ValidatePostLogoutRedirectUriAsync_ReturnsFalseWhenUriDiffersInCase()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://contoso.com/Logout"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Web);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidatePostLogoutRedirectUriAsync(application, "https://contoso.com/logout");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidatePostLogoutRedirectUriAsync_ReturnsTrueForNativeApplicationWhenLoopbackUriMatchesWithDifferentPort()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["http://127.0.0.1/logout"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Native);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
var result = await manager.ValidatePostLogoutRedirectUriAsync(application, "http://127.0.0.1:7890/logout");
// Assert
Assert.True(result);
}
[Fact]
public async Task ValidatePostLogoutRedirectUriAsync_ReturnsFalseForNativeApplicationWhenClientUriUsesDefaultPort()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["http://127.0.0.1/logout"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Native);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
var result = await manager.ValidatePostLogoutRedirectUriAsync(application, "http://127.0.0.1:80/logout");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidatePostLogoutRedirectUriAsync_ReturnsFalseForNativeApplicationWhenUriIsNotLoopback()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://contoso.com/logout"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Native);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
var result = await manager.ValidatePostLogoutRedirectUriAsync(application, "https://contoso.com:7890/logout");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidatePostLogoutRedirectUriAsync_ReturnsFalseForNativeApplicationWhenSchemesDiffer()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["http://127.0.0.1/logout"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Native);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
var result = await manager.ValidatePostLogoutRedirectUriAsync(application, "https://127.0.0.1:7890/logout");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidatePostLogoutRedirectUriAsync_ReturnsFalseForNativeApplicationWhenPathsDiffer()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["http://127.0.0.1/logout"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Native);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
var result = await manager.ValidatePostLogoutRedirectUriAsync(application, "http://127.0.0.1:7890/other");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidatePostLogoutRedirectUriAsync_ReturnsFalseForNonNativeApplicationWhenPortDiffers()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetPostLogoutRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["http://127.0.0.1/logout"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Web);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidatePostLogoutRedirectUriAsync(application, "http://127.0.0.1:7890/logout");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidatePublicKeyInfrastructureTlsClientCertificateAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
var certificate = (X509Certificate2)null!;
var policy = new X509ChainPolicy();
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ValidatePublicKeyInfrastructureTlsClientCertificateAsync(application: null!, certificate, policy).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task ValidatePublicKeyInfrastructureTlsClientCertificateAsync_ThrowsAnExceptionForNullCertificate()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
var policy = new X509ChainPolicy();
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ValidatePublicKeyInfrastructureTlsClientCertificateAsync(application, certificate: null!, policy).AsTask());
Assert.Equal("certificate", exception.ParamName);
}
[Fact]
public async Task ValidateRedirectUriAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ValidateRedirectUriAsync(application: null!, "https://localhost").AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task ValidateRedirectUriAsync_ThrowsAnExceptionForNullUri()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ValidateRedirectUriAsync(application, uri: null!).AsTask());
Assert.Equal("uri", exception.ParamName);
}
[Fact]
public async Task ValidateRedirectUriAsync_ThrowsAnExceptionForEmptyUri()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentException>(
() => manager.ValidateRedirectUriAsync(application, uri: string.Empty).AsTask());
Assert.Equal("uri", exception.ParamName);
}
[Fact]
public async Task ValidateRedirectUriAsync_ReturnsFalseWhenUriNotRegistered()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://contoso.com/callback"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidateRedirectUriAsync(application, "https://other.com/callback");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidateRedirectUriAsync_ReturnsTrueWhenUriMatchesExactly()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://contoso.com/callback"]);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidateRedirectUriAsync(application, "https://contoso.com/callback");
// Assert
Assert.True(result);
}
[Fact]
public async Task ValidateRedirectUriAsync_ReturnsFalseWhenUriDiffersInCase()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://contoso.com/Callback"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Web);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidateRedirectUriAsync(application, "https://contoso.com/callback");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidateRedirectUriAsync_ReturnsTrueForNativeApplicationWhenLoopbackUriMatchesWithDifferentPort()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["http://127.0.0.1/callback"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Native);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
var result = await manager.ValidateRedirectUriAsync(application, "http://127.0.0.1:7890/callback");
// Assert
Assert.True(result);
}
[Fact]
public async Task ValidateRedirectUriAsync_ReturnsFalseForNativeApplicationWhenClientUriUsesDefaultPort()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["http://127.0.0.1/callback"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Native);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
var result = await manager.ValidateRedirectUriAsync(application, "http://127.0.0.1:80/callback");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidateRedirectUriAsync_ReturnsFalseForNativeApplicationWhenUriIsNotLoopback()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["https://contoso.com/callback"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Native);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
var result = await manager.ValidateRedirectUriAsync(application, "https://contoso.com:7890/callback");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidateRedirectUriAsync_ReturnsFalseForNativeApplicationWhenSchemesDiffer()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["http://127.0.0.1/callback"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Native);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
var result = await manager.ValidateRedirectUriAsync(application, "https://127.0.0.1:7890/callback");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidateRedirectUriAsync_ReturnsFalseForNativeApplicationWhenPathsDiffer()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["http://127.0.0.1/callback"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Native);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
var result = await manager.ValidateRedirectUriAsync(application, "http://127.0.0.1:7890/other");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidateRedirectUriAsync_ReturnsFalseForNonNativeApplicationWhenPortDiffers()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = new Mock<IOpenIddictApplicationStore<CustomApplication>>();
store.Setup(store => store.GetRedirectUrisAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(["http://127.0.0.1/callback"]);
store.Setup(store => store.GetApplicationTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(ApplicationTypes.Web);
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store.Object);
// Act
var result = await manager.ValidateRedirectUriAsync(application, "http://127.0.0.1:7890/callback");
// Assert
Assert.False(result);
}
[Fact]
public async Task ValidateSelfSignedTlsClientCertificateAsync_ThrowsAnExceptionForNullApplication()
{
// Arrange
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
var certificate = (X509Certificate2)null!;
var policy = new X509ChainPolicy();
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ValidateSelfSignedTlsClientCertificateAsync(application: null!, certificate, policy).AsTask());
Assert.Equal("application", exception.ParamName);
}
[Fact]
public async Task ValidateSelfSignedTlsClientCertificateAsync_ThrowsAnExceptionForNullCertificate()
{
// Arrange
var application = new CustomApplication();
var cache = Mock.Of<IOpenIddictApplicationCache<CustomApplication>>();
var logger = Mock.Of<ILogger<OpenIddictApplicationManager<CustomApplication>>>();
var options = Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>();
var store = Mock.Of<IOpenIddictApplicationStore<CustomApplication>>();
var manager = new OpenIddictApplicationManager<CustomApplication>(cache, logger, options, store);
var policy = new X509ChainPolicy();
// Act and assert
var exception = await Assert.ThrowsAsync<ArgumentNullException>(
() => manager.ValidateSelfSignedTlsClientCertificateAsync(application, certificate: null!, policy).AsTask());
Assert.Equal("certificate", exception.ParamName);
}
public class CustomApplication { }
private class CustomApplicationManagerWithProtectedAccess : OpenIddictApplicationManager<CustomApplication>
{
public CustomApplicationManagerWithProtectedAccess(
IOpenIddictApplicationCache<CustomApplication> cache,
ILogger<OpenIddictApplicationManager<CustomApplication>> logger,
IOptionsMonitor<OpenIddictCoreOptions> options,
IOpenIddictApplicationStore<CustomApplication> store)
: base(cache, logger, options, store)
{
}
public new ValueTask<string> ObfuscateClientSecretAsync(string secret, CancellationToken cancellationToken = default)
=> base.ObfuscateClientSecretAsync(secret, cancellationToken);
public new ValueTask<(bool IsValid, bool IsRehashRequired)> ValidateClientSecretAsync(
string secret, string comparand, CancellationToken cancellationToken = default)
=> base.ValidateClientSecretAsync(secret, comparand, cancellationToken);
}
}