Browse Source

Update the ASP.NET Core/OWIN hosts to support returning authentication properties for errored requests

pull/2222/head
Kévin Chalet 1 year ago
parent
commit
878569cd3f
  1. 2
      sandbox/OpenIddict.Sandbox.AspNet.Server/Startup.cs
  2. 2
      sandbox/OpenIddict.Sandbox.AspNetCore.Server/Worker.cs
  3. 23
      src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs
  4. 21
      src/OpenIddict.Client.Owin/OpenIddictClientOwinHandler.cs
  5. 23
      src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs
  6. 21
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs
  7. 23
      src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs
  8. 21
      src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs
  9. 49
      test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.cs
  10. 51
      test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.cs
  11. 2
      test/OpenIddict.Validation.Owin.IntegrationTests/OpenIddictValidationOwinIntegrationTests.cs

2
sandbox/OpenIddict.Sandbox.AspNet.Server/Startup.cs

@ -201,7 +201,7 @@ public class Startup
ClientId = "mvc", ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654", ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
ClientType = ClientTypes.Confidential, ClientType = ClientTypes.Confidential,
ConsentType = ConsentTypes.Explicit, ConsentType = ConsentTypes.Systematic,
DisplayName = "MVC client application", DisplayName = "MVC client application",
RedirectUris = RedirectUris =
{ {

2
sandbox/OpenIddict.Sandbox.AspNetCore.Server/Worker.cs

@ -152,7 +152,7 @@ public class Worker : IHostedService
ClientId = "mvc", ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654", ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
ClientType = ClientTypes.Confidential, ClientType = ClientTypes.Confidential,
ConsentType = ConsentTypes.Explicit, ConsentType = ConsentTypes.Systematic,
DisplayName = "MVC client application", DisplayName = "MVC client application",
DisplayNames = DisplayNames =
{ {

23
src/OpenIddict.Client.AspNetCore/OpenIddictClientAspNetCoreHandler.cs

@ -156,17 +156,24 @@ public sealed class OpenIddictClientAspNetCoreHandler : AuthenticationHandler<Op
return AuthenticateResult.NoResult(); return AuthenticateResult.NoResult();
} }
var properties = new AuthenticationProperties(new Dictionary<string, string?> var properties = CreateAuthenticationProperties();
{ properties.Items[Properties.Error] = context.Error;
[Properties.Error] = context.Error, properties.Items[Properties.ErrorDescription] = context.ErrorDescription;
[Properties.ErrorDescription] = context.ErrorDescription, properties.Items[Properties.ErrorUri] = context.ErrorUri;
[Properties.ErrorUri] = context.ErrorUri
});
return AuthenticateResult.Fail(SR.GetResourceString(SR.ID0113), properties); return AuthenticateResult.Fail(SR.GetResourceString(SR.ID0113), properties);
} }
else else
{
var properties = CreateAuthenticationProperties();
return AuthenticateResult.Success(new AuthenticationTicket(
context.MergedPrincipal ?? new ClaimsPrincipal(new ClaimsIdentity()), properties,
OpenIddictClientAspNetCoreDefaults.AuthenticationScheme));
}
AuthenticationProperties CreateAuthenticationProperties()
{ {
var properties = new AuthenticationProperties var properties = new AuthenticationProperties
{ {
@ -336,9 +343,7 @@ public sealed class OpenIddictClientAspNetCoreHandler : AuthenticationHandler<Op
properties.SetParameter(Properties.UserInfoTokenPrincipal, context.UserInfoTokenPrincipal); properties.SetParameter(Properties.UserInfoTokenPrincipal, context.UserInfoTokenPrincipal);
} }
return AuthenticateResult.Success(new AuthenticationTicket( return properties;
context.MergedPrincipal ?? new ClaimsPrincipal(new ClaimsIdentity()), properties,
OpenIddictClientAspNetCoreDefaults.AuthenticationScheme));
} }
} }

21
src/OpenIddict.Client.Owin/OpenIddictClientOwinHandler.cs

@ -155,17 +155,22 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler<OpenIddi
return null; return null;
} }
var properties = new AuthenticationProperties(new Dictionary<string, string?> var properties = CreateAuthenticationProperties();
{ properties.Dictionary[Properties.Error] = context.Error;
[Properties.Error] = context.Error, properties.Dictionary[Properties.ErrorDescription] = context.ErrorDescription;
[Properties.ErrorDescription] = context.ErrorDescription, properties.Dictionary[Properties.ErrorUri] = context.ErrorUri;
[Properties.ErrorUri] = context.ErrorUri
});
return new AuthenticationTicket(null, properties); return new AuthenticationTicket(new ClaimsIdentity(), properties);
} }
else else
{
var properties = CreateAuthenticationProperties();
return new AuthenticationTicket(context.MergedPrincipal?.Identity as ClaimsIdentity ?? new ClaimsIdentity(), properties);
}
AuthenticationProperties CreateAuthenticationProperties()
{ {
var properties = new AuthenticationProperties var properties = new AuthenticationProperties
{ {
@ -240,7 +245,7 @@ public sealed class OpenIddictClientOwinHandler : AuthenticationHandler<OpenIddi
properties.Dictionary[Tokens.UserInfoToken] = context.UserInfoToken; properties.Dictionary[Tokens.UserInfoToken] = context.UserInfoToken;
} }
return new AuthenticationTicket(context.MergedPrincipal?.Identity as ClaimsIdentity, properties); return properties;
} }
} }

