Browse Source

Remove the email/profile scopes checks

pull/234/head
Kévin Chalet 9 years ago
parent
commit
6c7ff09f01
  1. 19
      samples/Mvc.Server/Controllers/AuthorizationController.cs
  2. 33
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs
  3. 35
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs
  4. 15
      src/OpenIddict.Core/Managers/OpenIddictUserManager.cs

19
samples/Mvc.Server/Controllers/AuthorizationController.cs

@ -5,6 +5,7 @@
*/ */
using System; using System;
using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Extensions;
@ -78,8 +79,13 @@ namespace Mvc.Server {
new AuthenticationProperties(), new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme); OpenIdConnectServerDefaults.AuthenticationScheme);
ticket.SetResources(request.GetResources()); // Set the list of scopes granted to the client application.
ticket.SetScopes(request.GetScopes()); ticket.SetScopes(new[] {
/* openid: */ OpenIdConnectConstants.Scopes.OpenId,
/* email: */ OpenIdConnectConstants.Scopes.Email,
/* profile: */ OpenIdConnectConstants.Scopes.Profile,
/* offline_access: */ OpenIdConnectConstants.Scopes.OfflineAccess
}.Intersect(request.GetScopes()));
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme); return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
@ -176,8 +182,13 @@ namespace Mvc.Server {
new AuthenticationProperties(), new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme); OpenIdConnectServerDefaults.AuthenticationScheme);
ticket.SetResources(request.GetResources()); // Set the list of scopes granted to the client application.
ticket.SetScopes(request.GetScopes()); ticket.SetScopes(new[] {
/* openid: */ OpenIdConnectConstants.Scopes.OpenId,
/* email: */ OpenIdConnectConstants.Scopes.Email,
/* profile: */ OpenIdConnectConstants.Scopes.Profile,
/* offline_access: */ OpenIdConnectConstants.Scopes.OfflineAccess
}.Intersect(request.GetScopes()));
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme); return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
} }

33
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs

@ -255,39 +255,6 @@ namespace OpenIddict.Infrastructure {
return; return;
} }
// Ensure that the appropriate set of scopes is requested to prevent personal data leakage when possible.
if (services.Users.SupportsUserEmail && context.HttpContext.User.Identities.Any(identity => identity.IsAuthenticated) &&
context.Request.HasScope(OpenIdConnectConstants.Scopes.Profile) &&
!context.Request.HasScope(OpenIdConnectConstants.Scopes.Email)) {
// Skip scope validation if the user cannot be found in the database.
var user = await services.Users.GetUserAsync(context.HttpContext.User);
if (user == null) {
services.Logger.LogWarning("The authorization request was not fully validated because the profile corresponding " +
"to the logged in user was not found in the database: {Identifier}.",
context.HttpContext.User.GetClaim(ClaimTypes.NameIdentifier));
}
else {
// Retrieve the username and the email address associated with the user.
var username = await services.Users.GetUserNameAsync(user);
var email = await services.Users.GetEmailAsync(user);
// Return an error if the username corresponds to the registered
// email address and if the "email" scope has not been requested.
if (!string.IsNullOrEmpty(email) && string.Equals(username, email, StringComparison.OrdinalIgnoreCase)) {
services.Logger.LogError("The authorization request was rejected because the 'email' scope was not requested: " +
"to prevent data leakage, the 'email' scope must be granted when the username " +
"is identical to the email address associated with the user profile.");
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "The 'email' scope is required.");
return;
}
}
}
// Run additional checks for prompt=none requests. // Run additional checks for prompt=none requests.
if (string.Equals(context.Request.Prompt, "none", StringComparison.Ordinal)) { if (string.Equals(context.Request.Prompt, "none", StringComparison.Ordinal)) {
// If the user is not authenticated, return an error to the client application. // If the user is not authenticated, return an error to the client application.

35
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs

@ -13,7 +13,6 @@ using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -290,40 +289,6 @@ namespace OpenIddict.Infrastructure {
return; return;
} }
else if (context.Request.IsPasswordGrantType()) {
// Note: at this stage, the client credentials cannot be null as the OpenID Connect server middleware
// automatically rejects grant_type=password requests that don't specify a username/password couple.
Debug.Assert(!string.IsNullOrEmpty(context.Request.Username) &&
!string.IsNullOrEmpty(context.Request.Password), "The user credentials shouldn't be null.");
var user = await services.Users.FindByNameAsync(context.Request.Username);
if (user == null) {
services.Logger.LogWarning("The token request was not fully validated because the profile corresponding to the " +
"given username was not found in the database: {Username}.", context.Request.Username);
}
// Return an error if the username corresponds to the registered
// email address and if the "email" scope has not been requested.
else if (services.Users.SupportsUserEmail && context.Request.HasScope(OpenIdConnectConstants.Scopes.Profile) &&
!context.Request.HasScope(OpenIdConnectConstants.Scopes.Email)) {
// Retrieve the username and the email address associated with the user.
var username = await services.Users.GetUserNameAsync(user);
var email = await services.Users.GetEmailAsync(user);
if (!string.IsNullOrEmpty(email) && string.Equals(username, email, StringComparison.OrdinalIgnoreCase)) {
services.Logger.LogError("The token request was rejected because the 'email' scope was not requested: " +
"to prevent data leakage, the 'email' scope must be granted when the username " +
"is identical to the email address associated with the user profile.");
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "The 'email' scope is required.");
return;
}
}
}
// Invoke the rest of the pipeline to allow // Invoke the rest of the pipeline to allow
// the user code to handle the token request. // the user code to handle the token request.
context.SkipToNextMiddleware(); context.SkipToNextMiddleware();

