private IEnumerable <string> GetDestinations(Claim claim, AuthenticationTicket ticket) { // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. switch (claim.Type) { case OpenIdConnectConstants.Claims.Name: yield return(OpenIdConnectConstants.Destinations.AccessToken); if (ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) { yield return(OpenIdConnectConstants.Destinations.IdentityToken); } yield break; case OpenIdConnectConstants.Claims.Email: yield return(OpenIdConnectConstants.Destinations.AccessToken); if (ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) { yield return(OpenIdConnectConstants.Destinations.IdentityToken); } yield break; case OpenIdConnectConstants.Claims.Role: yield return(OpenIdConnectConstants.Destinations.AccessToken); if (ticket.HasScope(OpenIddictConstants.Claims.Roles)) { yield return(OpenIdConnectConstants.Destinations.IdentityToken); } yield break; // Never include the security stamp in the access and identity tokens, as it's a secret value. case "AspNet.Identity.SecurityStamp": yield break; default: yield return(OpenIdConnectConstants.Destinations.AccessToken); yield break; } }
private async Task <AuthenticationTicket> CreateTicketAsync( OpenIdConnectRequest request, VanguardUser user, AuthenticationProperties properties = null) { var principal = await _signInManager.CreateUserPrincipalAsync(user); var ticket = new AuthenticationTicket(principal, properties, OpenIddictServerDefaults.AuthenticationScheme); if (!request.IsRefreshTokenGrantType()) { ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles }.Intersect(request.GetScopes())); } ticket.SetResources("vanguard-identity-management"); foreach (var claim in ticket.Principal.Claims) { if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; if (claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile) || claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email) || claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles)) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(ticket); }
private async Task <AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, ApplicationUser user, AuthenticationProperties properties = null) { var principal = await _signInManager.CreateUserPrincipalAsync(user); var ticket = new AuthenticationTicket(principal, properties, OpenIdConnectServerDefaults.AuthenticationScheme); if (request.IsPasswordGrantType()) { ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles, }.Intersect(request.GetScopes())); } ticket.SetResources(_appConfig.Jwt.Audiences); foreach (Claim claim in ticket.Principal.Claims.Where(x => x.Type != _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType)) { List <string> destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; if (claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile) || claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email) || claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles)) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } ticket.SetAccessTokenLifetime(TimeSpan.FromHours(5)); return(ticket); }
public void HasScope_ReturnsAppropriateResult(string scope, bool result) { // Arrange var ticket = new AuthenticationTicket( new ClaimsIdentity(), new AuthenticationProperties()); ticket.Properties.Dictionary[OpenIdConnectConstants.Properties.Scopes] = scope; // Act and assert Assert.Equal(result, ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId)); }
public void HasScope_ReturnsExpectedResult(string scope, bool result) { // Arrange var ticket = new AuthenticationTicket( new ClaimsPrincipal(), new AuthenticationProperties(), nameof(AuthenticationTicket)); ticket.Properties.Items[OpenIdConnectConstants.Properties.Scopes] = scope; // Act and assert Assert.Equal(result, ticket.HasScope()); }
public async Task <AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, ApplicationUser user) { var principal = await _signInManager.CreateUserPrincipalAsync(user); var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), OpenIddictServerDefaults.AuthenticationScheme); var scopes = request.GetScopes().ToImmutableArray(); ticket.SetScopes(scopes); ticket.SetResources(await _scopeManager.ListResourcesAsync(scopes)); foreach (var claim in ticket.Principal.Claims) { if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; if ((claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) || (claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) || (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles))) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(ticket); }
private async Task <AuthenticationTicket> CreateTicketAsync( OpenIdConnectRequest oidcRequest, ApplicationUser user, AuthenticationProperties properties = null) { var principal = await signInManager.CreateUserPrincipalAsync(user); var ticket = new AuthenticationTicket(principal, properties, OpenIddictServerDefaults.AuthenticationScheme); if (!oidcRequest.IsAuthorizationCodeGrantType()) { ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Profile }.Intersect(oidcRequest.GetScopes())); } ticket.SetResources("resource_server"); foreach (var claim in ticket.Principal.Claims) { if (claim.Type == identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; if (claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(ticket); }
public static AuthenticationTicket CreateAuthenticationTicket(this ClaimsPrincipal principal, string securityStampClaimType) { // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), OpenIdConnectServerDefaults.AuthenticationScheme); // Set the list of scopes granted to the client application. ticket.SetScopes(OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIddictConstants.Scopes.Roles);//.Intersect(request.GetScopes())); ticket.SetResources(ApplicationInfo.AppName); // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in from c in ticket.Principal.Claims // Never include the security stamp in the access and identity tokens, as it's a secret value. where c.Type != securityStampClaimType select c) { var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; if (IdentityClaimScopes.TryGetValue(claim.Type, out string scope) && ticket.HasScope(scope)) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(ticket); }
/// <summary> /// Creates a ticket for an OpenId connect request /// </summary> /// <param name="request">The OpenId connect request</param> /// <param name="user">The user to provide a ticket for</param> /// <returns>The OpenId ticket</returns> private async Task <AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, User user) { // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await CreateUserPrincipal(user, OpenIdConnectConstants.Schemes.Bearer); // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), OpenIddictServerDefaults.AuthenticationScheme); // The scopes that are provided to every request var defaultScopes = new List <string> { OpenIdConnectConstants.Scopes.OpenId, OpenIddictConstants.Scopes.Roles, }; // These are additional scopes that the client can also explicitly request var allowedScopes = new List <string> { OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, }; ticket.SetScopes(defaultScopes.Concat(allowedScopes.Intersect(request.GetScopes()))); ticket.SetResources("resource-server"); ticket.SetAccessTokenLifetime(new TimeSpan(7, 0, 0, 0)); // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in ticket.Principal.Claims) { // Never include the security stamp in the access and identity tokens, as it's a secret value. if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if ((claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) || (claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) || (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles))) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(ticket); }
public async Task <IActionResult> Index(LoginModel model) { var user = await _userManager.FindByNameAsync(model.loginData.Username); if (user == null) { return(Unauthorized()); } var result = await _signInManager.CheckPasswordSignInAsync(user, model.loginData.Password, true); if (!result.Succeeded) { return(Unauthorized()); } var app = await _applicationManager.FindByClientIdAsync(model.client_id); if (user.Applications.All(x => x != app.Id)) { return(Unauthorized()); } var principal = await _signInManager.CreateUserPrincipalAsync(user); var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), OpenIdConnectServerDefaults.AuthenticationScheme); ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.AuthorizationCode); var scope = model.scope.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries); if (scope.Contains(OpenIdConnectConstants.Scopes.Profile)) { ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles }.Intersect(scope)); } foreach (var claim in ticket.Principal.Claims.Where(x => x.Type != _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType)) { var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; if (claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile) || claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles)) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); }
private async Task <AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, ApplicationUser user) { // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await _signInManager.CreateUserPrincipalAsync(user); var identity = (ClaimsIdentity)principal.Identity; if (!string.IsNullOrEmpty(user.MobileNumber)) { var mobileNumberClaim = new Claim("mobileNumber", user.MobileNumber?.ToString() ?? "", ClaimValueTypes.String); mobileNumberClaim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken); identity.AddClaim(mobileNumberClaim); } // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), OpenIdConnectServerDefaults.AuthenticationScheme); // Set the list of scopes granted to the client application. ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIddictConstants.Scopes.Roles }.Intersect(request.GetScopes())); ticket.SetResources(_configuration["NetSo:Audience"]); // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in ticket.Principal.Claims) { // Never include the security stamp in the access and identity tokens, as it's a secret value. if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if ((claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) || (claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) || (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles))) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(ticket); }
private async Task <AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest oidcRequest, User user, AuthenticationProperties properties = null) { var principal = await _signInManager.CreateUserPrincipalAsync(user); var identity = (ClaimsIdentity)principal.Identity; AddUserInformationClaims(user, identity); var ticket = new AuthenticationTicket(principal, properties, OpenIddictServerDefaults.AuthenticationScheme); if (!oidcRequest.IsRefreshTokenGrantType()) { // Set the list of scopes granted to the client application. // Note: the offline_access scope must be granted // to allow OpenIddict to return a refresh token. ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles, }.Intersect(oidcRequest.GetScopes())); } ticket.SetResources("resource_server"); // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; foreach (var claim in ticket.Principal.Claims) { // Never include the security stamp in the access and identity tokens, as it's a secret value. if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if ((claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) || (claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) || (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles)) || (claim.Type == OpenIdConnectConstants.Claims.Audience && ticket.HasScope(OpenIddictConstants.Claims.Audience)) ) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } //var name = new Claim(OpenIdConnectConstants.Claims.GivenName, user.FullName ?? "[NA]"); //name.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken); //identity.AddClaim(name); return(ticket); }
//TODO: this is an example private async Task <AuthenticationTicket> CreateTicketAsync( OpenIdConnectRequest request, AuthenticateResult authResult = null) { AuthenticationProperties properties = authResult.Properties; var userId = authResult.Principal.Claims.FirstOrDefault(_ => _.Type == OpenIdConnectConstants.Claims.Subject)?.Value; if (userId == null) { return(null); } // string userPrincipalName = _sessionStateService.Get<string>("UserPrincipalName"); SimpleClaim simpleClaim = new SimpleClaim() { ObjectIdentifier = userId, UserPrincipalName = "SomeUPN", GivenName = "Joe", Surname = "Citizen" }; // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. //var principal = await _signInManager.CreateUserPrincipalAsync(user); var identity = new ClaimsIdentity( OpenIdConnectServerDefaults.AuthenticationScheme, OpenIdConnectConstants.Claims.Name, OpenIdConnectConstants.Claims.Role); // Add a "sub" claim containing the user identifier, and attach // the "access_token" destination to allow OpenIddict to store it // in the access token, so it can be retrieved from your controllers. identity.AddClaim(OpenIdConnectConstants.Claims.Subject, simpleClaim.ObjectIdentifier, OpenIdConnectConstants.Destinations.IdentityToken); identity.AddClaim(OpenIdConnectConstants.Claims.Name, simpleClaim.GivenName, OpenIdConnectConstants.Destinations.IdentityToken); identity.AddClaim(OpenIdConnectConstants.Claims.GivenName, simpleClaim.GivenName, OpenIdConnectConstants.Destinations.IdentityToken); identity.AddClaim(OpenIdConnectConstants.Claims.FamilyName, simpleClaim.Surname, OpenIdConnectConstants.Destinations.IdentityToken); identity.AddClaim(ClaimTypes.Upn, simpleClaim.UserPrincipalName, OpenIdConnectConstants.Destinations.IdentityToken); // ... add other claims, if necessary. var principal = new ClaimsPrincipal(identity); // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, properties, OpenIdConnectServerDefaults.AuthenticationScheme); if (!request.IsAuthorizationCodeGrantType()) { // Set the list of scopes granted to the client application. // Note: the offline_access scope must be granted // to allow OpenIddict to return a refresh token. ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles }.Intersect(request.GetScopes())); } ticket.SetResources("resource_server"); // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in ticket.Principal.Claims) { // Never include the security stamp in the access and identity tokens, as it's a secret value. if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if ((claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) || (claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) || (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles))) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(ticket); }
private async Task <AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, ApplicationUser user, AuthenticationProperties properties = null, string token = "") { // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await _signInManager.CreateUserPrincipalAsync(user); // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, properties, OpenIdConnectServerDefaults.AuthenticationScheme); if (!request.IsRefreshTokenGrantType()) { // Set the list of scopes granted to the client application. // Note: the offline_access scope must be granted // to allow OpenIddict to return a refresh token. ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, // "name" OpenIdConnectConstants.Scopes.OfflineAccess, OpenIdConnectConstants.Scopes.Phone, OpenIddictConstants.Scopes.Roles, //"Read", //"Balance", //"UserId", //"Address", }.Intersect(request.GetScopes())); } if (!string.IsNullOrEmpty(token))///send the redisstorage token to frant using Resources parameter. { ticket.SetResources(token); } else { ticket.SetResources("resource_server"); } // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in ticket.Principal.Claims) { // Never include the security stamp in the access and identity tokens, as it's a secret value. if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if ((claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) || (claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) || (claim.Type == OpenIdConnectConstants.Claims.PhoneNumber && ticket.HasScope(OpenIdConnectConstants.Scopes.Phone)) || (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles))) // || //(claim.Type == "Read" && ticket.HasScope("Read")) || (claim.Type == "Balance" && ticket.HasScope("Balance"))) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(ticket); }
private async Task <AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, ApplicationUser user, Cliente cliente = null, AuthenticationProperties properties = null) { // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await _signInManager.CreateUserPrincipalAsync(user); var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); //identity.AddClaims(principal.Claims); if (cliente != null) { Claim querOferecer = new Claim(Lust.Infra.CrossCutting.Identity.Primitives.Claims.QuerOferecer, cliente.EstaOferecendo.ToString().ToLower()); Claim querDesfrutar = new Claim(Lust.Infra.CrossCutting.Identity.Primitives.Claims.QuerDesfrutar, cliente.EstaDesfrutando.ToString().ToLower()); identity.AddClaim(querOferecer); identity.AddClaim(querDesfrutar); } principal.AddIdentity(identity); // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, properties, OpenIdConnectServerDefaults.AuthenticationScheme); if (!request.IsRefreshTokenGrantType()) { // Set the list of scopes granted to the client application. // Note: the offline_access scope must be granted // to allow OpenIddict to return a refresh token. ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles }.Intersect(request.GetScopes())); } ticket.SetResources("resource_server"); // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in ticket.Principal.Claims) { // Never include the security stamp in the access and identity tokens, as it's a secret value. if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if ((claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) || (claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) || (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles)) || (claim.Type == Lust.Infra.CrossCutting.Identity.Primitives.Claims.QuerOferecer) || (claim.Type == Lust.Infra.CrossCutting.Identity.Primitives.Claims.QuerDesfrutar)) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(ticket); }
private async Task <bool> HandleSignInAsync(AuthenticationTicket ticket) { // Extract the OpenID Connect request from the OWIN context. // If it cannot be found or doesn't correspond to an authorization // or a token request, throw an InvalidOperationException. var request = Context.GetOpenIdConnectRequest(); if (request == null || (!request.IsAuthorizationRequest() && !request.IsTokenRequest())) { throw new InvalidOperationException("An authorization or token response cannot be returned from this endpoint."); } // Note: if a response was already generated, throw an exception. var response = Context.GetOpenIdConnectResponse(); if (response != null) { throw new InvalidOperationException("A response has already been sent."); } if (string.IsNullOrEmpty(ticket.Identity.GetClaim(OpenIdConnectConstants.Claims.Subject))) { throw new InvalidOperationException("The authentication ticket was rejected because " + "the mandatory subject claim was missing."); } Logger.LogTrace("A sign-in operation was triggered: {Claims} ; {Properties}.", ticket.Identity.Claims, ticket.Properties.Dictionary); // Prepare a new OpenID Connect response. response = new OpenIdConnectResponse(); // Copy the confidentiality level associated with the request to the authentication ticket. if (!ticket.HasProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel)) { ticket.SetConfidentialityLevel(request.GetProperty <string>(OpenIdConnectConstants.Properties.ConfidentialityLevel)); } // Always include the "openid" scope when the developer doesn't explicitly call SetScopes. // Note: the application is allowed to specify a different "scopes": in this case, // don't replace the "scopes" property stored in the authentication ticket. if (request.HasScope(OpenIdConnectConstants.Scopes.OpenId) && !ticket.HasScope()) { ticket.SetScopes(OpenIdConnectConstants.Scopes.OpenId); } // When a "resources" property cannot be found in the ticket, // infer it from the "audiences" property. if (ticket.HasAudience() && !ticket.HasResource()) { ticket.SetResources(ticket.GetAudiences()); } // Add the validated client_id to the list of authorized presenters, // unless the presenters were explicitly set by the developer. var presenter = request.GetProperty <string>(OpenIdConnectConstants.Properties.ValidatedClientId); if (!string.IsNullOrEmpty(presenter) && !ticket.HasPresenter()) { ticket.SetPresenters(presenter); } var notification = new ProcessSigninResponseContext(Context, Options, ticket, request, response); if (request.IsAuthorizationRequest()) { // By default, return an authorization code if a response type containing code was specified. notification.IncludeAuthorizationCode = request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Code); // By default, return an access token if a response type containing token was specified. notification.IncludeAccessToken = request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Token); // By default, prevent a refresh token from being returned as the OAuth2 specification // explicitly disallows returning a refresh token from the authorization endpoint. // See https://tools.ietf.org/html/rfc6749#section-4.2.2 for more information. notification.IncludeRefreshToken = false; // By default, return an identity token if a response type containing code // was specified and if the openid scope was explicitly or implicitly granted. notification.IncludeIdentityToken = request.HasResponseType(OpenIdConnectConstants.ResponseTypes.IdToken) && ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId); } else { // By default, prevent an authorization code from being returned as this type of token // cannot be issued from the token endpoint in the standard OAuth2/OpenID Connect flows. notification.IncludeAuthorizationCode = false; // By default, always return an access token. notification.IncludeAccessToken = true; // By default, only return a refresh token is the offline_access scope was granted and if // sliding expiration is disabled or if the request is not a grant_type=refresh_token request. notification.IncludeRefreshToken = ticket.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) && (Options.UseSlidingExpiration || !request.IsRefreshTokenGrantType()); // By default, only return an identity token if the openid scope was granted. notification.IncludeIdentityToken = ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId); } await Options.Provider.ProcessSigninResponse(notification); if (notification.HandledResponse) { Logger.LogDebug("The sign-in response was handled in user code."); return(true); } else if (notification.Skipped) { Logger.LogDebug("The default sign-in handling was skipped from user code."); return(false); } else if (notification.IsRejected) { Logger.LogError("The request was rejected with the following error: {Error} ; {Description}", /* Error: */ notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, /* Description: */ notification.ErrorDescription); if (request.IsAuthorizationRequest()) { return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse { Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = notification.ErrorDescription, ErrorUri = notification.ErrorUri })); } return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = notification.ErrorDescription, ErrorUri = notification.ErrorUri })); } // Flow the changes made to the ticket. ticket = notification.Ticket; // Ensure an authentication ticket has been provided or return // an error code indicating that the request was rejected. if (ticket == null) { Logger.LogError("The request was rejected because no authentication ticket was provided."); if (request.IsAuthorizationRequest()) { return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.AccessDenied, ErrorDescription = "The authorization was denied by the resource owner." })); } return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "The token request was rejected by the authorization server." })); } if (notification.IncludeAuthorizationCode) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); response.Code = await SerializeAuthorizationCodeAsync(ticket.Identity, properties, request, response); } if (notification.IncludeAccessToken) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); // When receiving a grant_type=refresh_token request, determine whether the client application // requests a limited set of scopes and replace the corresponding properties if necessary. if (!string.IsNullOrEmpty(request.Scope) && request.IsTokenRequest() && request.IsRefreshTokenGrantType()) { Logger.LogDebug("The access token scopes will be limited to the scopes requested " + "by the client application: {Scopes}.", request.GetScopes()); // Replace the scopes initially granted by the scopes listed by the client // application in the token request. Note: request.GetScopes() automatically // removes duplicate entries, so additional filtering is not necessary. properties.SetProperty(OpenIdConnectConstants.Properties.Scopes, new JArray(request.GetScopes()).ToString(Formatting.None)); } var scopes = ticket.GetScopes(); if ((request.IsTokenRequest() && request.IsAuthorizationCodeGrantType()) || !new HashSet <string>(scopes).SetEquals(request.GetScopes())) { response.Scope = string.Join(" ", scopes); } response.TokenType = OpenIdConnectConstants.TokenTypes.Bearer; response.AccessToken = await SerializeAccessTokenAsync(ticket.Identity, properties, request, response); // properties.ExpiresUtc is automatically set by SerializeAccessTokenAsync but the end user // is free to set a null value directly in the SerializeAccessToken event. if (properties.ExpiresUtc.HasValue && properties.ExpiresUtc > Options.SystemClock.UtcNow) { var lifetime = properties.ExpiresUtc.Value - Options.SystemClock.UtcNow; response.ExpiresIn = (long)(lifetime.TotalSeconds + .5); } } if (notification.IncludeRefreshToken) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); response.RefreshToken = await SerializeRefreshTokenAsync(ticket.Identity, properties, request, response); } if (notification.IncludeIdentityToken) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); response.IdToken = await SerializeIdentityTokenAsync(ticket.Identity, properties, request, response); } if (request.IsAuthorizationRequest()) { return(await SendAuthorizationResponseAsync(response, ticket)); } return(await SendTokenResponseAsync(response, ticket)); }
private async Task <bool> HandleSignInAsync(AuthenticationTicket ticket) { // Extract the OpenID Connect request from the ASP.NET Core context. // If it cannot be found or doesn't correspond to an authorization // or a token request, throw an InvalidOperationException. var request = Context.GetOpenIdConnectRequest(); if (request == null || (!request.IsAuthorizationRequest() && !request.IsTokenRequest())) { throw new InvalidOperationException("An authorization or token response cannot be returned from this endpoint."); } // Note: if a response was already generated, throw an exception. var response = Context.GetOpenIdConnectResponse(); if (response != null || Response.HasStarted) { throw new InvalidOperationException("A response has already been sent."); } if (string.IsNullOrEmpty(ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Subject))) { throw new InvalidOperationException("The authentication ticket was rejected because " + "the mandatory subject claim was missing."); } Logger.LogTrace("A sign-in operation was triggered: {Claims} ; {Properties}.", ticket.Principal.Claims, ticket.Properties.Items); // Prepare a new OpenID Connect response. response = new OpenIdConnectResponse(); // Copy the confidentiality level associated with the request to the authentication ticket. if (!ticket.HasProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel)) { ticket.SetConfidentialityLevel(request.GetProperty <string>(OpenIdConnectConstants.Properties.ConfidentialityLevel)); } // Always include the "openid" scope when the developer doesn't explicitly call SetScopes. // Note: the application is allowed to specify a different "scopes": in this case, // don't replace the "scopes" property stored in the authentication ticket. if (request.HasScope(OpenIdConnectConstants.Scopes.OpenId) && !ticket.HasScope()) { ticket.SetScopes(OpenIdConnectConstants.Scopes.OpenId); } // When a "resources" property cannot be found in the ticket, // infer it from the "audiences" property. if (ticket.HasAudience() && !ticket.HasResource()) { ticket.SetResources(ticket.GetAudiences()); } // Add the validated client_id to the list of authorized presenters, // unless the presenters were explicitly set by the developer. var presenter = request.GetProperty <string>(OpenIdConnectConstants.Properties.ValidatedClientId); if (!string.IsNullOrEmpty(presenter) && !ticket.HasPresenter()) { ticket.SetPresenters(presenter); } // Only return an authorization code if the request is an authorization request and has response_type=code. if (request.IsAuthorizationRequest() && request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Code)) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); response.Code = await SerializeAuthorizationCodeAsync(ticket.Principal, properties, request, response); } // Only return an access token if the request is a token request // or an authorization request that specifies response_type=token. if (request.IsTokenRequest() || (request.IsAuthorizationRequest() && request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Token))) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); // When receiving a grant_type=refresh_token request, determine whether the client application // requests a limited set of scopes/resources and replace the corresponding properties if necessary. // Note: at this stage, request.GetResources() cannot return more items than the ones that were initially granted // by the resource owner as the "resources" parameter is always validated when receiving the token request. if (request.IsTokenRequest() && request.IsRefreshTokenGrantType()) { if (!string.IsNullOrEmpty(request.Resource)) { Logger.LogDebug("The access token resources will be limited to the resources requested " + "by the client application: {Resources}.", request.GetResources()); // Replace the resources initially granted by the resources listed by the client // application in the token request. Note: request.GetResources() automatically // removes duplicate entries, so additional filtering is not necessary. properties.SetProperty(OpenIdConnectConstants.Properties.Resources, new JArray(request.GetResources()).ToString(Formatting.None)); } if (!string.IsNullOrEmpty(request.Scope)) { Logger.LogDebug("The access token scopes will be limited to the scopes requested " + "by the client application: {Scopes}.", request.GetScopes()); // Replace the scopes initially granted by the scopes listed by the client // application in the token request. Note: request.GetScopes() automatically // removes duplicate entries, so additional filtering is not necessary. properties.SetProperty(OpenIdConnectConstants.Properties.Scopes, new JArray(request.GetScopes()).ToString(Formatting.None)); } } var resources = ticket.GetResources(); if (request.IsAuthorizationCodeGrantType() || !new HashSet <string>(resources).SetEquals(request.GetResources())) { response.Resource = string.Join(" ", resources); } var scopes = ticket.GetScopes(); if (request.IsAuthorizationCodeGrantType() || !new HashSet <string>(scopes).SetEquals(request.GetScopes())) { response.Scope = string.Join(" ", scopes); } response.TokenType = OpenIdConnectConstants.TokenTypes.Bearer; response.AccessToken = await SerializeAccessTokenAsync(ticket.Principal, properties, request, response); // properties.ExpiresUtc is automatically set by SerializeAccessTokenAsync but the end user // is free to set a null value directly in the SerializeAccessToken event. if (properties.ExpiresUtc.HasValue && properties.ExpiresUtc > Options.SystemClock.UtcNow) { var lifetime = properties.ExpiresUtc.Value - Options.SystemClock.UtcNow; response.ExpiresIn = (long)(lifetime.TotalSeconds + .5); } } // Only return a refresh token if the request is a token request that specifies scope=offline_access. if (request.IsTokenRequest() && ticket.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess)) { // Note: when sliding expiration is disabled, don't return a new refresh token, // unless the token request is not a grant_type=refresh_token request. if (Options.UseSlidingExpiration || !request.IsRefreshTokenGrantType()) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); response.RefreshToken = await SerializeRefreshTokenAsync(ticket.Principal, properties, request, response); } } // Only return an identity token if the openid scope was requested and granted // to avoid generating and returning an unnecessary token to pure OAuth2 clients. if (ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId)) { // Note: don't return an identity token if the request is an // authorization request that doesn't use response_type=id_token. if (request.IsTokenRequest() || request.HasResponseType(OpenIdConnectConstants.ResponseTypes.IdToken)) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); response.IdToken = await SerializeIdentityTokenAsync(ticket.Principal, properties, request, response); } } if (request.IsAuthorizationRequest()) { return(await SendAuthorizationResponseAsync(response, ticket)); } return(await SendTokenResponseAsync(response, ticket)); }
private async Task <AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, HaloLiveApplicationUser user) { // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await SignInManager.CreateUserPrincipalAsync(user); // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), OpenIdConnectServerDefaults.AuthenticationScheme); // Set the list of scopes granted to the client application. ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Profile, OpenIddictConstants.Scopes.Roles }.Intersect(request.GetScopes())); ticket.SetResources("auth-server"); // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in ticket.Principal.Claims) { // Never include the security stamp in the access and identity tokens, as it's a secret value. if (claim.Type == IdentityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. // We should also add the "sub" claim too for identity sake if ((claim.Type == OpenIdConnectConstants.Claims.Name || claim.Type == OpenIdConnectConstants.Claims.Subject) && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(ticket); }
private async Task <AuthenticationTicket> CreateTicketAsync( OpenIdConnectRequest request, Users user, AuthenticationProperties properties = null) { // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await _signInManager.CreateUserPrincipalAsync(user); //principal.AddIdentity(new ClaimsIdentity(new List<Claim>() //{ // new Claim("org", user.OrganizationId), // new Claim(System.Security.Claims.ClaimTypes.NameIdentifier, user.Id) //})); var ci = principal.Identity as ClaimsIdentity; ci.AddClaim("org", user.OrganizationId); ci.AddClaim(OpenIdConnectConstants.Claims.Subject, user.Id); if (properties == null) { properties = new AuthenticationProperties(); } // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, properties, OpenIdConnectServerDefaults.AuthenticationScheme); if (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType()) { // Set the list of scopes granted to the client application. // Note: the offline_access scope must be granted // to allow OpenIddict to return a refresh token. ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles }.Intersect(request.GetScopes())); } // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in ticket.Principal.Claims) { // Never include the security stamp in the access and identity tokens, as it's a secret value. if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if ((claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) || (claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) || (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles))) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(ticket); }
public async Task <AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, User user) { var principal = await _signInManager.CreateUserPrincipalAsync(user); var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), OpenIddictServerDefaults.AuthenticationScheme); ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Phone, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles }.Intersect(request.GetScopes())); foreach (var claim in ticket.Principal.Claims) { if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; if ((claim.Type == OpenIdConnectConstants.Claims.Subject && ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId)) || (claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) || (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles)) || (claim.Type == CustomClaimTypes.Permission && ticket.HasScope(OpenIddictConstants.Claims.Roles))) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } var identity = principal.Identity as ClaimsIdentity; if (ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) { if (!string.IsNullOrWhiteSpace(user.JobTitle)) { identity.AddClaim(CustomClaimTypes.JobTitle, user.JobTitle, OpenIdConnectConstants.Destinations.IdentityToken); } if (!string.IsNullOrWhiteSpace(user.FullName)) { identity.AddClaim(CustomClaimTypes.FullName, user.FullName, OpenIdConnectConstants.Destinations.IdentityToken); } if (!string.IsNullOrWhiteSpace(user.Configuration)) { identity.AddClaim(CustomClaimTypes.Configuration, user.Configuration, OpenIdConnectConstants.Destinations.IdentityToken); } } if (ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) { if (!string.IsNullOrWhiteSpace(user.Email)) { identity.AddClaim(CustomClaimTypes.Email, user.Email, OpenIdConnectConstants.Destinations.IdentityToken); } } if (ticket.HasScope(OpenIdConnectConstants.Scopes.Phone)) { if (!string.IsNullOrWhiteSpace(user.PhoneNumber)) { identity.AddClaim(CustomClaimTypes.Phone, user.PhoneNumber, OpenIdConnectConstants.Destinations.IdentityToken); } } return(ticket); }
private async Task <bool> InvokeTokenEndpointAsync() { if (!string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) { Logger.LogError("The token request was rejected because an invalid " + "HTTP method was received: {Method}.", Request.Method); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed token request has been received: make sure to use POST." })); } // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization if (string.IsNullOrEmpty(Request.ContentType)) { Logger.LogError("The token request was rejected because the " + "mandatory 'Content-Type' header was missing."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed token request has been received: " + "the mandatory 'Content-Type' header was missing from the POST request." })); } // May have media/type; charset=utf-8, allow partial match. if (!Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)) { Logger.LogError("The token request was rejected because an invalid 'Content-Type' " + "header was received: {ContentType}.", Request.ContentType); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed token request has been received: " + "the 'Content-Type' header contained an unexcepted value. " + "Make sure to use 'application/x-www-form-urlencoded'." })); } var form = await Request.ReadFormAsync(Context.RequestAborted); var request = new OpenIdConnectMessage(form.ToDictionary()) { RequestType = OpenIdConnectRequestType.TokenRequest }; // Store the token request in the ASP.NET context. Context.SetOpenIdConnectRequest(request); // Reject token requests missing the mandatory grant_type parameter. if (string.IsNullOrEmpty(request.GrantType)) { Logger.LogError("The token request was rejected because the grant type was missing."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The mandatory 'grant_type' parameter was missing.", })); } // Reject grant_type=authorization_code requests missing the authorization code. // See https://tools.ietf.org/html/rfc6749#section-4.1.3 else if (request.IsAuthorizationCodeGrantType() && string.IsNullOrEmpty(request.Code)) { Logger.LogError("The token request was rejected because the authorization code was missing."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The mandatory 'code' parameter was missing." })); } // Reject grant_type=refresh_token requests missing the refresh token. // See https://tools.ietf.org/html/rfc6749#section-6 else if (request.IsRefreshTokenGrantType() && string.IsNullOrEmpty(request.RefreshToken)) { Logger.LogError("The token request was rejected because the refresh token was missing."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The mandatory 'refresh_token' parameter was missing." })); } // Reject grant_type=password requests missing username or password. // See https://tools.ietf.org/html/rfc6749#section-4.3.2 else if (request.IsPasswordGrantType() && (string.IsNullOrEmpty(request.Username) || string.IsNullOrEmpty(request.Password))) { Logger.LogError("The token request was rejected because the resource owner credentials were missing."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The mandatory 'username' and/or 'password' parameters " + "was/were missing from the request message." })); } // When client_id and client_secret are both null, try to extract them from the Authorization header. // See http://tools.ietf.org/html/rfc6749#section-2.3.1 and // http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication if (string.IsNullOrEmpty(request.ClientId) && string.IsNullOrEmpty(request.ClientSecret)) { string header = Request.Headers[HeaderNames.Authorization]; if (!string.IsNullOrEmpty(header) && header.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) { try { var value = header.Substring("Basic ".Length).Trim(); var data = Encoding.UTF8.GetString(Convert.FromBase64String(value)); var index = data.IndexOf(':'); if (index >= 0) { request.ClientId = data.Substring(0, index); request.ClientSecret = data.Substring(index + 1); } } catch (FormatException) { } catch (ArgumentException) { } } } var validatingContext = new ValidateTokenRequestContext(Context, Options, request); await Options.Provider.ValidateTokenRequest(validatingContext); if (validatingContext.IsRejected) { Logger.LogInformation("The token request was rejected by application code."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = validatingContext.Error ?? OpenIdConnectConstants.Errors.InvalidClient, ErrorDescription = validatingContext.ErrorDescription, ErrorUri = validatingContext.ErrorUri })); } // Reject grant_type=client_credentials requests if validation was skipped. else if (validatingContext.IsSkipped && request.IsClientCredentialsGrantType()) { Logger.LogError("The token request must be fully validated to use the client_credentials grant type."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Client authentication is required when using client_credentials." })); } // Ensure that the client_id has been set from the ValidateTokenRequest event. else if (validatingContext.IsValidated && string.IsNullOrEmpty(request.ClientId)) { Logger.LogError("The token request was validated but the client_id was not set."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "An internal server error occurred." })); } AuthenticationTicket ticket = null; // See http://tools.ietf.org/html/rfc6749#section-4.1 // and http://tools.ietf.org/html/rfc6749#section-4.1.3 (authorization code grant). // See http://tools.ietf.org/html/rfc6749#section-6 (refresh token grant). if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType()) { ticket = request.IsAuthorizationCodeGrantType() ? await DeserializeAuthorizationCodeAsync(request.Code, request) : await DeserializeRefreshTokenAsync(request.RefreshToken, request); if (ticket == null) { Logger.LogError("The token request was rejected because the " + "authorization code or the refresh token was invalid."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Invalid ticket" })); } if (!ticket.Properties.ExpiresUtc.HasValue || ticket.Properties.ExpiresUtc < Options.SystemClock.UtcNow) { Logger.LogError("The token request was rejected because the " + "authorization code or the refresh token was expired."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Expired ticket" })); } // If the client was fully authenticated when retrieving its refresh token, // the current request must be rejected if client authentication was not enforced. if (request.IsRefreshTokenGrantType() && !validatingContext.IsValidated && ticket.IsConfidential()) { Logger.LogError("The token request was rejected because client authentication " + "was required to use the confidential refresh token."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Client authentication is required to use this ticket" })); } // Note: presenters may be empty during a grant_type=refresh_token request if the refresh token // was issued to a public client but cannot be null for an authorization code grant request. var presenters = ticket.GetPresenters(); if (request.IsAuthorizationCodeGrantType() && !presenters.Any()) { Logger.LogError("The token request was rejected because the authorization " + "code didn't contain any valid presenter."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "An internal server error occurred." })); } // At this stage, client_id cannot be null for grant_type=authorization_code requests, // as it must either be set in the ValidateClientAuthentication notification // by the developer or manually flowed by non-confidential client applications. // See https://tools.ietf.org/html/rfc6749#section-4.1.3 if (request.IsAuthorizationCodeGrantType() && string.IsNullOrEmpty(request.ClientId)) { Logger.LogError("The token request was rejected because the mandatory 'client_id' was missing."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "client_id was missing from the token request" })); } // Ensure the authorization code/refresh token was issued to the client application making the token request. // Note: when using the refresh token grant, client_id is optional but must validated if present. // As a consequence, this check doesn't depend on the actual status of client authentication. // See https://tools.ietf.org/html/rfc6749#section-6 // and http://openid.net/specs/openid-connect-core-1_0.html#RefreshingAccessToken if (!string.IsNullOrEmpty(request.ClientId) && presenters.Any() && !presenters.Contains(request.ClientId, StringComparer.Ordinal)) { Logger.LogError("The token request was rejected because the authorization " + "code was issued to a different client application."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Ticket does not contain matching client_id" })); } // Validate the redirect_uri flowed by the client application during this token request. // Note: for pure OAuth2 requests, redirect_uri is only mandatory if the authorization request // contained an explicit redirect_uri. OpenID Connect requests MUST include a redirect_uri // but the specifications allow proceeding the token request without returning an error // if the authorization request didn't contain an explicit redirect_uri. // See https://tools.ietf.org/html/rfc6749#section-4.1.3 // and http://openid.net/specs/openid-connect-core-1_0.html#TokenRequestValidation var address = ticket.GetProperty(OpenIdConnectConstants.Properties.RedirectUri); if (request.IsAuthorizationCodeGrantType() && !string.IsNullOrEmpty(address)) { ticket.Properties.Items.Remove(OpenIdConnectConstants.Properties.RedirectUri); if (string.IsNullOrEmpty(request.RedirectUri)) { Logger.LogError("The token request was rejected because the mandatory 'redirect_uri' " + "parameter was missing from the grant_type=authorization_code request."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "redirect_uri was missing from the token request" })); } else if (!string.Equals(address, request.RedirectUri, StringComparison.Ordinal)) { Logger.LogError("The token request was rejected because the 'redirect_uri' " + "parameter didn't correspond to the expected value."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Authorization code does not contain matching redirect_uri" })); } } if (!string.IsNullOrEmpty(request.Scope)) { // When an explicit scope parameter has been included in the token request // but was missing from the authorization request, the request MUST be rejected. // See http://tools.ietf.org/html/rfc6749#section-6 var scopes = ticket.GetScopes(); if (!scopes.Any()) { Logger.LogError("The token request was rejected because the 'scope' parameter was not allowed."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Token request cannot contain a scope parameter" + "if the authorization request didn't contain one" })); } // When an explicit scope parameter has been included in the token request, // the authorization server MUST ensure that it doesn't contain scopes // that were not allowed during the authorization request. // See https://tools.ietf.org/html/rfc6749#section-6 else if (!new HashSet <string>(scopes).IsSupersetOf(request.GetScopes())) { Logger.LogError("The token request was rejected because the 'scope' parameter was not valid."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Token request doesn't contain a valid scope parameter" })); } // Replace the scopes initially granted by the scopes // listed by the client application in the token request. ticket.SetScopes(request.GetScopes()); } if (request.IsAuthorizationCodeGrantType()) { // Note: the authentication ticket is copied to avoid modifying the properties of the authorization code. var context = new GrantAuthorizationCodeContext(Context, Options, request, ticket.Copy()); await Options.Provider.GrantAuthorizationCode(context); if (!context.IsValidated) { // Note: use invalid_grant as the default error if none has been explicitly provided. return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = context.ErrorDescription, ErrorUri = context.ErrorUri })); } // By default, when using the authorization code grant, the authentication ticket extracted from the // authorization code is used as-is. To avoid aligning the expiration date of the generated tokens // with the lifetime of the authorization code, the ticket properties are automatically reset to null. if (context.Ticket.Properties.IssuedUtc == ticket.Properties.IssuedUtc) { context.Ticket.Properties.IssuedUtc = null; } if (context.Ticket.Properties.ExpiresUtc == ticket.Properties.ExpiresUtc) { context.Ticket.Properties.ExpiresUtc = null; } ticket = context.Ticket; } else { // Note: the authentication ticket is copied to avoid modifying the properties of the refresh token. var context = new GrantRefreshTokenContext(Context, Options, request, ticket.Copy()); await Options.Provider.GrantRefreshToken(context); if (!context.IsValidated) { // Note: use invalid_grant as the default error if none has been explicitly provided. return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = context.ErrorDescription, ErrorUri = context.ErrorUri })); } // By default, when using the refresh token grant, the authentication ticket extracted from the // refresh token is used as-is. To avoid aligning the expiration date of the generated tokens // with the lifetime of the refresh token, the ticket properties are automatically reset to null. if (context.Ticket.Properties.IssuedUtc == ticket.Properties.IssuedUtc) { context.Ticket.Properties.IssuedUtc = null; } if (context.Ticket.Properties.ExpiresUtc == ticket.Properties.ExpiresUtc) { context.Ticket.Properties.ExpiresUtc = null; } ticket = context.Ticket; } } // See http://tools.ietf.org/html/rfc6749#section-4.3 // and http://tools.ietf.org/html/rfc6749#section-4.3.2 else if (request.IsPasswordGrantType()) { var context = new GrantResourceOwnerCredentialsContext(Context, Options, request); await Options.Provider.GrantResourceOwnerCredentials(context); if (!context.IsValidated) { // Note: use invalid_grant as the default error if none has been explicitly provided. return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = context.ErrorDescription, ErrorUri = context.ErrorUri })); } ticket = context.Ticket; } // See http://tools.ietf.org/html/rfc6749#section-4.4 // and http://tools.ietf.org/html/rfc6749#section-4.4.2 else if (request.IsClientCredentialsGrantType()) { var context = new GrantClientCredentialsContext(Context, Options, request); await Options.Provider.GrantClientCredentials(context); if (!context.IsValidated) { // Note: use unauthorized_client as the default error if none has been explicitly provided. return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = context.Error ?? OpenIdConnectConstants.Errors.UnauthorizedClient, ErrorDescription = context.ErrorDescription, ErrorUri = context.ErrorUri })); } ticket = context.Ticket; } // See http://tools.ietf.org/html/rfc6749#section-8.3 else { var context = new GrantCustomExtensionContext(Context, Options, request); await Options.Provider.GrantCustomExtension(context); if (!context.IsValidated) { // Note: use unsupported_grant_type as the default error if none has been explicitly provided. return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = context.Error ?? OpenIdConnectConstants.Errors.UnsupportedGrantType, ErrorDescription = context.ErrorDescription, ErrorUri = context.ErrorUri })); } ticket = context.Ticket; } var notification = new HandleTokenRequestContext(Context, Options, request, ticket); await Options.Provider.HandleTokenRequest(notification); if (notification.HandledResponse) { return(true); } else if (notification.Skipped) { return(false); } // Flow the changes made to the ticket. ticket = notification.Ticket; // Ensure an authentication ticket has been provided: // a null ticket MUST result in an internal server error. if (ticket == null) { Logger.LogError("The token request was rejected because no authentication " + "ticket was returned by application code."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.ServerError })); } if (validatingContext.IsValidated) { // Store a boolean indicating whether the ticket should be marked as confidential. ticket.Properties.Items[OpenIdConnectConstants.Properties.Confidential] = "true"; } // Always include the "openid" scope when the developer doesn't explicitly call SetScopes. // Note: the application is allowed to specify a different "scopes": in this case, // don't replace the "scopes" property stored in the authentication ticket. if (!ticket.Properties.Items.ContainsKey(OpenIdConnectConstants.Properties.Scopes) && request.HasScope(OpenIdConnectConstants.Scopes.OpenId)) { ticket.Properties.Items[OpenIdConnectConstants.Properties.Scopes] = OpenIdConnectConstants.Scopes.OpenId; } string audiences; // When a "resources" property cannot be found in the authentication properties, infer it from the "audiences" property. if (!ticket.Properties.Items.ContainsKey(OpenIdConnectConstants.Properties.Resources) && ticket.Properties.Items.TryGetValue(OpenIdConnectConstants.Properties.Audiences, out audiences)) { ticket.Properties.Items[OpenIdConnectConstants.Properties.Resources] = audiences; } var response = new OpenIdConnectMessage(); // Note: by default, an access token is always returned, but the client application can use the "response_type" parameter // to only include specific types of tokens. When this parameter is missing, an access token is always generated. if (string.IsNullOrEmpty(request.ResponseType) || request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Token)) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); string resources; if (!properties.Items.TryGetValue(OpenIdConnectConstants.Properties.Resources, out resources)) { Logger.LogInformation("No explicit resource has been associated with the authentication ticket: " + "the access token will thus be issued without any audience attached."); } // Note: when the "resource" parameter added to the OpenID Connect response // is identical to the request parameter, keeping it is not necessary. if (request.IsAuthorizationCodeGrantType() || (!string.IsNullOrEmpty(request.Resource) && !string.Equals(request.Resource, resources, StringComparison.Ordinal))) { response.Resource = resources; } // Note: when the "scope" parameter added to the OpenID Connect response // is identical to the request parameter, keeping it is not necessary. string scopes; properties.Items.TryGetValue(OpenIdConnectConstants.Properties.Scopes, out scopes); if (request.IsAuthorizationCodeGrantType() || (!string.IsNullOrEmpty(request.Scope) && !string.Equals(request.Scope, scopes, StringComparison.Ordinal))) { response.Scope = scopes; } response.TokenType = OpenIdConnectConstants.TokenTypes.Bearer; response.AccessToken = await SerializeAccessTokenAsync(ticket.Principal, properties, request, response); // Ensure that an access token is issued to avoid returning an invalid response. // See http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Combinations if (string.IsNullOrEmpty(response.AccessToken)) { Logger.LogError("An error occurred during the serialization of the " + "access token and a null value was returned."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "no valid access token was issued" })); } // properties.ExpiresUtc is automatically set by SerializeAccessTokenAsync but the end user // is free to set a null value directly in the SerializeAccessToken event. if (properties.ExpiresUtc.HasValue && properties.ExpiresUtc > Options.SystemClock.UtcNow) { var lifetime = properties.ExpiresUtc.Value - Options.SystemClock.UtcNow; var expiration = (long)(lifetime.TotalSeconds + .5); response.ExpiresIn = expiration.ToString(CultureInfo.InvariantCulture); } } // Note: by default, an identity token is always returned when the "openid" scope has been requested, // but the client application can use the "response_type" parameter to only include specific types of tokens. // When this parameter is missing, an identity token is always generated. if (ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId) && (string.IsNullOrEmpty(request.ResponseType) || request.HasResponseType(OpenIdConnectConstants.ResponseTypes.IdToken))) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); response.IdToken = await SerializeIdentityTokenAsync(ticket.Principal, properties, request, response); // Ensure that an identity token is issued to avoid returning an invalid response. // See http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse // and http://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse if (string.IsNullOrEmpty(response.IdToken)) { Logger.LogError("An error occurred during the serialization of the " + "identity token and a null value was returned."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "no valid identity token was issued" })); } } // Note: by default, a refresh token is always returned when the "offline_access" scope has been requested, // but the client application can use the "response_type" parameter to only include specific types of tokens. // When this parameter is missing, a refresh token is always generated. if (ticket.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) && (!request.IsRefreshTokenGrantType() || Options.UseSlidingExpiration) && (string.IsNullOrEmpty(request.ResponseType) || request.HasResponseType(OpenIdConnectConstants.Parameters.RefreshToken))) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); response.RefreshToken = await SerializeRefreshTokenAsync(ticket.Principal, properties, request, response); } var payload = new JObject(); foreach (var parameter in response.Parameters) { payload.Add(parameter.Key, parameter.Value); } var responseNotification = new ApplyTokenResponseContext(Context, Options, ticket, request, payload); await Options.Provider.ApplyTokenResponse(responseNotification); if (responseNotification.HandledResponse) { return(true); } else if (responseNotification.Skipped) { return(false); } using (var buffer = new MemoryStream()) using (var writer = new JsonTextWriter(new StreamWriter(buffer))) { payload.WriteTo(writer); writer.Flush(); Response.ContentLength = buffer.Length; Response.ContentType = "application/json;charset=UTF-8"; Response.Headers[HeaderNames.CacheControl] = "no-cache"; Response.Headers[HeaderNames.Pragma] = "no-cache"; Response.Headers[HeaderNames.Expires] = "-1"; buffer.Seek(offset: 0, loc: SeekOrigin.Begin); await buffer.CopyToAsync(Response.Body, 4096, Context.RequestAborted); return(true); } }
private async Task <AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, ApplicationUser user) { // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await _signInManager.CreateUserPrincipalAsync(user); // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), OpenIddictServerDefaults.AuthenticationScheme); //if (!request.IsRefreshTokenGrantType()) //{ // Set the list of scopes granted to the client application. // Note: the offline_access scope must be granted // to allow OpenIddict to return a refresh token. ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Phone, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles }.Intersect(request.GetScopes())); //} //ticket.SetResources("quickapp-api"); // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in ticket.Principal.Claims) { // Never include the security stamp in the access and identity tokens, as it's a secret value. if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if (claim.Type == OpenIdConnectConstants.Claims.Subject && ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId) || claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile) || claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles) || claim.Type == CustomClaimTypes.Permission && ticket.HasScope(OpenIddictConstants.Claims.Roles)) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } var identity = principal.Identity as ClaimsIdentity; if (ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) { if (!string.IsNullOrWhiteSpace(user.Email)) { identity.AddClaim(CustomClaimTypes.Email, user.Email, OpenIdConnectConstants.Destinations.IdentityToken); } } if (ticket.HasScope(OpenIdConnectConstants.Scopes.Phone)) { if (!string.IsNullOrWhiteSpace(user.PhoneNumber)) { identity.AddClaim(CustomClaimTypes.Phone, user.PhoneNumber, OpenIdConnectConstants.Destinations.IdentityToken); } } return(ticket); }
private async Task <AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, User user) { var principal = await _signInManager.CreateUserPrincipalAsync(user); var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), OpenIddictServerDefaults.AuthenticationScheme); ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Phone, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles }.Intersect(request.GetScopes())); //ticket.SetResources("aurelia-api"); // Add destinations to the claims based on the type foreach (var claim in ticket.Principal.Claims) { // Don't include if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if ((claim.Type == OpenIdConnectConstants.Claims.Subject && ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId)) || (claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) || (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles)) || (claim.Type == Claims.Permission && ticket.HasScope(OpenIddictConstants.Claims.Roles)) ) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } //Additional scopes and claims can be addedd if need be //var identity = principal.Identity as ClaimsIdentity; //if (ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) //{ // if (!string.IsNullOrWhiteSpace(user.JobTitle)) // identity.AddClaim(CustomClaimTypes.JobTitle, user.JobTitle, OpenIdConnectConstants.Destinations.IdentityToken); // if (!string.IsNullOrWhiteSpace(user.Configuration)) // identity.AddClaim(CustomClaimTypes.Configuration, user.Configuration, OpenIdConnectConstants.Destinations.IdentityToken); //} //if (ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) //{ // if (!string.IsNullOrWhiteSpace(user.Email)) // identity.AddClaim(CustomClaimTypes.Email, user.Email, OpenIdConnectConstants.Destinations.IdentityToken); //} //if (ticket.HasScope(OpenIdConnectConstants.Scopes.Phone)) //{ // if (!string.IsNullOrWhiteSpace(user.PhoneNumber)) // identity.AddClaim(CustomClaimTypes.Phone, user.PhoneNumber, OpenIdConnectConstants.Destinations.IdentityToken); //} return(ticket); }
private async Task <AuthenticationTicket> CreateTicketAsync( OpenIdConnectRequest request, ApplicationUser user, AuthenticationProperties properties = null) { // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await _signInManager.CreateUserPrincipalAsync(user); // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, properties ?? new AuthenticationProperties(), OpenIdConnectServerDefaults.AuthenticationScheme); // Set the list of scopes granted to the client application. // Note: the offline_access scope must be granted // to allow OpenIddict to return a refresh token. var roleNames = await _userManager.GetRolesAsync(user); var identity = ticket.Principal.Identity as ClaimsIdentity; if (!request.IsAuthorizationCodeGrantType()) { var scopes = new List <string> { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles }.Intersect(request.GetScopes()).ToList(); ticket.SetScopes(scopes); } // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in ticket.Principal.Claims) { // Never include the security stamp in the access and identity tokens, as it's a secret value. if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if ((claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) || (claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) || (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles))) { var type = claim.Type; claim.SetDestinations(OpenIdConnectConstants.Destinations.IdentityToken, OpenIdConnectConstants.Destinations.AccessToken); } else { claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken); } } if (_appOptions.TokenGeneration.Audiences.Any()) { foreach (var audience in _appOptions.TokenGeneration.Audiences) { ticket.SetAudiences(audience); } } if (_appOptions.TokenGeneration.Resources.Any()) { foreach (var resource in _appOptions.TokenGeneration.Resources) { ticket.SetResources(resource); } } if (_appOptions.TokenGeneration.IncludeUserIdClaim) { AddUserIdClaim(ticket, user); } ticket.SetAccessTokenLifetime(TimeSpan.FromSeconds(_appOptions.TokenGeneration.AccessTokenLifetime)); ticket.SetIdentityTokenLifetime(TimeSpan.FromSeconds(_appOptions.TokenGeneration.IdentityTokenLifetime)); ticket.SetRefreshTokenLifetime(TimeSpan.FromSeconds(_appOptions.TokenGeneration.RefreshTokenLifetime)); return(ticket); }
private async Task <AuthenticationTicket> CreateTicketAsync( IUser user, object application, object authorization, OpenIdConnectRequest request, AuthenticationProperties properties = null) { Debug.Assert(request.IsAuthorizationRequest() || request.IsTokenRequest(), "The request should be an authorization or token request."); // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await _signInManager.CreateUserPrincipalAsync(user); var identity = (ClaimsIdentity)principal.Identity; // Note: while ASP.NET Core Identity uses the legacy WS-Federation claims (exposed by the ClaimTypes class), // OpenIddict uses the newer JWT claims defined by the OpenID Connect specification. To ensure the mandatory // subject claim is correctly populated (and avoid an InvalidOperationException), it's manually added here. if (string.IsNullOrEmpty(principal.FindFirstValue(OpenIddictConstants.Claims.Subject))) { identity.AddClaim(new Claim(OpenIddictConstants.Claims.Subject, await _userManager.GetUserIdAsync(user))); } // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, properties, OpenIddictServerDefaults.AuthenticationScheme); if (request.IsAuthorizationRequest() || (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType())) { // Set the list of scopes granted to the client application. // Note: the offline_access scope must be granted // to allow OpenIddict to return a refresh token. ticket.SetScopes(request.GetScopes()); ticket.SetResources(await GetResourcesAsync(request.GetScopes())); // If the request is an authorization request, automatically create // a permanent authorization to avoid requiring explicit consent for // future authorization or token requests containing the same scopes. if (authorization == null && request.IsAuthorizationRequest()) { authorization = await _authorizationManager.CreateAsync( principal : ticket.Principal, subject : await _userManager.GetUserIdAsync(user), client : await _applicationManager.GetIdAsync(application), type : OpenIddictConstants.AuthorizationTypes.Permanent, scopes : ImmutableArray.CreateRange(ticket.GetScopes()), properties : ImmutableDictionary.CreateRange(ticket.Properties.Items)); } if (authorization != null) { // Attach the authorization identifier to the authentication ticket. ticket.SetProperty(OpenIddictConstants.Properties.InternalAuthorizationId, await _authorizationManager.GetIdAsync(authorization)); } } // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in ticket.Principal.Claims) { // Never include the security stamp in the access and identity tokens, as it's a secret value. if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIddictConstants.Destinations.AccessToken }; // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if ((claim.Type == OpenIddictConstants.Claims.Name && ticket.HasScope(OpenIddictConstants.Scopes.Profile)) || (claim.Type == OpenIddictConstants.Claims.Email && ticket.HasScope(OpenIddictConstants.Scopes.Email)) || (claim.Type == OpenIddictConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles))) { destinations.Add(OpenIddictConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } return(ticket); }
private IEnumerable <string> GetDestinations(Claim claim, AuthenticationTicket ticket) { switch (claim.Type) { // Note: always include acr and auth_time in the identity token as they must be flowed // from the authorization endpoint to the identity token returned from the token endpoint. case OpenIdConnectConstants.Claims.AuthenticationContextReference: case OpenIdConnectConstants.Claims.AuthenticationTime: yield return(OpenIdConnectConstants.Destinations.IdentityToken); yield break; // Note: the name claim is always included, even if // the profile scope is not requested nor granted. case OpenIdConnectConstants.Claims.Name: yield return(OpenIdConnectConstants.Destinations.AccessToken); yield return(OpenIdConnectConstants.Destinations.IdentityToken); yield break; case OpenIdConnectConstants.Claims.Subject: case OpenIdConnectConstants.Claims.Gender: case OpenIdConnectConstants.Claims.GivenName: case OpenIdConnectConstants.Claims.MiddleName: case OpenIdConnectConstants.Claims.FamilyName: case OpenIdConnectConstants.Claims.Nickname: case OpenIdConnectConstants.Claims.PreferredUsername: case OpenIdConnectConstants.Claims.Birthdate: case OpenIdConnectConstants.Claims.Profile: case OpenIdConnectConstants.Claims.Picture: case OpenIdConnectConstants.Claims.Website: case OpenIdConnectConstants.Claims.Locale: case OpenIdConnectConstants.Claims.Zoneinfo: case OpenIdConnectConstants.Claims.UpdatedAt: yield return(OpenIdConnectConstants.Destinations.AccessToken); if (ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) { yield return(OpenIdConnectConstants.Destinations.IdentityToken); } yield break; case OpenIdConnectConstants.Claims.Email: yield return(OpenIdConnectConstants.Destinations.AccessToken); if (ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) { yield return(OpenIdConnectConstants.Destinations.IdentityToken); } yield break; case OpenIdConnectConstants.Claims.PhoneNumber: yield return(OpenIdConnectConstants.Destinations.AccessToken); if (ticket.HasScope(OpenIdConnectConstants.Scopes.Phone)) { yield return(OpenIdConnectConstants.Destinations.IdentityToken); } yield break; case OpenIdConnectConstants.Claims.Address: yield return(OpenIdConnectConstants.Destinations.AccessToken); if (ticket.HasScope(OpenIdConnectConstants.Scopes.Address)) { yield return(OpenIdConnectConstants.Destinations.IdentityToken); } yield break; default: yield return(OpenIdConnectConstants.Destinations.AccessToken); yield break; } }
private async Task <bool> HandleSignInAsync(AuthenticationTicket ticket) { // Extract the OpenID Connect request from the ASP.NET context. // If it cannot be found or doesn't correspond to an authorization // or a token request, throw an InvalidOperationException. var request = Context.GetOpenIdConnectRequest(); if (request == null || (!request.IsAuthorizationRequest() && !request.IsTokenRequest())) { throw new InvalidOperationException("An OpenID Connect response cannot be returned from this endpoint."); } // Note: if an OpenID Connect response was already generated, throw an exception. var response = Context.GetOpenIdConnectResponse(); if (response != null) { throw new InvalidOperationException("An OpenID Connect response has already been sent."); } if (!ticket.Principal.HasClaim(claim => claim.Type == ClaimTypes.NameIdentifier)) { throw new InvalidOperationException("The authentication ticket was rejected because it didn't " + "contain the mandatory ClaimTypes.NameIdentifier claim."); } // Prepare a new OpenID Connect response. response = new OpenIdConnectResponse(); if (request.IsAuthorizationRequest()) { response.RedirectUri = request.RedirectUri; response.State = request.State; // Keep the code_challenge, code_challenge_method, nonce and redirect_uri parameters for later comparison. ticket.SetProperty(OpenIdConnectConstants.Properties.CodeChallenge, request.CodeChallenge); ticket.SetProperty(OpenIdConnectConstants.Properties.CodeChallengeMethod, request.CodeChallengeMethod); ticket.SetProperty(OpenIdConnectConstants.Properties.Nonce, request.Nonce); ticket.SetProperty(OpenIdConnectConstants.Properties.RedirectUri, request.RedirectUri); } // Copy the confidentiality level associated with the request to the authentication ticket. if (!ticket.HasProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel)) { ticket.SetProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel, request.GetProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel)); } // Always include the "openid" scope when the developer doesn't explicitly call SetScopes. // Note: the application is allowed to specify a different "scopes": in this case, // don't replace the "scopes" property stored in the authentication ticket. if (!ticket.HasProperty(OpenIdConnectConstants.Properties.Scopes) && request.HasScope(OpenIdConnectConstants.Scopes.OpenId)) { ticket.SetProperty(OpenIdConnectConstants.Properties.Scopes, OpenIdConnectConstants.Scopes.OpenId); } // When a "resources" property cannot be found in the ticket, infer it from the "audiences" property. if (!ticket.HasProperty(OpenIdConnectConstants.Properties.Resources)) { ticket.SetProperty(OpenIdConnectConstants.Properties.Resources, ticket.GetProperty(OpenIdConnectConstants.Properties.Audiences)); } // Only return an authorization code if the request is an authorization request and has response_type=code. if (request.IsAuthorizationRequest() && request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Code)) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); // properties.IssuedUtc and properties.ExpiresUtc are always // explicitly set to null to avoid aligning the expiration date // of the authorization code with the lifetime of the other tokens. properties.IssuedUtc = properties.ExpiresUtc = null; response.Code = await SerializeAuthorizationCodeAsync(ticket.Principal, properties, request, response); } // Only return an access token if the request is a token request // or an authorization request that specifies response_type=token. if (request.IsTokenRequest() || (request.IsAuthorizationRequest() && request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Token))) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); // When receiving a grant_type=refresh_token request, determine whether the client application // requests a limited set of scopes/resources and replace the corresponding properties if necessary. // Note: at this stage, request.GetResources() cannot return more items than the ones that were initially granted // by the resource owner as the "resources" parameter is always validated when receiving the token request. if (request.IsTokenRequest() && request.IsRefreshTokenGrantType()) { if (!string.IsNullOrEmpty(request.Resource)) { // Replace the resources initially granted by the resources listed by the client application in the token request. // Note: request.GetResources() automatically removes duplicate entries, so additional filtering is not necessary. properties.SetProperty(OpenIdConnectConstants.Properties.Resources, string.Join(" ", request.GetResources())); } if (!string.IsNullOrEmpty(request.Scope)) { // Replace the scopes initially granted by the scopes listed by the client application in the token request. // Note: request.GetScopes() automatically removes duplicate entries, so additional filtering is not necessary. properties.SetProperty(OpenIdConnectConstants.Properties.Scopes, string.Join(" ", request.GetScopes())); } } var resources = properties.GetProperty(OpenIdConnectConstants.Properties.Resources); if (request.IsAuthorizationCodeGrantType() || (!string.IsNullOrEmpty(resources) && !string.IsNullOrEmpty(request.Resource) && !string.Equals(request.Resource, resources, StringComparison.Ordinal))) { response.Resource = resources; } var scopes = properties.GetProperty(OpenIdConnectConstants.Properties.Scopes); if (request.IsAuthorizationCodeGrantType() || (!string.IsNullOrEmpty(scopes) && !string.IsNullOrEmpty(request.Scope) && !string.Equals(request.Scope, scopes, StringComparison.Ordinal))) { response.Scope = scopes; } response.TokenType = OpenIdConnectConstants.TokenTypes.Bearer; response.AccessToken = await SerializeAccessTokenAsync(ticket.Principal, properties, request, response); // properties.ExpiresUtc is automatically set by SerializeAccessTokenAsync but the end user // is free to set a null value directly in the SerializeAccessToken event. if (properties.ExpiresUtc.HasValue && properties.ExpiresUtc > Options.SystemClock.UtcNow) { var lifetime = properties.ExpiresUtc.Value - Options.SystemClock.UtcNow; response.ExpiresIn = (long)(lifetime.TotalSeconds + .5); } } // Only return a refresh token if the request is a token request that specifies scope=offline_access. if (request.IsTokenRequest() && ticket.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess)) { // Note: when sliding expiration is enabled, don't return a new refresh token, // unless the token request is not a grant_type=refresh_token request. if (!request.IsRefreshTokenGrantType() || Options.UseSlidingExpiration) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); response.RefreshToken = await SerializeRefreshTokenAsync(ticket.Principal, properties, request, response); } } // Only return an identity token if the openid scope was requested and granted // to avoid generating and returning an unnecessary token to pure OAuth2 clients. if (ticket.HasScope(OpenIdConnectConstants.Scopes.OpenId)) { // Note: don't return an identity token if the request is an // authorization request that doesn't use response_type=id_token. if (request.IsTokenRequest() || request.HasResponseType(OpenIdConnectConstants.ResponseTypes.IdToken)) { // Make sure to create a copy of the authentication properties // to avoid modifying the properties set on the original ticket. var properties = ticket.Properties.Copy(); // properties.IssuedUtc and properties.ExpiresUtc are always // explicitly set to null to avoid aligning the expiration date // of the identity token with the lifetime of the other tokens. properties.IssuedUtc = properties.ExpiresUtc = null; response.IdToken = await SerializeIdentityTokenAsync(ticket.Principal, properties, request, response); } } if (request.IsAuthorizationRequest()) { return(await SendAuthorizationResponseAsync(response, ticket)); } return(await SendTokenResponseAsync(response, ticket)); }
private async Task <AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, User user, AuthenticationProperties properties = null) { // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var principal = await _signInManager.CreateUserPrincipalAsync(user); var ticket = new AuthenticationTicket(principal, properties, OpenIdConnectServerDefaults.AuthenticationScheme); if (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType()) { // Set the list of scopes granted to the client application. // Note: the offline_access scope must be granted // to allow OpenIddict to return a refresh token. ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Profile, OpenIdConnectConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Roles }.Intersect(request.GetScopes())); } // Note: by default, claims are NOT automatically included in the access and identity tokens. // To allow OpenIddict to serialize them, you must attach them a destination, that specifies // whether they should be included in access tokens, in identity tokens or in both. foreach (var claim in principal.Claims) { if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) { continue; } var destinations = new List <string> { OpenIdConnectConstants.Destinations.AccessToken }; // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application. // The other claims will only be added to the access_token, which is encrypted when using the default format. if ((claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) || (claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) || (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles))) { destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken); } claim.SetDestinations(destinations); } ticket.Properties.AllowRefresh = true; // var roles = principal.Claims.Where(c => c.Type == ClaimTypes.Role).ToList(); // ticket.Properties.Items.Add(new KeyValuePair<string, string>("roles", string.Join(", ", roles.Select(r => r.Value)))); // ticket.Properties.ExpiresUtc = DateTimeOffset.Now.AddDays(14); // ticket.Properties.IsPersistent = false; await UpdateIpAddressForUser(user.Id); return(ticket); }