23
src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs

@ -156,12 +156,10 @@ public sealed class OpenIddictServerAspNetCoreHandler : AuthenticationHandler<Op
return AuthenticateResult.NoResult(); return AuthenticateResult.NoResult();
} }
var properties = new AuthenticationProperties(new Dictionary<string, string?> var properties = CreateAuthenticationProperties();
{ properties.Items[Properties.Error] = context.Error;
[Properties.Error] = context.Error, properties.Items[Properties.ErrorDescription] = context.ErrorDescription;
[Properties.ErrorDescription] = context.ErrorDescription, properties.Items[Properties.ErrorUri] = context.ErrorUri;
[Properties.ErrorUri] = context.ErrorUri
});
return AuthenticateResult.Fail(SR.GetResourceString(SR.ID0113), properties); return AuthenticateResult.Fail(SR.GetResourceString(SR.ID0113), properties);
} }
@ -199,6 +197,15 @@ public sealed class OpenIddictServerAspNetCoreHandler : AuthenticationHandler<Op
_ => null _ => null
}; };
var properties = CreateAuthenticationProperties(principal);
return AuthenticateResult.Success(new AuthenticationTicket(
principal ?? new ClaimsPrincipal(new ClaimsIdentity()), properties,
OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
}
AuthenticationProperties CreateAuthenticationProperties(ClaimsPrincipal? principal = null)
{
var properties = new AuthenticationProperties var properties = new AuthenticationProperties
{ {
ExpiresUtc = principal?.GetExpirationDate(), ExpiresUtc = principal?.GetExpirationDate(),
@ -325,9 +332,7 @@ public sealed class OpenIddictServerAspNetCoreHandler : AuthenticationHandler<Op
properties.StoreTokens(tokens); properties.StoreTokens(tokens);
} }
return AuthenticateResult.Success(new AuthenticationTicket( return properties;
principal ?? new ClaimsPrincipal(new ClaimsIdentity()), properties,
OpenIddictServerAspNetCoreDefaults.AuthenticationScheme));
} }
} }

21
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs

@ -151,14 +151,12 @@ public sealed class OpenIddictServerOwinHandler : AuthenticationHandler<OpenIddi
return null; return null;
} }
var properties = new AuthenticationProperties(new Dictionary<string, string?> var properties = CreateAuthenticationProperties();
{ properties.Dictionary[Properties.Error] = context.Error;
[Properties.Error] = context.Error, properties.Dictionary[Properties.ErrorDescription] = context.ErrorDescription;
[Properties.ErrorDescription] = context.ErrorDescription, properties.Dictionary[Properties.ErrorUri] = context.ErrorUri;
[Properties.ErrorUri] = context.ErrorUri
});
return new AuthenticationTicket(null, properties); return new AuthenticationTicket(new ClaimsIdentity(), properties);
} }
else else
@ -191,6 +189,13 @@ public sealed class OpenIddictServerOwinHandler : AuthenticationHandler<OpenIddi
_ => null _ => null
}; };
var properties = CreateAuthenticationProperties(principal);
return new AuthenticationTicket(principal?.Identity as ClaimsIdentity ?? new ClaimsIdentity(), properties);
}
AuthenticationProperties CreateAuthenticationProperties(ClaimsPrincipal? principal = null)
{
var properties = new AuthenticationProperties var properties = new AuthenticationProperties
{ {
ExpiresUtc = principal?.GetExpirationDate(), ExpiresUtc = principal?.GetExpirationDate(),
@ -240,7 +245,7 @@ public sealed class OpenIddictServerOwinHandler : AuthenticationHandler<OpenIddi
properties.Dictionary[Tokens.UserCode] = context.UserCode; properties.Dictionary[Tokens.UserCode] = context.UserCode;
} }
return new AuthenticationTicket(principal?.Identity as ClaimsIdentity, properties); return properties;
} }
} }

23
src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs

