7 changed files with 115 additions and 319 deletions
@ -1,180 +0,0 @@ |
|||
using System.Diagnostics; |
|||
using System.Linq; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using AspNet.Security.OpenIdConnect.Extensions; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.AspNetCore.Authentication; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.AspNetCore.Http.Authentication; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace OpenIddict.Infrastructure { |
|||
public class OpenIddictMiddleware<TUser, TApplication, TAuthorization, TScope, TToken> |
|||
where TUser : class where TApplication : class |
|||
where TAuthorization : class where TScope : class where TToken : class { |
|||
private readonly RequestDelegate next; |
|||
|
|||
public OpenIddictMiddleware([NotNull] RequestDelegate next) { |
|||
this.next = next; |
|||
} |
|||
|
|||
public async Task Invoke([NotNull] HttpContext context) { |
|||
// Invoke the rest of the pipeline to allow handling
|
|||
// authorization, logout or token requests in user code.
|
|||
await next(context); |
|||
|
|||
// If the request was already handled, skip the default logic.
|
|||
if (context.Response.HasStarted || context.Response.StatusCode != 404) { |
|||
return; |
|||
} |
|||
|
|||
// If the request doesn't correspond to an OpenID Connect request, ignore it.
|
|||
var request = context.GetOpenIdConnectRequest(); |
|||
if (request == null || (!request.IsAuthorizationRequest() && |
|||
!request.IsLogoutRequest() && |
|||
!request.IsTokenRequest())) { |
|||
return; |
|||
} |
|||
|
|||
// If an OpenID Connect response was already prepared, bypass the default logic.
|
|||
var response = context.GetOpenIdConnectResponse(); |
|||
if (response != null) { |
|||
return; |
|||
} |
|||
|
|||
// Resolve the OpenIddict services from the scoped container.
|
|||
var services = context.RequestServices.GetRequiredService<OpenIddictServices< |
|||
TUser, TApplication, TAuthorization, TScope, TToken>>(); |
|||
|
|||
// Reset the response status code to allow the OpenID Connect server
|
|||
// middleware to apply a challenge, signin or logout response.
|
|||
context.Response.StatusCode = 200; |
|||
|
|||
ClaimsPrincipal principal = null; |
|||
|
|||
if (request.IsAuthorizationRequest()) { |
|||
// If the user is not logged in, return a challenge response.
|
|||
if (!context.User.Identities.Any(identity => identity.IsAuthenticated)) { |
|||
await context.Authentication.ChallengeAsync(); |
|||
|
|||
return; |
|||
} |
|||
|
|||
// Retrieve the profile of the logged in user. If the user
|
|||
// cannot be found, return a challenge response.
|
|||
var user = await services.Users.GetUserAsync(context.User); |
|||
if (user == null) { |
|||
await context.Authentication.ChallengeAsync(); |
|||
|
|||
return; |
|||
} |
|||
|
|||
services.Logger.LogInformation("The authorization request was handled without asking for user consent."); |
|||
|
|||
principal = new ClaimsPrincipal(await services.Users.CreateIdentityAsync(user, request.GetScopes())); |
|||
} |
|||
|
|||
else if (request.IsLogoutRequest()) { |
|||
// Ask ASP.NET Core Identity to delete the local and external cookies created
|
|||
// when the user agent is redirected from the external identity provider
|
|||
// after a successful authentication flow (e.g Google or Facebook).
|
|||
await services.SignIn.SignOutAsync(); |
|||
|
|||
await context.Authentication.SignOutAsync(services.Options.AuthenticationScheme); |
|||
|
|||
services.Logger.LogInformation("The logout request was handled without asking for user consent."); |
|||
|
|||
return; |
|||
} |
|||
|
|||
else if (request.IsTokenRequest()) { |
|||
Debug.Assert(request.IsClientCredentialsGrantType() || request.IsPasswordGrantType(), |
|||
"Only grant_type=client_credentials and grant_type=password requests should be handled here."); |
|||
|
|||
services.Logger.LogInformation("The token request was automatically handled."); |
|||
|
|||
if (request.IsClientCredentialsGrantType()) { |
|||
// Retrieve the application details corresponding to the requested client_id.
|
|||
// Note: this call shouldn't return a null instance, but a race condition may occur
|
|||
// if the application was removed after the initial check made by ValidateTokenRequest.
|
|||
var application = await services.Applications.FindByClientIdAsync(request.ClientId); |
|||
if (application == null) { |
|||
services.Logger.LogError("The token request was aborted because the client application " + |
|||
"was not found in the database: '{ClientId}'.", request.ClientId); |
|||
|
|||
await context.Authentication.ForbidAsync(services.Options.AuthenticationScheme); |
|||
|
|||
return; |
|||
} |
|||
|
|||
var identity = new ClaimsIdentity(services.Options.AuthenticationScheme); |
|||
|
|||
// Note: the name identifier is always included in both identity and
|
|||
// access tokens, even if an explicit destination is not specified.
|
|||
identity.AddClaim(ClaimTypes.NameIdentifier, request.ClientId); |
|||
|
|||
identity.AddClaim(ClaimTypes.Name, await services.Applications.GetDisplayNameAsync(application), |
|||
OpenIdConnectConstants.Destinations.AccessToken, |
|||
OpenIdConnectConstants.Destinations.IdentityToken); |
|||
|
|||
principal = new ClaimsPrincipal(identity); |
|||
} |
|||
|
|||
else if (request.IsPasswordGrantType()) { |
|||
// Retrieve the user profile corresponding to the specified username.
|
|||
var user = await services.Users.FindByNameAsync(request.Username); |
|||
if (user == null) { |
|||
services.Logger.LogError("The token request was rejected because no user profile corresponding to " + |
|||
"the specified username was found: '{Username}'.", request.Username); |
|||
|
|||
await context.Authentication.ForbidAsync(services.Options.AuthenticationScheme); |
|||
|
|||
return; |
|||
} |
|||
|
|||
// Ensure the username/password couple is valid.
|
|||
if (!await services.Users.CheckPasswordAsync(user, request.Password)) { |
|||
services.Logger.LogError("The token request was rejected because the password didn't match " + |
|||
"the password associated with the account '{Username}'.", request.Username); |
|||
|
|||
if (services.Users.SupportsUserLockout) { |
|||
await services.Users.AccessFailedAsync(user); |
|||
|
|||
if (await services.Users.IsLockedOutAsync(user)) { |
|||
services.Logger.LogError("The token request was rejected because the account '{Username}' " + |
|||
"was locked out to prevent brute force attacks.", request.Username); |
|||
} |
|||
} |
|||
|
|||
await context.Authentication.ForbidAsync(services.Options.AuthenticationScheme); |
|||
|
|||
return; |
|||
} |
|||
|
|||
if (services.Users.SupportsUserLockout) { |
|||
await services.Users.ResetAccessFailedCountAsync(user); |
|||
} |
|||
|
|||
principal = new ClaimsPrincipal(await services.Users.CreateIdentityAsync(user, request.GetScopes())); |
|||
} |
|||
} |
|||
|
|||
// At this stage, don't alter the response
|
|||
// if a sign-in operation can't be performed.
|
|||
if (principal != null) { |
|||
// Create a new authentication ticket holding the user identity.
|
|||
var ticket = new AuthenticationTicket( |
|||
principal, new AuthenticationProperties(), |
|||
services.Options.AuthenticationScheme); |
|||
|
|||
ticket.SetResources(request.GetResources()); |
|||
ticket.SetScopes(request.GetScopes()); |
|||
|
|||
await context.Authentication.SignInAsync(ticket.AuthenticationScheme, ticket.Principal, ticket.Properties); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,44 +0,0 @@ |
|||
/* |
|||
* 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 AspNet.Security.OpenIdConnect.Server; |
|||
|
|||
namespace OpenIddict { |
|||
/// <summary>
|
|||
/// Exposes the default values used by OpenIddict.
|
|||
/// </summary>
|
|||
public static class OpenIddictDefaults { |
|||
/// <summary>
|
|||
/// Default value for <see cref="OpenIdConnectServerOptions.AuthorizationEndpointPath"/>.
|
|||
/// </summary>
|
|||
public const string AuthorizationEndpointPath = "/connect/authorize"; |
|||
|
|||
/// <summary>
|
|||
/// Default value for <see cref="OpenIdConnectServerOptions.IntrospectionEndpointPath"/>.
|
|||
/// </summary>
|
|||
public const string IntrospectionEndpointPath = "/connect/introspect"; |
|||
|
|||
/// <summary>
|
|||
/// Default value for <see cref="OpenIdConnectServerOptions.LogoutEndpointPath"/>.
|
|||
/// </summary>
|
|||
public const string LogoutEndpointPath = "/connect/logout"; |
|||
|
|||
/// <summary>
|
|||
/// Default value for <see cref="OpenIdConnectServerOptions.RevocationEndpointPath"/>.
|
|||
/// </summary>
|
|||
public const string RevocationEndpointPath = "/connect/revoke"; |
|||
|
|||
/// <summary>
|
|||
/// Default value for <see cref="OpenIdConnectServerOptions.TokenEndpointPath"/>.
|
|||
/// </summary>
|
|||
public const string TokenEndpointPath = "/connect/token"; |
|||
|
|||
/// <summary>
|
|||
/// Default value for <see cref="OpenIdConnectServerOptions.UserinfoEndpointPath"/>.
|
|||
/// </summary>
|
|||
public const string UserinfoEndpointPath = "/connect/userinfo"; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue