public async Task <IActionResult> Authorize(OpenIdConnectRequest request) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); // Retrieve the profile of the logged in user. //var user = await _userManager.GetUserAsync(User); var user = await GetCurrentUser(); if (user == null) { return(View("Error", new ErrorViewModel { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "An internal error has occurred" })); } // Create a new authentication ticket. var ticket = await CreateTicketAsync(request, user); // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); }
public async Task <IActionResult> Authorize(OpenIdConnectRequest request) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); // Retrieve the application details from the database. var application = await _applicationManager.FindByClientIdAsync(request.ClientId, HttpContext.RequestAborted); if (application == null) { return(new JsonResult(new { code = OpenIdConnectConstants.Errors.InvalidClient, message = "Details concerning the calling client application cannot be found in the database" })); } var user = await _userManager.GetUserAsync(User); if (user == null) { return(new JsonResult(new { code = OpenIdConnectConstants.Errors.ServerError, message = "An internal error has occurred" })); } // Create a new authentication ticket. var ticket = await CreateTicketAsync(request, user); // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); }
private async Task <IActionResult> Accept(OpenIdConnectRequest request) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); // Retrieve the profile of the logged in user. var user = await _userManager.GetUserAsync(User); if (user == null) { // Your user in the cookie is invalid, remove cookie and retry with Authorize await _signInManager.SignOutAsync(); var retryUrl = QueryHelpers.AddQueryString(Url.Action(nameof(Authorize)), new Dictionary <string, string>() { { "response_type", request.ResponseType }, { "client_id", request.ClientId }, { "scope", request.Scope }, { "nonce", request.Nonce }, { "state", request.State }, { "display", request.Display }, { "redirect_uri", request.RedirectUri } }); return(Redirect(retryUrl)); } // Create a new authentication ticket. var ticket = await CreateTicketAsync(request, user); // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); }
public async Task <IActionResult> Authorize(OpenIdConnectRequest request) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); // Retrieve the application details from the database. var application = await _applicationManager.FindByClientIdAsync(request.ClientId, HttpContext.RequestAborted); if (application == null) { return(View("Error", new ErrorViewModel { Error = OpenIdConnectConstants.Errors.InvalidClient, ErrorDescription = "Details concerning the calling client application cannot be found in the database" })); } // Flow the request_id to allow OpenIddict to restore // the original authorization request from the cache. return(View(new AuthzViewModel { ApplicationName = application.DisplayName, RequestId = request.RequestId, Scope = request.Scope, State = request.State })); }
private AuthenticationTicket CreateTicket( OpenIdConnectRequest request, AuthenticateResult result, AuthenticationProperties properties = null) { // Create a new ClaimsIdentity containing the claims that // will be used to create an id_token, a token or a code. var identity = new ClaimsIdentity( result.Principal.Claims, OpenIdConnectServerDefaults.AuthenticationScheme, OpenIdConnectConstants.Claims.Name, OpenIdConnectConstants.Claims.Role); // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket( new ClaimsPrincipal(identity), properties, OpenIdConnectServerDefaults.AuthenticationScheme); // Set the list of scopes granted to the client application. if (request.IsAuthorizationRequest() || (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType())) { ticket.SetScopes(new[] { OpenIdConnectConstants.Scopes.OfflineAccess, OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.Address, OpenIdConnectConstants.Scopes.Email, OpenIdConnectConstants.Scopes.Phone, OpenIdConnectConstants.Scopes.Profile }.Intersect(request.GetScopes())); } // The OP-Req-acr_values test consists in sending an "acr_values=1 2" parameter // as part of the authorization request. To indicate to the certification client // that the "1" reference value was satisfied, an "acr" claim is added. if (request.IsAuthorizationRequest() && request.HasAcrValue("1")) { identity.AddClaim(new Claim(OpenIdConnectConstants.Claims.AuthenticationContextReference, "1")); } foreach (var claim in identity.Claims) { claim.SetDestinations(destinations: GetDestinations(claim, ticket)); } return(ticket); }
public async Task <IActionResult> Accept(OpenIdConnectRequest request, CancellationToken cancellationToken) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); var application = await _applicationManager.FindByClientIdAsync(request.ClientId, HttpContext.RequestAborted); if (application == null) { return(View("Error", new ErrorViewModel { Error = OpenIdConnectConstants.Errors.InvalidClient, ErrorDescription = "Details concerning the calling client application cannot be found in the database" })); } // Retrieve the profile of the logged in user. var user = await _userManager.GetUserAsync(User); if (user == null) { return(View("Error", new ErrorViewModel { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "An internal error has occurred" })); } // Create a new authentication ticket. var ticket = await CreateTicketAsync(request, user); var authorization = await _authorizationManager.FindAsync(user.Id, application.Id, cancellationToken); if (authorization != null) { if (false == request.GetScopes().Except(authorization.Scopes).Any()) { authorization.Scopes = authorization.Scopes.Union(request.GetScopes()).ToList(); await _authorizationManager.UpdateAsync(authorization, cancellationToken); } } else { authorization = new DynamoIdentityAuthorization() { Application = application.Id, Subject = user.Id, Scopes = ticket.GetScopes().ToList() }; await _authorizationManager.CreateAsync(authorization, cancellationToken); } // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); }
public async Task <IActionResult> Authorize(OpenIdConnectRequest request, CancellationToken cancellationToken) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); // Retrieve the application details from the database. var application = await _applicationManager.FindByClientIdAsync(request.ClientId, HttpContext.RequestAborted); if (application == null) { return(View("Error", new ErrorViewModel { Error = OpenIdConnectConstants.Errors.InvalidClient, ErrorDescription = "Details concerning the calling client application cannot be found in the database" })); } var user = await _userManager.GetUserAsync(User); if (user == null) { return(View("Error", new ErrorViewModel { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "An internal error has occurred" })); } var authorization = await _authorizationManager.FindAsync(user.Id, application.Id, cancellationToken); if (authorization != null) { // if we didn't ask for any scopes that aren't already authorized if (false == request.GetScopes().Except(authorization.Scopes).Any()) { var ticket = await CreateTicketAsync(request, user); return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); } } // Flow the request_id to allow OpenIddict to restore // the original authorization request from the cache. return(View(new AuthorizeViewModel { ApplicationName = application.DisplayName, RequestId = request.RequestId, Scope = request.Scope, ClientId = request.ClientId, ResponseType = request.ResponseType, ResponseMode = request.ResponseMode, RedirectUri = request.RedirectUri, State = request.State, Nonce = request.Nonce })); }
public void IsAuthorizationRequest_ReturnsExpectedResult(string type, bool result) { // Arrange var request = new OpenIdConnectRequest(); request.SetProperty(OpenIdConnectConstants.Properties.MessageType, type); // Act and assert Assert.Equal(result, request.IsAuthorizationRequest()); }
public async Task <IActionResult> Authorize(OpenIdConnectRequest request) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); // Retrieve the application details from the database. var application = await _applicationManager.FindByClientIdAsync(request.ClientId, HttpContext.RequestAborted); if (application == null) { return(View("Error", new ErrorViewModel { Error = OpenIdConnectConstants.Errors.InvalidClient, ErrorDescription = "Details concerning the calling client application cannot be found in the database" })); } var user = await _userManager.GetUserAsync(User); if (user == null) { return(View("Error", new ErrorViewModel { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "An internal error has occurred" })); } if (user.IsDeleted) { return(View("Error", new ErrorViewModel { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "An internal error has occurred" })); } // Create a new authentication ticket. var ticket = await CreateTicketAsync(request, user); // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); // Flow the request_id to allow OpenIddict to restore // the original authorization request from the cache. //return View(new AuthorizeViewModel //{ // ApplicationName = application.DisplayName, // RequestId = request.RequestId, // Scope = request.Scope //}); }
public async Task <IActionResult> Authorize(OpenIdConnectRequest request) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); if (!User.Identity.IsAuthenticated) { // If the client application request promptless authentication, // return an error indicating that the user is not logged in. if (request.HasPrompt(OpenIdConnectConstants.Prompts.None)) { var properties = new AuthenticationProperties(new Dictionary <string, string> { [OpenIdConnectConstants.Properties.Error] = OpenIdConnectConstants.Errors.LoginRequired, [OpenIdConnectConstants.Properties.ErrorDescription] = "The user is not logged in." }); // Ask OpenIddict to return a login_required error to the client application. return(Forbid(properties, OpenIdConnectServerDefaults.AuthenticationScheme)); } return(Challenge()); } // Retrieve the application details from the database. var application = await applicationManager.FindByClientIdAsync(request.ClientId, HttpContext.RequestAborted); if (application == null) { return(View("Error", new ErrorViewModel { Error = OpenIdConnectConstants.Errors.InvalidClient, ErrorDescription = "Details concerning the calling client application cannot be found in the database." })); } // Flow the request_id to allow OpenIddict to restore // the original authorization request from the cache. return(View(new AuthorizeViewModel { ApplicationName = application.DisplayName, RequestId = request.RequestId, Scope = request.Scope })); }
public async Task <IActionResult> Authorize(OpenIdConnectRequest request) { if (request == null) { throw new System.ArgumentNullException(nameof(request)); } Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); // Check if a user is authenticated. If not, challenge the GitHub authentication handler if (!User.Identity.IsAuthenticated) { return(Challenge("Google")); } var response = await _bus.RequestAsync <GetOrCreateUserRequest, GetOrCreateUserResponse>(new GetOrCreateUserRequest(User.FindFirstValue(ClaimTypes.Email))); var userId = response.Id; // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var identity = new ClaimsIdentity("OpenIddict"); identity.AddClaim(OpenIdConnectConstants.Claims.Subject, userId.ToString(), OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken); identity.AddClaim(OpenIdConnectConstants.Claims.Name, User.FindFirstValue(ClaimTypes.Name), OpenIdConnectConstants.Destinations.IdentityToken); identity.AddClaim(OpenIdConnectConstants.Claims.Email, User.FindFirstValue(ClaimTypes.Email), OpenIdConnectConstants.Destinations.IdentityToken); identity.AddClaim(OpenIdConnectConstants.Claims.EmailVerified, "true", OpenIdConnectConstants.Destinations.IdentityToken); // We'll assume email is verified since we get it from GitHub var principal = new ClaimsPrincipal(identity); // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), OpenIdConnectServerDefaults.AuthenticationScheme); ticket.SetIdentityTokenLifetime(TimeSpan.FromDays(1)); // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); }
public async Task <IActionResult> Authorize(OpenIdConnectRequest request) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddServer().UseMvc() is correctly called."); // Retrieve the application details from the database. var application = await _applicationManager.FindByClientIdAsync(request.ClientId); if (application == null) { return(BadRequest("Details concerning the calling client application cannot be found in the database")); } // Flow the request_id to allow OpenIddict to restore // the original authorization request from the cache. return(Redirect($"/Login?ApplicationName={await _applicationManager.GetDisplayNameAsync(application)}&RequestId={request.RequestId}&Scope={request.Scope}")); }
public async Task <IActionResult> Authorize(OpenIdConnectRequest request) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); if (!User.Identity.IsAuthenticated) { // If the client application request promptless authentication, // return an error indicating that the user is not logged in. if (request.HasPrompt(OpenIdConnectConstants.Prompts.None)) { var properties = new AuthenticationProperties(new Dictionary <string, string> { [OpenIdConnectConstants.Properties.Error] = OpenIdConnectConstants.Errors.LoginRequired, [OpenIdConnectConstants.Properties.ErrorDescription] = "The user is not logged in." }); // Ask OpenIddict to return a login_required error to the client application. return(Forbid(properties, OpenIdConnectServerDefaults.AuthenticationScheme)); } return(Challenge()); } // Retrieve the profile of the logged in user. var user = await _userManager.GetUserAsync(User); if (user == null) { return(BadRequest(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "The username/password couple is invalid." })); } // Create a new authentication ticket. var ticket = await CreateTicketAsync(request, user); // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); }
public async Task <IActionResult> Authorize(OpenIdConnectRequest request) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); // Retrieve the application details from the database. var application = await _applicationManager.FindByClientIdAsync(request.ClientId, HttpContext.RequestAborted); if (application == null) { return(View("Error", new ErrorViewModel { Error = OpenIdConnectConstants.Errors.InvalidClient, ErrorDescription = "Details concerning the calling client application cannot be found in the database" })); } return(await Accept(request)); }
public async Task <IActionResult> Accept(OpenIdConnectRequest request) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddServer().UseMvc() is correctly called."); // Retrieve the profile of the logged in user. var user = await _userManager.FindByEmailAsync(request.Username); await _signInManager.PasswordSignInAsync(user, request.Password, true, false); if (user == null) { return(BadRequest("User not found.")); } // Create a new authentication ticket. var ticket = await CreateTicketAsync(request, user); // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); }
public async Task <IActionResult> Authorize(OpenIdConnectRequest request) { Debug.Assert(request.IsAuthorizationRequest(), "The OpenIddict binder for ASP.NET Core MVC is not registered. " + "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called."); // Check if a user is authenticated. If not, challenge the GitHub authentication handler if (!User.Identity.IsAuthenticated) { return(Challenge("GitHub")); } // Create a new ClaimsPrincipal containing the claims that // will be used to create an id_token, a token or a code. var identity = new ClaimsIdentity("OpenIddict"); identity.AddClaim(OpenIdConnectConstants.Claims.Subject, User.FindFirstValue(ClaimTypes.NameIdentifier), OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken); identity.AddClaim(OpenIdConnectConstants.Claims.Name, User.FindFirstValue(ClaimTypes.Name), OpenIdConnectConstants.Destinations.IdentityToken); identity.AddClaim(OpenIdConnectConstants.Claims.Email, User.FindFirstValue(ClaimTypes.Email), OpenIdConnectConstants.Destinations.IdentityToken); identity.AddClaim(OpenIdConnectConstants.Claims.EmailVerified, "true", OpenIdConnectConstants.Destinations.IdentityToken); // We'll assume email is verified since we get it from GitHub identity.AddClaim(OpenIdConnectConstants.Claims.Picture, User.FindFirstValue("github:avatar"), OpenIdConnectConstants.Destinations.IdentityToken); var principal = new ClaimsPrincipal(identity); // Create a new authentication ticket holding the user identity. var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), OpenIdConnectServerDefaults.AuthenticationScheme); // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens. return(SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme)); }
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 property, string parameter, OpenIdConnectParameter value)> GetParameters( OpenIdConnectRequest request, AuthenticationProperties properties) { Debug.Assert(properties != null, "The authentication properties shouldn't be null."); Debug.Assert(request != null, "The request shouldn't be null."); Debug.Assert(request.IsAuthorizationRequest() || request.IsLogoutRequest() || request.IsTokenRequest(), "The request should be an authorization, logout or token request."); foreach (var property in properties.Items) { if (string.IsNullOrEmpty(property.Key)) { continue; } if (string.IsNullOrEmpty(property.Value)) { continue; } if (property.Key.EndsWith(OpenIddictConstants.PropertyTypes.Boolean)) { var name = property.Key.Substring( /* index: */ 0, /* length: */ property.Key.LastIndexOf(OpenIddictConstants.PropertyTypes.Boolean)); bool value; try { value = bool.Parse(property.Value); } catch (Exception exception) { Logger.LogWarning(exception, "An error occurred while parsing the public property " + "'{Name}' from the authentication ticket.", name); continue; } yield return(property.Key, name, value); } else if (property.Key.EndsWith(OpenIddictConstants.PropertyTypes.Integer)) { var name = property.Key.Substring( /* index: */ 0, /* length: */ property.Key.LastIndexOf(OpenIddictConstants.PropertyTypes.Integer)); long value; try { value = long.Parse(property.Value, CultureInfo.InvariantCulture); } catch (Exception exception) { Logger.LogWarning(exception, "An error occurred while parsing the public property " + "'{Name}' from the authentication ticket.", name); continue; } yield return(property.Key, name, value); } else if (property.Key.EndsWith(OpenIddictConstants.PropertyTypes.Json)) { var name = property.Key.Substring( /* index: */ 0, /* length: */ property.Key.LastIndexOf(OpenIddictConstants.PropertyTypes.Json)); if (request.IsAuthorizationRequest() || request.IsLogoutRequest()) { Logger.LogWarning("The JSON property '{Name}' was excluded as it was not " + "compatible with the OpenID Connect response type.", name); continue; } JToken value; try { value = JToken.Parse(property.Value); } catch (Exception exception) { Logger.LogWarning(exception, "An error occurred while deserializing the public JSON " + "property '{Name}' from the authentication ticket.", name); continue; } yield return(property.Key, name, value); } else if (property.Key.EndsWith(OpenIddictConstants.PropertyTypes.String)) { var name = property.Key.Substring( /* index: */ 0, /* length: */ property.Key.LastIndexOf(OpenIddictConstants.PropertyTypes.String)); yield return(property.Key, name, property.Value); } continue; } }
private async Task <string> SerializeIdentityTokenAsync( ClaimsPrincipal principal, AuthenticationProperties properties, OpenIdConnectRequest request, OpenIdConnectResponse response) { // Replace the principal by a new one containing only the filtered claims. // Actors identities are also filtered (delegation scenarios). principal = principal.Clone(claim => { // Never exclude the subject claim. if (string.Equals(claim.Type, OpenIdConnectConstants.Claims.Subject, StringComparison.OrdinalIgnoreCase)) { return(true); } // Claims whose destination is not explicitly referenced or doesn't // contain "id_token" are not included in the identity token. if (!claim.HasDestination(OpenIdConnectConstants.Destinations.IdentityToken)) { Logger.LogDebug("'{Claim}' was excluded from the identity token claims.", claim.Type); return(false); } return(true); }); // Remove the destinations from the claim properties. foreach (var claim in principal.Claims) { claim.Properties.Remove(OpenIdConnectConstants.Properties.Destinations); } var identity = (ClaimsIdentity)principal.Identity; // Create a new ticket containing the updated properties and the filtered principal. var ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme); ticket.Properties.IssuedUtc = Options.SystemClock.UtcNow; ticket.Properties.ExpiresUtc = ticket.Properties.IssuedUtc + (ticket.GetIdentityTokenLifetime() ?? Options.IdentityTokenLifetime); ticket.SetUsage(OpenIdConnectConstants.Usages.IdentityToken); // Associate a random identifier with the identity token. ticket.SetTicketId(Guid.NewGuid().ToString()); // Remove the unwanted properties from the authentication ticket. ticket.RemoveProperty(OpenIdConnectConstants.Properties.AccessTokenLifetime) .RemoveProperty(OpenIdConnectConstants.Properties.AuthorizationCodeLifetime) .RemoveProperty(OpenIdConnectConstants.Properties.ClientId) .RemoveProperty(OpenIdConnectConstants.Properties.CodeChallenge) .RemoveProperty(OpenIdConnectConstants.Properties.CodeChallengeMethod) .RemoveProperty(OpenIdConnectConstants.Properties.IdentityTokenLifetime) .RemoveProperty(OpenIdConnectConstants.Properties.RedirectUri) .RemoveProperty(OpenIdConnectConstants.Properties.RefreshTokenLifetime); ticket.SetAudiences(ticket.GetPresenters()); var notification = new SerializeIdentityTokenContext(Context, Options, request, response, ticket) { Issuer = Context.GetIssuer(Options), SecurityTokenHandler = Options.IdentityTokenHandler, SigningCredentials = Options.SigningCredentials.FirstOrDefault(key => key.Key is AsymmetricSecurityKey) }; await Options.Provider.SerializeIdentityToken(notification); if (notification.HandledResponse || !string.IsNullOrEmpty(notification.IdentityToken)) { return(notification.IdentityToken); } else if (notification.Skipped) { return(null); } if (!ReferenceEquals(ticket, notification.Ticket)) { throw new InvalidOperationException("The authentication ticket cannot be replaced."); } if (notification.SecurityTokenHandler == null) { return(null); } // Extract the main identity from the principal. identity = (ClaimsIdentity)ticket.Principal.Identity; if (string.IsNullOrEmpty(identity.GetClaim(OpenIdConnectConstants.Claims.Subject))) { throw new InvalidOperationException("The authentication ticket was rejected because " + "it doesn't contain the mandatory subject claim."); } // Note: identity tokens must be signed but an exception is made by the OpenID Connect specification // when they are returned from the token endpoint: in this case, signing is not mandatory, as the TLS // server validation can be used as a way to ensure an identity token was issued by a trusted party. // See http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation for more information. if (notification.SigningCredentials == null && request.IsAuthorizationRequest()) { throw new InvalidOperationException("A signing key must be provided."); } // Store the "unique_id" property as a claim. identity.AddClaim(OpenIdConnectConstants.Claims.JwtId, ticket.GetTicketId()); // Store the "usage" property as a claim. identity.AddClaim(OpenIdConnectConstants.Claims.Usage, ticket.GetUsage()); // Store the "confidentiality_level" property as a claim. var confidentiality = ticket.GetProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel); if (!string.IsNullOrEmpty(confidentiality)) { identity.AddClaim(OpenIdConnectConstants.Claims.ConfidentialityLevel, confidentiality); } // Store the audiences as claims. foreach (var audience in notification.Audiences) { identity.AddClaim(OpenIdConnectConstants.Claims.Audience, audience); } // If a nonce was present in the authorization request, it MUST // be included in the id_token generated by the token endpoint. // See http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation var nonce = request.Nonce; if (request.IsAuthorizationCodeGrantType()) { // Restore the nonce stored in the authentication // ticket extracted from the authorization code. nonce = ticket.GetProperty(OpenIdConnectConstants.Properties.Nonce); } if (!string.IsNullOrEmpty(nonce)) { identity.AddClaim(OpenIdConnectConstants.Claims.Nonce, nonce); } if (notification.SigningCredentials != null && (!string.IsNullOrEmpty(response.Code) || !string.IsNullOrEmpty(response.AccessToken))) { using (var algorithm = OpenIdConnectServerHelpers.GetHashAlgorithm(notification.SigningCredentials.Algorithm)) { // Create an authorization code hash if necessary. if (!string.IsNullOrEmpty(response.Code)) { var hash = algorithm.ComputeHash(Encoding.ASCII.GetBytes(response.Code)); // Note: only the left-most half of the hash of the octets is used. // See http://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken identity.AddClaim(OpenIdConnectConstants.Claims.CodeHash, Base64UrlEncoder.Encode(hash, 0, hash.Length / 2)); } // Create an access token hash if necessary. if (!string.IsNullOrEmpty(response.AccessToken)) { var hash = algorithm.ComputeHash(Encoding.ASCII.GetBytes(response.AccessToken)); // Note: only the left-most half of the hash of the octets is used. // See http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken identity.AddClaim(OpenIdConnectConstants.Claims.AccessTokenHash, Base64UrlEncoder.Encode(hash, 0, hash.Length / 2)); } } } // Extract the presenters from the authentication ticket. var presenters = notification.Presenters.ToArray(); switch (presenters.Length) { case 0: break; case 1: identity.AddClaim(OpenIdConnectConstants.Claims.AuthorizedParty, presenters[0]); break; default: Logger.LogWarning("Multiple presenters have been associated with the identity token " + "but the JWT format only accepts single values."); // Only add the first authorized party. identity.AddClaim(OpenIdConnectConstants.Claims.AuthorizedParty, presenters[0]); break; } var token = notification.SecurityTokenHandler.CreateToken(new SecurityTokenDescriptor { Subject = identity, Issuer = notification.Issuer, SigningCredentials = notification.SigningCredentials, IssuedAt = notification.Ticket.Properties.IssuedUtc?.UtcDateTime, NotBefore = notification.Ticket.Properties.IssuedUtc?.UtcDateTime, Expires = notification.Ticket.Properties.ExpiresUtc?.UtcDateTime }); return(notification.SecurityTokenHandler.WriteToken(token)); }