Browse Source

Add integration tests for the authorization endpoint

pull/871/head
Kévin Chalet 6 years ago
parent
commit
5361c66be1
  1. 21
      OpenIddict.sln
  2. 1
      eng/Versions.props
  3. 8
      src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs
  4. 3
      test/OpenIddict.Abstractions.Tests/OpenIddict.Abstractions.Tests.csproj
  5. 3
      test/OpenIddict.Core.Tests/OpenIddict.Core.Tests.csproj
  6. 3
      test/OpenIddict.EntityFramework.Tests/OpenIddict.EntityFramework.Tests.csproj
  7. 3
      test/OpenIddict.EntityFrameworkCore.Tests/OpenIddict.EntityFrameworkCore.Tests.csproj
  8. 3
      test/OpenIddict.MongoDb.Tests/OpenIddict.MongoDb.Tests.csproj
  9. 3
      test/OpenIddict.NHibernate.Tests/OpenIddict.NHibernate.Tests.csproj
  10. 24
      test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddict.Server.AspNetCore.IntegrationTests.csproj
  11. 58
      test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.Authentication.cs
  12. 107
      test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.cs
  13. BIN
      test/OpenIddict.Server.IntegrationTests/Certificate.cer
  14. BIN
      test/OpenIddict.Server.IntegrationTests/Certificate.pfx
  15. 27
      test/OpenIddict.Server.IntegrationTests/OpenIddict.Server.IntegrationTests.csproj
  16. 458
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs
  17. 1651
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs
  18. 170
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs
  19. 20
      test/OpenIddict.Server.Owin.IntegrationTests/OpenIddict.Server.Owin.IntegrationTests.csproj
  20. 58
      test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.Authentication.cs
  21. 118
      test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs

21
OpenIddict.sln

@ -92,6 +92,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Validation.Serve
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Validation.SystemNetHttp", "src\OpenIddict.Validation.SystemNetHttp\OpenIddict.Validation.SystemNetHttp.csproj", "{AC3F3AFC-0E3A-4D3B-A245-58211AE630E5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Server.IntegrationTests", "test\OpenIddict.Server.IntegrationTests\OpenIddict.Server.IntegrationTests.csproj", "{0947C388-31DD-45C0-8DD2-61582C648F07}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Server.AspNetCore.IntegrationTests", "test\OpenIddict.Server.AspNetCore.IntegrationTests\OpenIddict.Server.AspNetCore.IntegrationTests.csproj", "{FBFDB9E2-4A44-4B90-B896-E094BFC05C03}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Server.Owin.IntegrationTests", "test\OpenIddict.Server.Owin.IntegrationTests\OpenIddict.Server.Owin.IntegrationTests.csproj", "{E62124D4-3660-4590-B4D1-787168BBBEDD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -226,6 +232,18 @@ Global
{AC3F3AFC-0E3A-4D3B-A245-58211AE630E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC3F3AFC-0E3A-4D3B-A245-58211AE630E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC3F3AFC-0E3A-4D3B-A245-58211AE630E5}.Release|Any CPU.Build.0 = Release|Any CPU
{0947C388-31DD-45C0-8DD2-61582C648F07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0947C388-31DD-45C0-8DD2-61582C648F07}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0947C388-31DD-45C0-8DD2-61582C648F07}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0947C388-31DD-45C0-8DD2-61582C648F07}.Release|Any CPU.Build.0 = Release|Any CPU
{FBFDB9E2-4A44-4B90-B896-E094BFC05C03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FBFDB9E2-4A44-4B90-B896-E094BFC05C03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FBFDB9E2-4A44-4B90-B896-E094BFC05C03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FBFDB9E2-4A44-4B90-B896-E094BFC05C03}.Release|Any CPU.Build.0 = Release|Any CPU
{E62124D4-3660-4590-B4D1-787168BBBEDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E62124D4-3660-4590-B4D1-787168BBBEDD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E62124D4-3660-4590-B4D1-787168BBBEDD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E62124D4-3660-4590-B4D1-787168BBBEDD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -263,6 +281,9 @@ Global
{B5F6A324-AB31-47EB-BF98-48C7105C4BCE} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{36FE030D-855F-4971-9E1A-76DACE53D349} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{AC3F3AFC-0E3A-4D3B-A245-58211AE630E5} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{0947C388-31DD-45C0-8DD2-61582C648F07} = {5FC71D6A-A994-4F62-977F-88A7D25379D7}
{FBFDB9E2-4A44-4B90-B896-E094BFC05C03} = {5FC71D6A-A994-4F62-977F-88A7D25379D7}
{E62124D4-3660-4590-B4D1-787168BBBEDD} = {5FC71D6A-A994-4F62-977F-88A7D25379D7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A710059F-0466-4D48-9B3A-0EF4F840B616}

1
eng/Versions.props

@ -29,6 +29,7 @@
</PropertyGroup>
<PropertyGroup>
<AngleSharpVersion>0.13.0</AngleSharpVersion>
<BclAsyncInterfacesVersion>1.0.0</BclAsyncInterfacesVersion>
<BclHashCodeVersion>1.0.0</BclHashCodeVersion>
<BouncyCastleVersion>1.8.5</BouncyCastleVersion>

8
src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs

@ -207,7 +207,7 @@ namespace OpenIddict.Server
if (string.IsNullOrEmpty(notification.RedirectUri))
{
throw new InvalidOperationException("The request cannot be validated because no client_id was specified.");
throw new InvalidOperationException("The request cannot be validated because no redirect_uri was specified.");
}
context.Logger.LogInformation("The authorization request was successfully validated.");
@ -424,7 +424,7 @@ namespace OpenIddict.Server
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateAuthorizationRequestContext>()
.UseSingletonHandler<ValidateRedirectUriParameter>()
.UseSingletonHandler<ValidateRequestUriParameter>()
.SetOrder(ValidateRequestParameter.Descriptor.Order + 1_000)
.Build();
@ -645,7 +645,7 @@ namespace OpenIddict.Server
context.Reject(
error: Errors.UnsupportedResponseType,
description: "The specified 'response_type' parameter is not supported.");
description: "The specified 'response_type' parameter is not allowed.");
return default;
}
@ -978,7 +978,7 @@ namespace OpenIddict.Server
context.Reject(
error: Errors.InvalidRequest,
description: "The specified code_challenge_method is not supported'.");
description: "The specified code_challenge_method is not supported.");
return default;
}

