diff --git a/src/OpenIddict.Core/OpenIddictProvider.cs b/src/OpenIddict.Core/OpenIddictProvider.cs index 20147283..ba01b51c 100644 --- a/src/OpenIddict.Core/OpenIddictProvider.cs +++ b/src/OpenIddict.Core/OpenIddictProvider.cs @@ -12,6 +12,7 @@ using System.Security.Claims; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Server; +using Microsoft.AspNet.Authentication; using Microsoft.AspNet.Identity; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Internal; @@ -217,6 +218,20 @@ namespace OpenIddict { return; } + + // 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 manager.GetUserNameAsync(user), + await manager.GetEmailAsync(user), + StringComparison.OrdinalIgnoreCase)) { + context.Reject( + error: OpenIdConnectConstants.Errors.InvalidRequest, + description: "The 'email' scope is required."); + + return; + } } } @@ -257,24 +272,22 @@ namespace OpenIddict { return; } - var identity = new ClaimsIdentity(context.Options.AuthenticationScheme); - identity.AddClaim(ClaimTypes.NameIdentifier, await manager.GetUserIdAsync(user), destination: "id_token token"); - - // Only add the name claim if the "profile" scope was present in the authorization request. // Note: filtering the username is not needed at this stage as OpenIddictController.Accept // and OpenIddictProvider.GrantResourceOwnerCredentials are expected to reject requests that // don't include the "email" scope if the username corresponds to the registed email address. - if (context.Request.ContainsScope(OpenIdConnectConstants.Scopes.Profile)) { - identity.AddClaim(ClaimTypes.Name, await manager.GetUserNameAsync(user), destination: "id_token token"); - } + var identity = await manager.CreateIdentityAsync(user, context.Request.GetScopes()); + Debug.Assert(identity != null); - // Only add the email address if the "email" scope was present in the authorization request. - if (context.Request.ContainsScope(OpenIdConnectConstants.Scopes.Email)) { - identity.AddClaim(ClaimTypes.Email, await manager.GetEmailAsync(user), destination: "id_token token"); - } + // Create a new authentication ticket holding the user identity. + var ticket = new AuthenticationTicket( + new ClaimsPrincipal(identity), + null, context.Options.AuthenticationScheme); + + ticket.SetResources(context.Request.GetResources()); + ticket.SetScopes(context.Request.GetScopes()); // Call SignInAsync to create and return a new OpenID Connect response containing the serialized code/tokens. - await context.HttpContext.Authentication.SignInAsync(context.Options.AuthenticationScheme, new ClaimsPrincipal(identity)); + await context.HttpContext.Authentication.SignInAsync(ticket.AuthenticationScheme, ticket.Principal, ticket.Properties); // Mark the response as handled // to skip the rest of the pipeline. @@ -378,7 +391,16 @@ namespace OpenIddict { identity.AddClaim(ClaimTypes.NameIdentifier, context.ClientId, destination: "id_token token"); identity.AddClaim(ClaimTypes.Name, await manager.GetDisplayNameAsync(application), destination: "id_token token"); - context.Validate(new ClaimsPrincipal(identity)); + // Create a new authentication ticket + // holding the application identity. + var ticket = new AuthenticationTicket( + new ClaimsPrincipal(identity), + null, context.Options.AuthenticationScheme); + + ticket.SetResources(context.Request.GetResources()); + ticket.SetScopes(context.Request.GetScopes()); + + context.Validate(ticket); } public override async Task GrantRefreshToken([NotNull] GrantRefreshTokenContext context) { @@ -461,8 +483,8 @@ 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.ContainsScope(OpenIdConnectConstants.Scopes.Profile) && - !context.Request.ContainsScope(OpenIdConnectConstants.Scopes.Email) && + if (context.Request.HasScope(OpenIdConnectConstants.Scopes.Profile) && + !context.Request.HasScope(OpenIdConnectConstants.Scopes.Email) && string.Equals(await manager.GetUserNameAsync(user), await manager.GetEmailAsync(user), StringComparison.OrdinalIgnoreCase)) { @@ -476,7 +498,15 @@ namespace OpenIddict { var identity = await manager.CreateIdentityAsync(user, context.Request.GetScopes()); Debug.Assert(identity != null); - context.Validate(new ClaimsPrincipal(identity)); + // Create a new authentication ticket holding the user identity. + var ticket = new AuthenticationTicket( + new ClaimsPrincipal(identity), + null, context.Options.AuthenticationScheme); + + ticket.SetResources(context.Request.GetResources()); + ticket.SetScopes(context.Request.GetScopes()); + + context.Validate(ticket); } } } \ No newline at end of file diff --git a/src/OpenIddict.Mvc/OpenIddictController.cs b/src/OpenIddict.Mvc/OpenIddictController.cs index b01def11..21689604 100644 --- a/src/OpenIddict.Mvc/OpenIddictController.cs +++ b/src/OpenIddict.Mvc/OpenIddictController.cs @@ -11,6 +11,7 @@ using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Extensions; +using Microsoft.AspNet.Authentication; using Microsoft.AspNet.Authorization; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http.Authentication; @@ -116,8 +117,8 @@ namespace OpenIddict.Mvc { // Return an error if the username corresponds to the registered // email address and if the "email" scope has not been requested. - if (request.ContainsScope(OpenIdConnectConstants.Scopes.Profile) && - !request.ContainsScope(OpenIdConnectConstants.Scopes.Email) && + if (request.HasScope(OpenIdConnectConstants.Scopes.Profile) && + !request.HasScope(OpenIdConnectConstants.Scopes.Email) && string.Equals(await Manager.GetUserNameAsync(user), await Manager.GetEmailAsync(user), StringComparison.OrdinalIgnoreCase)) { @@ -154,12 +155,17 @@ namespace OpenIddict.Mvc { identity.Actor.AddClaim(ClaimTypes.NameIdentifier, request.ClientId); identity.Actor.AddClaim(ClaimTypes.Name, await Manager.GetDisplayNameAsync(application), destination: "id_token token"); + // Create a new authentication ticket holding the user identity. + var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), null, Options.AuthenticationScheme); + ticket.SetResources(request.GetResources()); + ticket.SetScopes(request.GetScopes()); + // This call will instruct AspNet.Security.OpenIdConnect.Server to serialize // the specified identity to build appropriate tokens (id_token and token). // Note: you should always make sure the identities you return contain either // a 'sub' or a 'ClaimTypes.NameIdentifier' claim. In this case, the returned // identities always contain the name identifier returned by the external provider. - await HttpContext.Authentication.SignInAsync(Options.AuthenticationScheme, new ClaimsPrincipal(identity)); + await HttpContext.Authentication.SignInAsync(ticket.AuthenticationScheme, ticket.Principal, ticket.Properties); return new EmptyResult(); }