diff --git a/src/OpenIddict.Core/OpenIddictManager.cs b/src/OpenIddict.Core/OpenIddictManager.cs index d7a60e31..e090d7a2 100644 --- a/src/OpenIddict.Core/OpenIddictManager.cs +++ b/src/OpenIddict.Core/OpenIddictManager.cs @@ -68,15 +68,17 @@ namespace OpenIddict { // access tokens, even if an explicit destination is not specified. identity.AddClaim(ClaimTypes.NameIdentifier, await Services.Users.GetUserIdAsync(user)); - // Resolve the username and the email address associated with the user. - var username = await Services.Users.GetUserNameAsync(user); - var email = await Services.Users.GetEmailAsync(user); + // Resolve the email address associated with the user if the underlying store supports it. + var email = Services.Users.SupportsUserEmail ? await Services.Users.GetEmailAsync(user) : null; // Only add the name claim if the "profile" scope was granted. if (scopes.Contains(OpenIdConnectConstants.Scopes.Profile)) { + var username = await Services.Users.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."); } @@ -87,7 +89,7 @@ namespace OpenIddict { } // Only add the email address if the "email" scope was granted. - if (scopes.Contains(OpenIdConnectConstants.Scopes.Email)) { + if (!string.IsNullOrEmpty(email) && scopes.Contains(OpenIdConnectConstants.Scopes.Email)) { identity.AddClaim(ClaimTypes.Email, email, OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken); @@ -96,8 +98,8 @@ namespace OpenIddict { if (Services.Users.SupportsUserRole && scopes.Contains(OpenIddictConstants.Scopes.Roles)) { foreach (var role in await Services.Users.GetRolesAsync(user)) { identity.AddClaim(identity.RoleClaimType, role, - OpenIdConnectConstants.Destinations.AccessToken, - OpenIdConnectConstants.Destinations.IdentityToken); + OpenIdConnectConstants.Destinations.AccessToken, + OpenIdConnectConstants.Destinations.IdentityToken); } } diff --git a/src/OpenIddict.Core/OpenIddictProvider.Authentication.cs b/src/OpenIddict.Core/OpenIddictProvider.Authentication.cs index acee2c1d..d188b1ad 100644 --- a/src/OpenIddict.Core/OpenIddictProvider.Authentication.cs +++ b/src/OpenIddict.Core/OpenIddictProvider.Authentication.cs @@ -79,16 +79,19 @@ namespace OpenIddict { // Return an error if the username corresponds to the registered // email address and if the "email" scope has not been requested. - if (context.Request.HasScope(OpenIdConnectConstants.Scopes.Profile) && - !context.Request.HasScope(OpenIdConnectConstants.Scopes.Email) && - string.Equals(await services.Users.GetUserNameAsync(user), - await services.Users.GetEmailAsync(user), - StringComparison.OrdinalIgnoreCase)) { - context.Reject( - error: OpenIdConnectConstants.Errors.InvalidRequest, - description: "The 'email' scope is required."); - - return; + 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)) { + context.Reject( + error: OpenIdConnectConstants.Errors.InvalidRequest, + description: "The 'email' scope is required."); + + return; + } } } diff --git a/src/OpenIddict.Core/OpenIddictProvider.Exchange.cs b/src/OpenIddict.Core/OpenIddictProvider.Exchange.cs index 236830ce..8db5512e 100644 --- a/src/OpenIddict.Core/OpenIddictProvider.Exchange.cs +++ b/src/OpenIddict.Core/OpenIddictProvider.Exchange.cs @@ -234,16 +234,19 @@ namespace OpenIddict { // Return an error if the username corresponds to the registered // email address and if the "email" scope has not been requested. - if (context.Request.HasScope(OpenIdConnectConstants.Scopes.Profile) && - !context.Request.HasScope(OpenIdConnectConstants.Scopes.Email) && - string.Equals(await services.Users.GetUserNameAsync(user), - await services.Users.GetEmailAsync(user), - StringComparison.OrdinalIgnoreCase)) { - context.Reject( - error: OpenIdConnectConstants.Errors.InvalidRequest, - description: "The 'email' scope is required."); + 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); - return; + if (!string.IsNullOrEmpty(email) && string.Equals(username, email, StringComparison.OrdinalIgnoreCase)) { + context.Reject( + error: OpenIdConnectConstants.Errors.InvalidRequest, + description: "The 'email' scope is required."); + + return; + } } var identity = await services.Applications.CreateIdentityAsync(user, context.Request.GetScopes()); diff --git a/src/OpenIddict.Core/OpenIddictProvider.Userinfo.cs b/src/OpenIddict.Core/OpenIddictProvider.Userinfo.cs index cc1ae11f..3862727e 100644 --- a/src/OpenIddict.Core/OpenIddictProvider.Userinfo.cs +++ b/src/OpenIddict.Core/OpenIddictProvider.Userinfo.cs @@ -50,7 +50,7 @@ namespace OpenIddict { } // Only add the email address details if the "email" scope was present in the access token. - if (context.Ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) { + if (services.Users.SupportsUserEmail && context.Ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) { context.Email = await services.Users.GetEmailAsync(user); // Only add the "email_verified" claim @@ -61,7 +61,8 @@ namespace OpenIddict { }; // Only add the phone number details if the "phone" scope was present in the access token. - if (context.Ticket.HasScope(OpenIdConnectConstants.Scopes.Phone)) { + if (services.Users.SupportsUserPhoneNumber && + context.Ticket.HasScope(OpenIdConnectConstants.Scopes.Phone)) { context.PhoneNumber = await services.Users.GetPhoneNumberAsync(user); // Only add the "phone_number_verified"