@ -154,12 +154,10 @@ public sealed class OpenIddictValidationAspNetCoreHandler : AuthenticationHandle
return AuthenticateResult.NoResult(); return AuthenticateResult.NoResult();
} }
var properties = new AuthenticationProperties(new Dictionary<string, string?> var properties = CreateAuthenticationProperties();
{ properties.Items[Properties.Error] = context.Error;
[Properties.Error] = context.Error, properties.Items[Properties.ErrorDescription] = context.ErrorDescription;
[Properties.ErrorDescription] = context.ErrorDescription, properties.Items[Properties.ErrorUri] = context.ErrorUri;
[Properties.ErrorUri] = context.ErrorUri
});
return AuthenticateResult.Fail(SR.GetResourceString(SR.ID0113), properties); return AuthenticateResult.Fail(SR.GetResourceString(SR.ID0113), properties);
} }
@ -177,6 +175,15 @@ public sealed class OpenIddictValidationAspNetCoreHandler : AuthenticationHandle
_ => null _ => null
}; };
var properties = CreateAuthenticationProperties(principal);
return AuthenticateResult.Success(new AuthenticationTicket(
principal ?? new ClaimsPrincipal(new ClaimsIdentity()), properties,
OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme));
}
AuthenticationProperties CreateAuthenticationProperties(ClaimsPrincipal? principal = null)
{
var properties = new AuthenticationProperties var properties = new AuthenticationProperties
{ {
ExpiresUtc = principal?.GetExpirationDate(), ExpiresUtc = principal?.GetExpirationDate(),
@ -208,9 +215,7 @@ public sealed class OpenIddictValidationAspNetCoreHandler : AuthenticationHandle
properties.StoreTokens(tokens); properties.StoreTokens(tokens);
} }
return AuthenticateResult.Success(new AuthenticationTicket( return properties;
principal ?? new ClaimsPrincipal(new ClaimsIdentity()), properties,
OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme));
} }
} }

21
src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs

@ -150,14 +150,12 @@ public sealed class OpenIddictValidationOwinHandler : AuthenticationHandler<Open
return null; return null;
} }
var properties = new AuthenticationProperties(new Dictionary<string, string?> var properties = CreateAuthenticationProperties();
{ properties.Dictionary[Properties.Error] = context.Error;
[Properties.Error] = context.Error, properties.Dictionary[Properties.ErrorDescription] = context.ErrorDescription;
[Properties.ErrorDescription] = context.ErrorDescription, properties.Dictionary[Properties.ErrorUri] = context.ErrorUri;
[Properties.ErrorUri] = context.ErrorUri
});
return new AuthenticationTicket(null, properties); return new AuthenticationTicket(new ClaimsIdentity(), properties);
} }
else else
@ -170,6 +168,13 @@ public sealed class OpenIddictValidationOwinHandler : AuthenticationHandler<Open
_ => null _ => null
}; };
var properties = CreateAuthenticationProperties(principal);
return new AuthenticationTicket(principal?.Identity as ClaimsIdentity ?? new ClaimsIdentity(), properties);
}
AuthenticationProperties CreateAuthenticationProperties(ClaimsPrincipal? principal = null)
{
var properties = new AuthenticationProperties var properties = new AuthenticationProperties
{ {
ExpiresUtc = principal?.GetExpirationDate(), ExpiresUtc = principal?.GetExpirationDate(),
@ -184,7 +189,7 @@ public sealed class OpenIddictValidationOwinHandler : AuthenticationHandler<Open
properties.Dictionary[TokenTypeHints.AccessToken] = context.AccessToken; properties.Dictionary[TokenTypeHints.AccessToken] = context.AccessToken;
} }
return new AuthenticationTicket(principal?.Identity as ClaimsIdentity, properties); return properties;
} }
} }

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