3
test/OpenIddict.Abstractions.Tests/OpenIddict.Abstractions.Tests.csproj

@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.0;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
<TargetFrameworks>net461;netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>

3
test/OpenIddict.Core.Tests/OpenIddict.Core.Tests.csproj

@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.0;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
<TargetFrameworks>net461;netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>

3
test/OpenIddict.EntityFramework.Tests/OpenIddict.EntityFramework.Tests.csproj

@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp3.0</TargetFrameworks>
<TargetFrameworks>net461;netcoreapp3.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>

3
test/OpenIddict.EntityFrameworkCore.Tests/OpenIddict.EntityFrameworkCore.Tests.csproj

@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.0;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
<TargetFrameworks>net461;netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>

3
test/OpenIddict.MongoDb.Tests/OpenIddict.MongoDb.Tests.csproj

@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.0;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
<TargetFrameworks>net461;netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
<SignAssembly>false</SignAssembly>
<PublicSign>false</PublicSign>
</PropertyGroup>

3
test/OpenIddict.NHibernate.Tests/OpenIddict.NHibernate.Tests.csproj

@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.0;net461</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
<TargetFrameworks>net461;netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>

24
test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddict.Server.AspNetCore.IntegrationTests.csproj

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.0;net472</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.0' ">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\OpenIddict.Server.AspNetCore\OpenIddict.Server.AspNetCore.csproj" />
<ProjectReference Include="..\OpenIddict.Server.IntegrationTests\OpenIddict.Server.IntegrationTests.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(AspNetCoreVersion)" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<Reference Include="System.Net.Http" />
</ItemGroup>
</Project>

58
test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.Authentication.cs