15
src/OpenIddict.Core/Managers/OpenIddictUserManager.cs

@ -90,28 +90,19 @@ namespace OpenIddict {
// access tokens, even if an explicit destination is not specified. // access tokens, even if an explicit destination is not specified.
identity.AddClaim(ClaimTypes.NameIdentifier, await GetUserIdAsync(user)); identity.AddClaim(ClaimTypes.NameIdentifier, await GetUserIdAsync(user));
// Resolve the email address associated with the user if the underlying store supports it.
var email = SupportsUserEmail ? await GetEmailAsync(user) : null;
// Only add the name claim if the "profile" scope was granted. // Only add the name claim if the "profile" scope was granted.
if (scopes.Contains(OpenIdConnectConstants.Scopes.Profile)) { if (scopes.Contains(OpenIdConnectConstants.Scopes.Profile)) {
var username = await GetUserNameAsync(user); var username = await GetUserNameAsync(user);
// Throw an exception if the username corresponds to the registered
// email address and if the "email" scope has not been requested.
if (!scopes.Contains(OpenIdConnectConstants.Scopes.Email) &&
!string.IsNullOrEmpty(email) &&
string.Equals(username, email, StringComparison.OrdinalIgnoreCase)) {
throw new InvalidOperationException("The 'email' scope is required.");
}
identity.AddClaim(ClaimTypes.Name, username, identity.AddClaim(ClaimTypes.Name, username,
OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken); OpenIdConnectConstants.Destinations.IdentityToken);
} }
// Only add the email address if the "email" scope was granted. // Only add the email address if the "email" scope was granted.
if (!string.IsNullOrEmpty(email) && scopes.Contains(OpenIdConnectConstants.Scopes.Email)) { if (SupportsUserEmail && scopes.Contains(OpenIdConnectConstants.Scopes.Email)) {
var email = await GetEmailAsync(user);
identity.AddClaim(ClaimTypes.Email, email, identity.AddClaim(ClaimTypes.Email, email,
OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken); OpenIdConnectConstants.Destinations.IdentityToken);

Loading…
Cancel
Save