@ -18,6 +18,7 @@ using OpenIddict.Server.IntegrationTests;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
using static OpenIddict.Server.OpenIddictServerEvents; using static OpenIddict.Server.OpenIddictServerEvents;
using static OpenIddict.Server.OpenIddictServerHandlers;
using static OpenIddict.Server.OpenIddictServerHandlers.Protection; using static OpenIddict.Server.OpenIddictServerHandlers.Protection;
#if SUPPORTS_JSON_NODES #if SUPPORTS_JSON_NODES
@ -134,6 +135,54 @@ public partial class OpenIddictServerAspNetCoreIntegrationTests : OpenIddictServ
Assert.Equal(new DateTimeOffset(2120, 01, 01, 00, 00, 00, TimeSpan.Zero), properties.ExpiresUtc); Assert.Equal(new DateTimeOffset(2120, 01, 01, 00, 00, 00, TimeSpan.Zero), properties.ExpiresUtc);
} }
[Fact]
public async Task ProcessAuthentication_CustomPropertiesAreAddedForErroredAuthenticationResults()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetAuthorizationEndpointUris("/authenticate/properties");
options.UseAspNetCore()
.EnableErrorPassthrough()
.EnableAuthorizationEndpointPassthrough();
options.AddEventHandler<ProcessAuthenticationContext>(builder =>
{
builder.UseInlineHandler(context =>
{
context.RejectIdentityToken = true;
context.Properties["custom_property"] = "value";
return default;
});
builder.SetOrder(EvaluateValidatedTokens.Descriptor.Order + 1);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.PostAsync("/authenticate/properties", new OpenIddictRequest
{
ClientId = "Fabrikam",
IdTokenHint = "id_token_hint",
Nonce = "n-0S6_WzA2Mj",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = "id_token",
Scope = Scopes.OpenId
});
// Assert
var properties = new AuthenticationProperties(response.GetParameters()
.ToDictionary(parameter => parameter.Key, parameter => (string?) parameter.Value));
Assert.Equal("value", properties.Items["custom_property"]);
}
[Fact] [Fact]
public async Task ProcessChallenge_ImportsAuthenticationProperties() public async Task ProcessChallenge_ImportsAuthenticationProperties()
{ {

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

@ -16,6 +16,7 @@ using Owin;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
using static OpenIddict.Server.OpenIddictServerEvents; using static OpenIddict.Server.OpenIddictServerEvents;
using static OpenIddict.Server.OpenIddictServerHandlers;
using static OpenIddict.Server.OpenIddictServerHandlers.Protection; using static OpenIddict.Server.OpenIddictServerHandlers.Protection;
namespace OpenIddict.Server.Owin.IntegrationTests; namespace OpenIddict.Server.Owin.IntegrationTests;
@ -128,6 +129,54 @@ public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerInte
Assert.Equal(new DateTimeOffset(2120, 01, 01, 00, 00, 00, TimeSpan.Zero), properties.ExpiresUtc); Assert.Equal(new DateTimeOffset(2120, 01, 01, 00, 00, 00, TimeSpan.Zero), properties.ExpiresUtc);
} }
[Fact]
public async Task ProcessAuthentication_CustomPropertiesAreAddedForErroredAuthenticationResults()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetAuthorizationEndpointUris("/authenticate/properties");
options.UseOwin()
.EnableErrorPassthrough()
.EnableAuthorizationEndpointPassthrough();
options.AddEventHandler<ProcessAuthenticationContext>(builder =>
{
builder.UseInlineHandler(context =>
{
context.RejectIdentityToken = true;
context.Properties["custom_property"] = "value";
return default;
});
builder.SetOrder(EvaluateValidatedTokens.Descriptor.Order + 1);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.PostAsync("/authenticate/properties", new OpenIddictRequest
{
ClientId = "Fabrikam",
IdTokenHint = "id_token_hint",
Nonce = "n-0S6_WzA2Mj",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = "id_token",
Scope = Scopes.OpenId
});
// Assert
var properties = new AuthenticationProperties(response.GetParameters()
.ToDictionary(parameter => parameter.Key, parameter => (string?) parameter.Value));
Assert.Equal("value", properties.Dictionary["custom_property"]);
}
[Fact] [Fact]
public async Task ProcessChallenge_ImportsAuthenticationProperties() public async Task ProcessChallenge_ImportsAuthenticationProperties()
{ {
@ -642,7 +691,7 @@ public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerInte
else if (context.Request.Path == new PathString("/authenticate")) else if (context.Request.Path == new PathString("/authenticate"))
{ {
var result = await context.Authentication.AuthenticateAsync(OpenIddictServerOwinDefaults.AuthenticationType); var result = await context.Authentication.AuthenticateAsync(OpenIddictServerOwinDefaults.AuthenticationType);
if (result?.Identity is null) if (result?.Identity is not { IsAuthenticated: true })
{ {
return; return;
} }

2
test/OpenIddict.Validation.Owin.IntegrationTests/OpenIddictValidationOwinIntegrationTests.cs

@ -162,7 +162,7 @@ public partial class OpenIddictValidationOwinIntegrationTests : OpenIddictValida
if (context.Request.Path == new PathString("/authenticate")) if (context.Request.Path == new PathString("/authenticate"))
{ {
var result = await context.Authentication.AuthenticateAsync(OpenIddictValidationOwinDefaults.AuthenticationType); var result = await context.Authentication.AuthenticateAsync(OpenIddictValidationOwinDefaults.AuthenticationType);
if (result?.Identity is null) if (result?.Identity is not { IsAuthenticated: true })
{ {
context.Authentication.Challenge(OpenIddictValidationOwinDefaults.AuthenticationType); context.Authentication.Challenge(OpenIddictValidationOwinDefaults.AuthenticationType);
return; return;

Loading…
Cancel
Save