@ -0,0 +1,58 @@
/*
* 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.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using OpenIddict.Server.FunctionalTests;
using Xunit;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace OpenIddict.Server.AspNetCore.FunctionalTests
{
public partial class OpenIddictServerAspNetCoreIntegrationTests : OpenIddictServerIntegrationTests
{
[Fact(Skip = "The handler responsible of rejecting such requests has not been ported yet.")]
public async Task ExtractAuthorizationRequest_RequestIdParameterIsRejectedWhenRequestCachingIsDisabled()
{
// Arrange
var client = CreateClient(options => options.EnableDegradedMode());
// Act
var response = await client.PostAsync("/connect/authorize", new OpenIddictRequest
{
RequestId = "EFAF3596-F868-497F-96BB-AA2AD1F8B7E7"
});
// Assert
Assert.Equal(Errors.InvalidRequest, response.Error);
Assert.Equal("The 'request_id' parameter is not supported.", response.ErrorDescription);
}
[Fact]
public async Task ExtractAuthorizationRequest_InvalidRequestIdParameterIsRejected()
{
// Arrange
var client = CreateClient(options =>
{
options.Services.AddDistributedMemoryCache();
options.UseAspNetCore()
.EnableAuthorizationEndpointCaching();
});
// Act
var response = await client.PostAsync("/connect/authorize", new OpenIddictRequest
{
RequestId = "EFAF3596-F868-497F-96BB-AA2AD1F8B7E7"
});
// Assert
Assert.Equal(Errors.InvalidRequest, response.Error);
Assert.Equal("The specified 'request_id' parameter is invalid.", response.ErrorDescription);
}
}
}

107
test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.cs

@ -0,0 +1,107 @@
/*
* 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;
using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using OpenIddict.Server.FunctionalTests;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace OpenIddict.Server.AspNetCore.FunctionalTests
{
public partial class OpenIddictServerAspNetCoreIntegrationTests : OpenIddictServerIntegrationTests
{
protected override OpenIddictServerIntegrationTestClient CreateClient(Action<OpenIddictServerBuilder> configuration = null)
{
var builder = new WebHostBuilder();
builder.UseEnvironment("Testing");
builder.ConfigureServices(ConfigureServices);
builder.ConfigureServices(services =>
{
services.AddOpenIddict()
.AddServer(options =>
{
// Disable the transport security requirement during testing.
options.UseAspNetCore()
.DisableTransportSecurityRequirement();
configuration?.Invoke(options);
});
});
builder.Configure(app =>
{
app.Use(next => async context =>
{
await next(context);
var feature = context.Features.Get<OpenIddictServerAspNetCoreFeature>();
var response = feature?.Transaction.GetProperty<object>("custom_response");
if (response != null)
{
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize(response));
}
});
app.UseAuthentication();
app.Use(next => context =>
{
if (context.Request.Path == "/invalid-signin")
{
var identity = new ClaimsIdentity(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
identity.AddClaim(Claims.Subject, "Bob le Bricoleur");
var principal = new ClaimsPrincipal(identity);
return context.SignInAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, principal);
}
else if (context.Request.Path == "/invalid-signout")
{
return context.SignOutAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
else if (context.Request.Path == "/invalid-challenge")
{
return context.ChallengeAsync(
OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
new AuthenticationProperties());
}
else if (context.Request.Path == "/invalid-authenticate")
{
return context.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
return next(context);
});
app.Run(context =>
{
context.Response.ContentType = "application/json";
return context.Response.WriteAsync(JsonSerializer.Serialize(new
{
name = "Bob le Magnifique"
}));
});
});
var server = new TestServer(builder);
return new OpenIddictServerIntegrationTestClient(server.CreateClient());
}
}
}

BIN
test/OpenIddict.Server.IntegrationTests/Certificate.cer

Binary file not shown.

BIN
test/OpenIddict.Server.IntegrationTests/Certificate.pfx

Binary file not shown.

27
test/OpenIddict.Server.IntegrationTests/OpenIddict.Server.IntegrationTests.csproj

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.0;net472</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Certificate.cer" />
<EmbeddedResource Include="Certificate.pfx" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\OpenIddict.Core\OpenIddict.Core.csproj" />
<ProjectReference Include="..\..\src\OpenIddict.Server\OpenIddict.Server.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AngleSharp" Version="$(AngleSharpVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(ExtensionsVersion)" />
<PackageReference Include="Moq" Version="$(MoqVersion)" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<Reference Include="System.Net.Http" />
</ItemGroup>
</Project>

458
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTestClient.cs

@ -0,0 +1,458 @@
/*
* 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;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;
using AngleSharp.Html.Parser;
using Microsoft.Extensions.Primitives;
using OpenIddict.Abstractions;
namespace OpenIddict.Server.FunctionalTests
{
/// <summary>
/// Exposes methods that allow sending OpenID Connect
/// requests and extracting the corresponding responses.
/// </summary>
public class OpenIddictServerIntegrationTestClient
{
/// <summary>
/// Initializes a new instance of the OpenID Connect client.
/// </summary>
public OpenIddictServerIntegrationTestClient()
: this(new HttpClient())
{
}
/// <summary>
/// Initializes a new instance of the OpenID Connect client.
/// </summary>
/// <param name="client">The HTTP client used to communicate with the OpenID Connect server.</param>
public OpenIddictServerIntegrationTestClient(HttpClient client)
: this(client, new HtmlParser())
{
}
/// <summary>
/// Initializes a new instance of the OpenID Connect client.
/// </summary>
/// <param name="client">The HTTP client used to communicate with the OpenID Connect server.</param>
/// <param name="parser">The HTML parser used to parse the responses returned by the OpenID Connect server.</param>
public OpenIddictServerIntegrationTestClient(HttpClient client, HtmlParser parser)
{
if (client == null)
{
throw new ArgumentNullException(nameof(client));
}
if (parser == null)
{
throw new ArgumentNullException(nameof(parser));
}
HttpClient = client;
HtmlParser = parser;
}
/// <summary>
/// Gets the underlying HTTP client used to
/// communicate with the OpenID Connect server.
/// </summary>
public HttpClient HttpClient { get; }
/// <summary>
/// Gets the underlying HTML parser used to parse the
/// responses returned by the OpenID Connect server.
/// </summary>
public HtmlParser HtmlParser { get; }
/// <summary>
/// Sends an empty OpenID Connect request to the given endpoint using GET
/// and converts the returned response to an OpenID Connect response.
/// </summary>
/// <param name="uri">The endpoint to which the request is sent.</param>
/// <returns>The OpenID Connect response returned by the server.</returns>
public Task<OpenIddictResponse> GetAsync(string uri)
=> GetAsync(uri, new OpenIddictRequest());
/// <summary>
/// Sends an empty OpenID Connect request to the given endpoint using GET
/// and converts the returned response to an OpenID Connect response.
/// </summary>
/// <param name="uri">The endpoint to which the request is sent.</param>
/// <returns>The OpenID Connect response returned by the server.</returns>
public Task<OpenIddictResponse> GetAsync(Uri uri)
=> GetAsync(uri, new OpenIddictRequest());
/// <summary>
/// Sends a generic OpenID Connect request to the given endpoint using GET
/// and converts the returned response to an OpenID Connect response.
/// </summary>
/// <param name="uri">The endpoint to which the request is sent.</param>
/// <param name="request">The OpenID Connect request to send.</param>
/// <returns>The OpenID Connect response returned by the server.</returns>
public Task<OpenIddictResponse> GetAsync(string uri, OpenIddictRequest request)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (string.IsNullOrEmpty(uri))
{
throw new ArgumentException("The URL cannot be null or empty.", nameof(uri));
}
return GetAsync(new Uri(uri, UriKind.RelativeOrAbsolute), request);
}
/// <summary>
/// Sends a generic OpenID Connect request to the given endpoint using GET
/// and converts the returned response to an OpenID Connect response.
/// </summary>
/// <param name="uri">The endpoint to which the request is sent.</param>
/// <param name="request">The OpenID Connect request to send.</param>
/// <returns>The OpenID Connect response returned by the server.</returns>
public Task<OpenIddictResponse> GetAsync(Uri uri, OpenIddictRequest request)
=> SendAsync(HttpMethod.Get, uri, request);
/// <summary>
/// Sends a generic OpenID Connect request to the given endpoint using POST
/// and converts the returned response to an OpenID Connect response.
/// </summary>
/// <param name="uri">The endpoint to which the request is sent.</param>
/// <param name="request">The OpenID Connect request to send.</param>
/// <returns>The OpenID Connect response returned by the server.</returns>
public Task<OpenIddictResponse> PostAsync(string uri, OpenIddictRequest request)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (string.IsNullOrEmpty(uri))
{
throw new ArgumentException("The URL cannot be null or empty.", nameof(uri));
}
return PostAsync(new Uri(uri, UriKind.RelativeOrAbsolute), request);
}
/// <summary>
/// Sends a generic OpenID Connect request to the given endpoint using POST
/// and converts the returned response to an OpenID Connect response.
/// </summary>
/// <param name="uri">The endpoint to which the request is sent.</param>
/// <param name="request">The OpenID Connect request to send.</param>
/// <returns>The OpenID Connect response returned by the server.</returns>
public Task<OpenIddictResponse> PostAsync(Uri uri, OpenIddictRequest request)
=> SendAsync(HttpMethod.Post, uri, request);
/// <summary>
/// Sends a generic OpenID Connect request to the given endpoint and
/// converts the returned response to an OpenID Connect response.
/// </summary>
/// <param name="method">The HTTP method used to send the OpenID Connect request.</param>
/// <param name="uri">The endpoint to which the request is sent.</param>
/// <param name="request">The OpenID Connect request to send.</param>
/// <returns>The OpenID Connect response returned by the server.</returns>
public Task<OpenIddictResponse> SendAsync(string method, string uri, OpenIddictRequest request)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (string.IsNullOrEmpty(method))
{
throw new ArgumentException("The HTTP method cannot be null or empty.", nameof(method));
}
if (string.IsNullOrEmpty(uri))
{
throw new ArgumentException("The URL cannot be null or empty.", nameof(uri));
}
return SendAsync(new HttpMethod(method), uri, request);
}
/// <summary>
/// Sends a generic OpenID Connect request to the given endpoint and
/// converts the returned response to an OpenID Connect response.
/// </summary>
/// <param name="method">The HTTP method used to send the OpenID Connect request.</param>
/// <param name="uri">The endpoint to which the request is sent.</param>
/// <param name="request">The OpenID Connect request to send.</param>
/// <returns>The OpenID Connect response returned by the server.</returns>
public Task<OpenIddictResponse> SendAsync(HttpMethod method, string uri, OpenIddictRequest request)
{
if (method == null)
{
throw new ArgumentNullException(nameof(method));
}
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (string.IsNullOrEmpty(uri))
{
throw new ArgumentException("The URL cannot be null or empty.", nameof(uri));
}
return SendAsync(method, new Uri(uri, UriKind.RelativeOrAbsolute), request);
}
/// <summary>
/// Sends a generic OpenID Connect request to the given endpoint and
/// converts the returned response to an OpenID Connect response.
/// </summary>
/// <param name="method">The HTTP method used to send the OpenID Connect request.</param>
/// <param name="uri">The endpoint to which the request is sent.</param>
/// <param name="request">The OpenID Connect request to send.</param>
/// <returns>The OpenID Connect response returned by the server.</returns>
public virtual async Task<OpenIddictResponse> SendAsync(HttpMethod method, Uri uri, OpenIddictRequest request)
{
if (method == null)
{
throw new ArgumentNullException(nameof(method));
}
if (uri == null)
{
throw new ArgumentNullException(nameof(uri));
}
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (HttpClient.BaseAddress == null && !uri.IsAbsoluteUri)
{
throw new ArgumentException("The address cannot be a relative URI when no base address " +
"is associated with the HTTP client.", nameof(uri));
}
return await GetResponseAsync(await HttpClient.SendAsync(CreateRequestMessage(request, method, uri)));
}
private HttpRequestMessage CreateRequestMessage(OpenIddictRequest request, HttpMethod method, Uri uri)
{
// Note: a dictionary is deliberately not used here to allow multiple parameters with the
// same name to be specified. While initially not allowed by the core OAuth2 specification,
// this is required for derived drafts like the OAuth2 token exchange specification.
var parameters = new List<KeyValuePair<string, string>>();
foreach (var parameter in request.GetParameters())
{
// If the parameter is null or empty, send an empty value.
if (OpenIddictParameter.IsNullOrEmpty(parameter.Value))
{
parameters.Add(new KeyValuePair<string, string>(parameter.Key, string.Empty));
continue;
}
var values = (string[]) parameter.Value;
if (values == null || values.Length == 0)
{
continue;
}
foreach (var value in values)
{
parameters.Add(new KeyValuePair<string, string>(parameter.Key, value));
}
}
if (method == HttpMethod.Get && parameters.Count != 0)
{
var builder = new StringBuilder();
foreach (var parameter in parameters)
{
if (builder.Length != 0)
{
builder.Append('&');
}
builder.Append(UrlEncoder.Default.Encode(parameter.Key));
builder.Append('=');
builder.Append(UrlEncoder.Default.Encode(parameter.Value));
}
if (!uri.IsAbsoluteUri)
{
uri = new Uri(HttpClient.BaseAddress, uri);
}
uri = new UriBuilder(uri) { Query = builder.ToString() }.Uri;
}
var message = new HttpRequestMessage(method, uri);
if (method != HttpMethod.Get)
{
message.Content = new FormUrlEncodedContent(parameters);
}
return message;
}
private async Task<OpenIddictResponse> GetResponseAsync(HttpResponseMessage message)
{
if (message.Headers.Location != null)
{
var payload = message.Headers.Location.Fragment;
if (string.IsNullOrEmpty(payload))
{
payload = message.Headers.Location.Query;
}
if (string.IsNullOrEmpty(payload))
{
return new OpenIddictResponse();
}
string UnescapeDataString(string value)
{
if (string.IsNullOrEmpty(value))
{
return null;
}
return Uri.UnescapeDataString(value.Replace("+", "%20"));
}
// Note: a dictionary is deliberately not used here to allow multiple parameters with the
// same name to be retrieved. While initially not allowed by the core OAuth2 specification,
// this is required for derived drafts like the OAuth2 token exchange specification.
var parameters = new List<KeyValuePair<string, string>>();
foreach (var element in new StringTokenizer(payload, OpenIddictConstants.Separators.Ampersand))
{
var segment = element;
if (segment.Length == 0)
{
continue;
}
// Always skip the first char (# or ?).
if (segment.Offset == 0)
{
segment = segment.Subsegment(1, segment.Length - 1);
}
var index = segment.IndexOf('=');
if (index == -1)
{
continue;
}
var name = UnescapeDataString(segment.Substring(0, index));
if (string.IsNullOrEmpty(name))
{
continue;
}
var value = UnescapeDataString(segment.Substring(index + 1, segment.Length - (index + 1)));
parameters.Add(new KeyValuePair<string, string>(name, value));
}
return new OpenIddictResponse(
from parameter in parameters
group parameter by parameter.Key into grouping
let values = grouping.Select(parameter => parameter.Value)
select new KeyValuePair<string, StringValues>(grouping.Key, values.ToArray()));
}
else if (string.Equals(message.Content?.Headers?.ContentType?.MediaType, "application/json", StringComparison.OrdinalIgnoreCase))
{
using var stream = await message.Content.ReadAsStreamAsync();
return await JsonSerializer.DeserializeAsync<OpenIddictResponse>(stream);
}
else if (string.Equals(message.Content?.Headers?.ContentType?.MediaType, "text/html", StringComparison.OrdinalIgnoreCase))
{
using var stream = await message.Content.ReadAsStreamAsync();
using var document = await HtmlParser.ParseDocumentAsync(stream);
// Note: a dictionary is deliberately not used here to allow multiple parameters with the
// same name to be retrieved. While initially not allowed by the core OAuth2 specification,
// this is required for derived drafts like the OAuth2 token exchange specification.
var parameters = new List<KeyValuePair<string, string>>();
foreach (var element in document.Body.GetElementsByTagName("input"))
{
var name = element.GetAttribute("name");
if (string.IsNullOrEmpty(name))
{
continue;
}
var value = element.GetAttribute("value");
parameters.Add(new KeyValuePair<string, string>(name, value));
}
return new OpenIddictResponse(
from parameter in parameters
group parameter by parameter.Key into grouping
let values = grouping.Select(parameter => parameter.Value)
select new KeyValuePair<string, StringValues>(grouping.Key, values.ToArray()));
}
else if (string.Equals(message.Content?.Headers?.ContentType?.MediaType, "text/plain", StringComparison.OrdinalIgnoreCase))
{
using (var stream = await message.Content.ReadAsStreamAsync())
using (var reader = new StreamReader(stream))
{
// Note: a dictionary is deliberately not used here to allow multiple parameters with the
// same name to be retrieved. While initially not allowed by the core OAuth2 specification,
// this is required for derived drafts like the OAuth2 token exchange specification.
var parameters = new List<KeyValuePair<string, string>>();
for (var line = await reader.ReadLineAsync(); line != null; line = await reader.ReadLineAsync())
{
var index = line.IndexOf(':');
if (index == -1)
{
continue;
}
var name = line.Substring(0, index);
if (string.IsNullOrEmpty(name))
{
continue;
}
var value = line.Substring(index + 1);
parameters.Add(new KeyValuePair<string, string>(name, value));
}
return new OpenIddictResponse(
from parameter in parameters
group parameter by parameter.Key into grouping
let values = grouping.Select(parameter => parameter.Value)
select new KeyValuePair<string, StringValues>(grouping.Key, values.ToArray()));
}
}
return new OpenIddictResponse();
}
}
}

1651
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs

File diff suppressed because it is too large

170
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs

@ -0,0 +1,170 @@
/*
* 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;
using System.Security.Claims;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents;
namespace OpenIddict.Server.FunctionalTests
{
public abstract partial class OpenIddictServerIntegrationTests
{
protected virtual void ConfigureServices(IServiceCollection services)
{
services.AddOpenIddict()
.AddCore(options =>
{
options.SetDefaultApplicationEntity<OpenIddictApplication>()
.SetDefaultAuthorizationEntity<OpenIddictAuthorization>()
.SetDefaultScopeEntity<OpenIddictScope>()
.SetDefaultTokenEntity<OpenIddictToken>();
options.Services.AddSingleton(CreateApplicationManager())
.AddSingleton(CreateAuthorizationManager())
.AddSingleton(CreateScopeManager())
.AddSingleton(CreateTokenManager());
})
.AddServer(options =>
{
// Enable the tested endpoints.
options.SetAuthorizationEndpointUris("/connect/authorize")
.SetConfigurationEndpointUris("/.well-known/openid-configuration")
.SetCryptographyEndpointUris("/.well-known/jwks")
.SetIntrospectionEndpointUris("/connect/introspect")
.SetLogoutEndpointUris("/connect/logout")
.SetRevocationEndpointUris("/connect/revoke")
.SetTokenEndpointUris("/connect/token")
.SetUserinfoEndpointUris("/connect/userinfo");
options.AllowAuthorizationCodeFlow()
.AllowClientCredentialsFlow()
.AllowImplicitFlow()
.AllowPasswordFlow()
.AllowRefreshTokenFlow();
// Accept anonymous clients by default.
options.AcceptAnonymousClients();
// Disable permission enforcement by default.
options.IgnoreEndpointPermissions()
.IgnoreGrantTypePermissions()
.IgnoreScopePermissions();
options.AddSigningCertificate(
assembly: typeof(OpenIddictServerIntegrationTests).Assembly,
resource: "OpenIddict.Server.IntegrationTests.Certificate.pfx",
password: "Owin.Security.OpenIdConnect.Server");
options.AddEncryptionCertificate(
assembly: typeof(OpenIddictServerIntegrationTests).Assembly,
resource: "OpenIddict.Server.IntegrationTests.Certificate.pfx",
password: "Owin.Security.OpenIdConnect.Server");
options.AddEventHandler<ValidateAuthorizationRequestContext>(builder =>
builder.UseInlineHandler(context => default));
options.AddEventHandler<ValidateIntrospectionRequestContext>(builder =>
builder.UseInlineHandler(context => default));
options.AddEventHandler<ValidateLogoutRequestContext>(builder =>
builder.UseInlineHandler(context => default));
options.AddEventHandler<ValidateRevocationRequestContext>(builder =>
builder.UseInlineHandler(context => default));
options.AddEventHandler<ValidateTokenRequestContext>(builder =>
builder.UseInlineHandler(context => default));
options.AddEventHandler<HandleAuthorizationRequestContext>(builder =>
{
builder.UseInlineHandler(context =>
{
var identity = new ClaimsIdentity("Bearer");
identity.AddClaim(Claims.Subject, "Bob le Magnifique");
context.Principal = new ClaimsPrincipal(identity);
context.HandleAuthentication();
return default;
});
builder.SetOrder(int.MaxValue);
});
});
}
protected abstract OpenIddictServerIntegrationTestClient CreateClient(Action<OpenIddictServerBuilder> configuration = null);
protected OpenIddictApplicationManager<OpenIddictApplication> CreateApplicationManager(
Action<Mock<OpenIddictApplicationManager<OpenIddictApplication>>> configuration = null)
{
var manager = new Mock<OpenIddictApplicationManager<OpenIddictApplication>>(
Mock.Of<IOpenIddictApplicationCache<OpenIddictApplication>>(),
Mock.Of<IOpenIddictApplicationStoreResolver>(),
Mock.Of<ILogger<OpenIddictApplicationManager<OpenIddictApplication>>>(),
Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>());
configuration?.Invoke(manager);
return manager.Object;
}
protected OpenIddictAuthorizationManager<OpenIddictAuthorization> CreateAuthorizationManager(
Action<Mock<OpenIddictAuthorizationManager<OpenIddictAuthorization>>> configuration = null)
{
var manager = new Mock<OpenIddictAuthorizationManager<OpenIddictAuthorization>>(
Mock.Of<IOpenIddictAuthorizationCache<OpenIddictAuthorization>>(),
Mock.Of<IOpenIddictAuthorizationStoreResolver>(),
Mock.Of<ILogger<OpenIddictAuthorizationManager<OpenIddictAuthorization>>>(),
Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>());
configuration?.Invoke(manager);
return manager.Object;
}
protected OpenIddictScopeManager<OpenIddictScope> CreateScopeManager(
Action<Mock<OpenIddictScopeManager<OpenIddictScope>>> configuration = null)
{
var manager = new Mock<OpenIddictScopeManager<OpenIddictScope>>(
Mock.Of<IOpenIddictScopeCache<OpenIddictScope>>(),
Mock.Of<IOpenIddictScopeStoreResolver>(),
Mock.Of<ILogger<OpenIddictScopeManager<OpenIddictScope>>>(),
Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>());
configuration?.Invoke(manager);
return manager.Object;
}
protected OpenIddictTokenManager<OpenIddictToken> CreateTokenManager(
Action<Mock<OpenIddictTokenManager<OpenIddictToken>>> configuration = null)
{
var manager = new Mock<OpenIddictTokenManager<OpenIddictToken>>(
Mock.Of<IOpenIddictTokenCache<OpenIddictToken>>(),
Mock.Of<IOpenIddictTokenStoreResolver>(),
Mock.Of<ILogger<OpenIddictTokenManager<OpenIddictToken>>>(),
Mock.Of<IOptionsMonitor<OpenIddictCoreOptions>>());
configuration?.Invoke(manager);
return manager.Object;
}
public class OpenIddictApplication { }
public class OpenIddictAuthorization { }
public class OpenIddictScope { }
public class OpenIddictToken { }
}
}

20
test/OpenIddict.Server.Owin.IntegrationTests/OpenIddict.Server.Owin.IntegrationTests.csproj

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\OpenIddict.Server.Owin\OpenIddict.Server.Owin.csproj" />
<ProjectReference Include="..\OpenIddict.Server.IntegrationTests\OpenIddict.Server.IntegrationTests.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Owin.Testing" Version="$(OwinVersion)" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<Reference Include="System.Net.Http" />
</ItemGroup>
</Project>

58
test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.Authentication.cs

@ -0,0 +1,58 @@
/*
* 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.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using OpenIddict.Server.FunctionalTests;
using Xunit;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace OpenIddict.Server.Owin.FunctionalTests
{
public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerIntegrationTests
{
[Fact(Skip = "The handler responsible of rejecting such requests has not been ported yet.")]
public async Task ExtractAuthorizationRequest_RequestIdParameterIsRejectedWhenRequestCachingIsDisabled()
{
// Arrange
var client = CreateClient(options => options.EnableDegradedMode());
// Act
var response = await client.PostAsync("/connect/authorize", new OpenIddictRequest
{
RequestId = "EFAF3596-F868-497F-96BB-AA2AD1F8B7E7"
});
// Assert
Assert.Equal(Errors.InvalidRequest, response.Error);
Assert.Equal("The 'request_id' parameter is not supported.", response.ErrorDescription);
}
[Fact]
public async Task ExtractAuthorizationRequest_InvalidRequestIdParameterIsRejected()
{
// Arrange
var client = CreateClient(options =>
{
options.Services.AddDistributedMemoryCache();
options.UseOwin()
.EnableAuthorizationEndpointCaching();
});
// Act
var response = await client.PostAsync("/connect/authorize", new OpenIddictRequest
{
RequestId = "EFAF3596-F868-497F-96BB-AA2AD1F8B7E7"
});
// Assert
Assert.Equal(Errors.InvalidRequest, response.Error);
Assert.Equal("The specified 'request_id' parameter is invalid.", response.ErrorDescription);
}
}
}

118
test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs

@ -0,0 +1,118 @@
/*
* 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;
using System.Security.Claims;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Owin;
using Microsoft.Owin.Testing;
using OpenIddict.Abstractions;
using OpenIddict.Server.FunctionalTests;
using Owin;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace OpenIddict.Server.Owin.FunctionalTests
{
public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerIntegrationTests
{
protected override OpenIddictServerIntegrationTestClient CreateClient(Action<OpenIddictServerBuilder> configuration = null)
{
var services = new ServiceCollection();
ConfigureServices(services);
services.AddOpenIddict()
.AddServer(options =>
{
// Disable the transport security requirement during testing.
options.UseOwin()
.DisableTransportSecurityRequirement();
configuration?.Invoke(options);
});
var provider = services.BuildServiceProvider();
var server = TestServer.Create(app =>
{
app.Use(async (context, next) =>
{
using var scope = provider.CreateScope();
context.Set(typeof(IServiceProvider).FullName, scope.ServiceProvider);
try
{
await next();
}
finally
{
context.Environment.Remove(typeof(IServiceProvider).FullName);
}
});
app.Use(async (context, next) =>
{
await next();
var transaction = context.Get<OpenIddictServerTransaction>(typeof(OpenIddictServerTransaction).FullName);
var response = transaction?.GetProperty<object>("custom_response");
if (response != null)
{
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize(response));
}
});
app.UseOpenIddictServer();
app.Use((context, next) =>
{
if (context.Request.Path == new PathString("/invalid-signin"))
{
var identity = new ClaimsIdentity(OpenIddictServerOwinDefaults.AuthenticationType);
identity.AddClaim(Claims.Subject, "Bob le Bricoleur");
context.Authentication.SignIn(identity);
return Task.CompletedTask;
}
else if (context.Request.Path == new PathString("/invalid-signout"))
{
context.Authentication.SignOut(OpenIddictServerOwinDefaults.AuthenticationType);
return Task.CompletedTask;
}
else if (context.Request.Path == new PathString("/invalid-challenge"))
{
context.Authentication.Challenge(OpenIddictServerOwinDefaults.AuthenticationType);
return Task.CompletedTask;
}
else if (context.Request.Path == new PathString("/invalid-authenticate"))
{
return context.Authentication.AuthenticateAsync(OpenIddictServerOwinDefaults.AuthenticationType);
}
return next();
});
app.Run(context =>
{
context.Response.ContentType = "application/json";
return context.Response.WriteAsync(JsonSerializer.Serialize(new
{
name = "Bob le Magnifique"
}));
});
});
return new OpenIddictServerIntegrationTestClient(server.HttpClient);
}
}
}
Loading…
Cancel
Save