private async Task <bool> InvokeUserinfoEndpointAsync() { OpenIdConnectRequest request; if (string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase)) { request = new OpenIdConnectRequest(Request.Query); } else if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) { // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization if (string.IsNullOrWhiteSpace(Request.ContentType)) { Logger.LogError("The userinfo request was rejected because " + "the mandatory 'Content-Type' header was missing."); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed userinfo 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 userinfo request was rejected because an invalid 'Content-Type' " + "header was received: {ContentType}.", Request.ContentType); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed userinfo request has been received: " + "the 'Content-Type' header contained an unexcepted value. " + "Make sure to use 'application/x-www-form-urlencoded'." })); } request = new OpenIdConnectRequest(await Request.ReadFormAsync(Context.RequestAborted)); } else { Logger.LogError("The userinfo request was rejected because an invalid " + "HTTP method was received: {Method}.", Request.Method); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed userinfo request has been received: " + "make sure to use either GET or POST." })); } // Note: set the message type before invoking the ExtractUserinfoRequest event. request.SetProperty(OpenIdConnectConstants.Properties.MessageType, OpenIdConnectConstants.MessageTypes.Userinfo); // Insert the userinfo request in the ASP.NET context. Context.SetOpenIdConnectRequest(request); var @event = new ExtractUserinfoRequestContext(Context, Options, request); await Options.Provider.ExtractUserinfoRequest(@event); if (@event.HandledResponse) { return(true); } else if (@event.Skipped) { return(false); } else if (@event.IsRejected) { Logger.LogError("The userinfo request was rejected with the following error: {Error} ; {Description}", /* Error: */ @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, /* Description: */ @event.ErrorDescription); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = @event.ErrorDescription, ErrorUri = @event.ErrorUri })); } string token; if (!string.IsNullOrEmpty(request.AccessToken)) { token = request.AccessToken; } else { string header = Request.Headers[HeaderNames.Authorization]; if (string.IsNullOrEmpty(header)) { Logger.LogError("The userinfo request was rejected because " + "the 'Authorization' header was missing."); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed userinfo request has been received." })); } if (!header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { Logger.LogError("The userinfo request was rejected because the " + "'Authorization' header was invalid: {Header}.", header); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed userinfo request has been received." })); } token = header.Substring("Bearer ".Length); if (string.IsNullOrEmpty(token)) { Logger.LogError("The userinfo request was rejected because the access token was missing."); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed userinfo request has been received." })); } } var context = new ValidateUserinfoRequestContext(Context, Options, request); await Options.Provider.ValidateUserinfoRequest(context); if (context.HandledResponse) { return(true); } else if (context.Skipped) { return(false); } else if (!context.IsValidated) { Logger.LogError("The userinfo request was rejected with the following error: {Error} ; {Description}", /* Error: */ context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, /* Description: */ context.ErrorDescription); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = context.ErrorDescription, ErrorUri = context.ErrorUri })); } var ticket = await DeserializeAccessTokenAsync(token, request); if (ticket == null) { Logger.LogError("The userinfo request was rejected because the access token was invalid."); // Note: an invalid token should result in an unauthorized response // but returning a 401 status would invoke the previously registered // authentication middleware and potentially replace it by a 302 response. // To work around this limitation, a 400 error is returned instead. // See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoError return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Invalid token." })); } if (ticket.Properties.ExpiresUtc.HasValue && ticket.Properties.ExpiresUtc < Options.SystemClock.UtcNow) { Logger.LogError("The userinfo request was rejected because the access token was expired."); // Note: an invalid token should result in an unauthorized response // but returning a 401 status would invoke the previously registered // authentication middleware and potentially replace it by a 302 response. // To work around this limitation, a 400 error is returned instead. // See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoError return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Expired token." })); } var notification = new HandleUserinfoRequestContext(Context, Options, request, ticket); notification.Subject = ticket.Principal.GetClaim(ClaimTypes.NameIdentifier); notification.Issuer = Context.GetIssuer(Options); // Note: when receiving an access token, its audiences list cannot be used for the "aud" claim // as the client application is not the intented audience but only an authorized presenter. // See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse foreach (var presenter in ticket.GetPresenters()) { notification.Audiences.Add(presenter); } // The following claims are all optional and should be excluded when // no corresponding value has been found in the authentication ticket. if (ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) { notification.FamilyName = ticket.Principal.GetClaim(ClaimTypes.Surname); notification.GivenName = ticket.Principal.GetClaim(ClaimTypes.GivenName); notification.BirthDate = ticket.Principal.GetClaim(ClaimTypes.DateOfBirth); } if (ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) { notification.Email = ticket.Principal.GetClaim(ClaimTypes.Email); } ; if (ticket.HasScope(OpenIdConnectConstants.Scopes.Phone)) { notification.PhoneNumber = ticket.Principal.GetClaim(ClaimTypes.HomePhone) ?? ticket.Principal.GetClaim(ClaimTypes.MobilePhone) ?? ticket.Principal.GetClaim(ClaimTypes.OtherPhone); } ; await Options.Provider.HandleUserinfoRequest(notification); if (notification.HandledResponse) { return(true); } else if (notification.Skipped) { return(false); } else if (notification.IsRejected) { Logger.LogError("The userinfo request was rejected with the following error: {Error} ; {Description}", /* Error: */ notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, /* Description: */ notification.ErrorDescription); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = notification.ErrorDescription, ErrorUri = notification.ErrorUri })); } // Ensure the "sub" claim has been correctly populated. if (string.IsNullOrEmpty(notification.Subject)) { Logger.LogError("The mandatory 'sub' claim was missing from the userinfo response."); Response.StatusCode = 500; return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "The mandatory 'sub' claim was missing." })); } return(await SendUserinfoResponseAsync(new OpenIdConnectResponse(notification.Claims))); }
private async Task <bool> InvokeUserinfoEndpointAsync() { OpenIdConnectRequest request; if (HttpMethods.IsGet(Request.Method)) { request = new OpenIdConnectRequest(Request.Query); } else if (HttpMethods.IsPost(Request.Method)) { // Note: if no Content-Type header was specified, assume the userinfo request // doesn't contain any parameter and create an empty OpenIdConnectRequest. if (string.IsNullOrEmpty(Request.ContentType)) { request = new OpenIdConnectRequest(); } else { // May have media/type; charset=utf-8, allow partial match. if (!Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)) { Logger.LogError("The userinfo request was rejected because an invalid 'Content-Type' " + "header was specified: {ContentType}.", Request.ContentType); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The specified 'Content-Type' header is not valid." })); } request = new OpenIdConnectRequest(await Request.ReadFormAsync()); } } else { Logger.LogError("The userinfo request was rejected because an invalid " + "HTTP method was specified: {Method}.", Request.Method); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The specified HTTP method is not valid." })); } // Note: set the message type before invoking the ExtractUserinfoRequest event. request.SetProperty(OpenIdConnectConstants.Properties.MessageType, OpenIdConnectConstants.MessageTypes.UserinfoRequest); // Insert the userinfo request in the ASP.NET context. Context.SetOpenIdConnectRequest(request); var @event = new ExtractUserinfoRequestContext(Context, Scheme, Options, request); await Provider.ExtractUserinfoRequest(@event); if (@event.Result != null) { if (@event.Result.Handled) { Logger.LogDebug("The userinfo request was handled in user code."); return(true); } else if (@event.Result.Skipped) { Logger.LogDebug("The default userinfo request handling was skipped from user code."); return(false); } } else if (@event.IsRejected) { Logger.LogError("The userinfo request was rejected with the following error: {Error} ; {Description}", /* Error: */ @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, /* Description: */ @event.ErrorDescription); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = @event.ErrorDescription, ErrorUri = @event.ErrorUri })); } Logger.LogInformation("The userinfo request was successfully extracted " + "from the HTTP request: {Request}.", request); string token = null; if (!string.IsNullOrEmpty(request.AccessToken)) { token = request.AccessToken; } else { string header = Request.Headers[HeaderNames.Authorization]; if (!string.IsNullOrEmpty(header)) { if (!header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { Logger.LogError("The userinfo request was rejected because the " + "'Authorization' header was invalid: {Header}.", header); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The specified 'Authorization' header is invalid." })); } token = header.Substring("Bearer ".Length); } } if (string.IsNullOrEmpty(token)) { Logger.LogError("The userinfo request was rejected because the access token was missing."); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The mandatory 'access_token' parameter is missing." })); } var context = new ValidateUserinfoRequestContext(Context, Scheme, Options, request); await Provider.ValidateUserinfoRequest(context); if (context.Result != null) { if (context.Result.Handled) { Logger.LogDebug("The userinfo request was handled in user code."); return(true); } else if (context.Result.Skipped) { Logger.LogDebug("The default userinfo request handling was skipped from user code."); return(false); } } else if (context.IsRejected) { Logger.LogError("The userinfo request was rejected with the following error: {Error} ; {Description}", /* Error: */ context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, /* Description: */ context.ErrorDescription); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = context.ErrorDescription, ErrorUri = context.ErrorUri })); } Logger.LogInformation("The userinfo request was successfully validated."); var ticket = await DeserializeAccessTokenAsync(token, request); if (ticket == null) { Logger.LogError("The userinfo request was rejected because the access token was invalid."); // Note: an invalid token should result in an unauthorized response // but returning a 401 status would invoke the previously registered // authentication middleware and potentially replace it by a 302 response. // To work around this limitation, a 400 error is returned instead. // See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoError return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "The specified access token is not valid." })); } if (ticket.Properties.ExpiresUtc.HasValue && ticket.Properties.ExpiresUtc < Options.SystemClock.UtcNow) { Logger.LogError("The userinfo request was rejected because the access token was expired."); // Note: an invalid token should result in an unauthorized response // but returning a 401 status would invoke the previously registered // authentication middleware and potentially replace it by a 302 response. // To work around this limitation, a 400 error is returned instead. // See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoError return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "The specified access token is no longer valid." })); } var notification = new HandleUserinfoRequestContext(Context, Scheme, Options, request, ticket) { Issuer = Context.GetIssuer(Options), Subject = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Subject) }; // Note: when receiving an access token, its audiences list cannot be used for the "aud" claim // as the client application is not the intented audience but only an authorized presenter. // See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse notification.Audiences.UnionWith(ticket.GetPresenters()); // The following claims are all optional and should be excluded when // no corresponding value has been found in the authentication ticket. if (ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) { notification.FamilyName = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.FamilyName); notification.GivenName = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.GivenName); notification.BirthDate = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Birthdate); } if (ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) { notification.Email = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Email); } if (ticket.HasScope(OpenIdConnectConstants.Scopes.Phone)) { notification.PhoneNumber = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.PhoneNumber); } await Provider.HandleUserinfoRequest(notification); if (notification.Result != null) { if (notification.Result.Handled) { Logger.LogDebug("The userinfo request was handled in user code."); return(true); } else if (notification.Result.Skipped) { Logger.LogDebug("The default userinfo request handling was skipped from user code."); return(false); } } else if (notification.IsRejected) { Logger.LogError("The userinfo request was rejected with the following error: {Error} ; {Description}", /* Error: */ notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, /* Description: */ notification.ErrorDescription); return(await SendUserinfoResponseAsync(new OpenIdConnectResponse { Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = notification.ErrorDescription, ErrorUri = notification.ErrorUri })); } // Ensure the "sub" claim has been correctly populated. if (string.IsNullOrEmpty(notification.Subject)) { throw new InvalidOperationException("The subject claim cannot be null or empty."); } var response = new OpenIdConnectResponse { [OpenIdConnectConstants.Claims.Subject] = notification.Subject, [OpenIdConnectConstants.Claims.Address] = notification.Address, [OpenIdConnectConstants.Claims.Birthdate] = notification.BirthDate, [OpenIdConnectConstants.Claims.Email] = notification.Email, [OpenIdConnectConstants.Claims.EmailVerified] = notification.EmailVerified, [OpenIdConnectConstants.Claims.FamilyName] = notification.FamilyName, [OpenIdConnectConstants.Claims.GivenName] = notification.GivenName, [OpenIdConnectConstants.Claims.Issuer] = notification.Issuer, [OpenIdConnectConstants.Claims.PhoneNumber] = notification.PhoneNumber, [OpenIdConnectConstants.Claims.PhoneNumberVerified] = notification.PhoneNumberVerified, [OpenIdConnectConstants.Claims.PreferredUsername] = notification.PreferredUsername, [OpenIdConnectConstants.Claims.Profile] = notification.Profile, [OpenIdConnectConstants.Claims.Website] = notification.Website }; switch (notification.Audiences.Count) { case 0: break; case 1: response[OpenIdConnectConstants.Claims.Audience] = notification.Audiences.ElementAt(0); break; default: response[OpenIdConnectConstants.Claims.Audience] = new JArray(notification.Audiences); break; } foreach (var claim in notification.Claims) { response.SetParameter(claim.Key, claim.Value); } return(await SendUserinfoResponseAsync(response)); }
/// <summary> /// Called for each request to the userinfo endpoint to determine if the request is valid and should continue. /// </summary> /// <param name="context">The context of the event carries information in and results out.</param> /// <returns>Task to enable asynchronous execution</returns> public virtual Task ValidateUserinfoRequest(ValidateUserinfoRequestContext context) => OnValidateUserinfoRequest(context);
private async Task <bool> InvokeUserinfoEndpointAsync() { OpenIdConnectMessage request; if (string.Equals(Request.Method, "GET", StringComparison.OrdinalIgnoreCase)) { request = new OpenIdConnectMessage(Request.Query.ToDictionary()); } else if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) { // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization if (string.IsNullOrWhiteSpace(Request.ContentType)) { Logger.LogError("The userinfo request was rejected because " + "the mandatory 'Content-Type' header was missing."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed userinfo 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 userinfo 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 userinfo 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); request = new OpenIdConnectMessage(form.ToDictionary()); } else { Logger.LogError("The userinfo request was rejected because an invalid " + "HTTP method was received: {Method}.", Request.Method); return(await SendErrorPageAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed userinfo request has been received: " + "make sure to use either GET or POST." })); } // Insert the userinfo request in the ASP.NET context. Context.SetOpenIdConnectRequest(request); string token; if (!string.IsNullOrEmpty(request.AccessToken)) { token = request.AccessToken; } else { string header = Request.Headers[HeaderNames.Authorization]; if (string.IsNullOrEmpty(header)) { Logger.LogError("The userinfo request was rejected because " + "the 'Authorization' header was missing."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed userinfo request has been received." })); } if (!header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { Logger.LogError("The userinfo request was rejected because the " + "'Authorization' header was invalid: {Header}.", header); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed userinfo request has been received." })); } token = header.Substring("Bearer ".Length); if (string.IsNullOrEmpty(token)) { Logger.LogError("The userinfo request was rejected because the access token was missing."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed userinfo request has been received." })); } } var ticket = await DeserializeAccessTokenAsync(token, request); if (ticket == null) { Logger.LogError("The userinfo request was rejected because access token was invalid."); // Note: an invalid token should result in an unauthorized response // but returning a 401 status would invoke the previously registered // authentication middleware and potentially replace it by a 302 response. // To work around this limitation, a 400 error is returned instead. // See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoError return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Invalid token." })); } if (!ticket.Properties.ExpiresUtc.HasValue || ticket.Properties.ExpiresUtc < Options.SystemClock.UtcNow) { Logger.LogError("The userinfo request was rejected because access token was expired."); // Note: an invalid token should result in an unauthorized response // but returning a 401 status would invoke the previously registered // authentication middleware and potentially replace it by a 302 response. // To work around this limitation, a 400 error is returned instead. // See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoError return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Expired token." })); } var validatingContext = new ValidateUserinfoRequestContext(Context, Options, request); await Options.Provider.ValidateUserinfoRequest(validatingContext); if (!validatingContext.IsValidated) { Logger.LogInformation("The userinfo request was rejected by application code."); return(await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = validatingContext.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = validatingContext.ErrorDescription, ErrorUri = validatingContext.ErrorUri })); } var notification = new HandleUserinfoRequestContext(Context, Options, request, ticket); notification.Subject = ticket.Principal.GetClaim(ClaimTypes.NameIdentifier); notification.Issuer = Context.GetIssuer(Options); // Note: when receiving an access token, its audiences list cannot be used for the "aud" claim // as the client application is not the intented audience but only an authorized presenter. // See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse foreach (var presenter in ticket.GetPresenters()) { notification.Audiences.Add(presenter); } // The following claims are all optional and should be excluded when // no corresponding value has been found in the authentication ticket. if (ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) { notification.FamilyName = ticket.Principal.GetClaim(ClaimTypes.Surname); notification.GivenName = ticket.Principal.GetClaim(ClaimTypes.GivenName); notification.BirthDate = ticket.Principal.GetClaim(ClaimTypes.DateOfBirth); } if (ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) { notification.Email = ticket.Principal.GetClaim(ClaimTypes.Email); } ; if (ticket.HasScope(OpenIdConnectConstants.Scopes.Phone)) { notification.PhoneNumber = ticket.Principal.GetClaim(ClaimTypes.HomePhone) ?? ticket.Principal.GetClaim(ClaimTypes.MobilePhone) ?? ticket.Principal.GetClaim(ClaimTypes.OtherPhone); } ; await Options.Provider.HandleUserinfoRequest(notification); if (notification.HandledResponse) { return(true); } else if (notification.Skipped) { return(false); } // Ensure the "sub" claim has been correctly populated. if (string.IsNullOrEmpty(notification.Subject)) { Logger.LogError("The mandatory 'sub' claim was missing from the userinfo response."); Response.StatusCode = 500; await SendErrorPayloadAsync(new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "The mandatory 'sub' claim was missing." }); return(true); } var payload = new JObject { [JwtRegisteredClaimNames.Sub] = notification.Subject }; if (notification.Address != null) { payload[OpenIdConnectConstants.Claims.Address] = notification.Address; } if (!string.IsNullOrEmpty(notification.BirthDate)) { payload[JwtRegisteredClaimNames.Birthdate] = notification.BirthDate; } if (!string.IsNullOrEmpty(notification.Email)) { payload[JwtRegisteredClaimNames.Email] = notification.Email; } if (notification.EmailVerified.HasValue) { payload[OpenIdConnectConstants.Claims.EmailVerified] = notification.EmailVerified.Value; } if (!string.IsNullOrEmpty(notification.FamilyName)) { payload[JwtRegisteredClaimNames.FamilyName] = notification.FamilyName; } if (!string.IsNullOrEmpty(notification.GivenName)) { payload[JwtRegisteredClaimNames.GivenName] = notification.GivenName; } if (!string.IsNullOrEmpty(notification.Issuer)) { payload[JwtRegisteredClaimNames.Iss] = notification.Issuer; } if (!string.IsNullOrEmpty(notification.PhoneNumber)) { payload[OpenIdConnectConstants.Claims.PhoneNumber] = notification.PhoneNumber; } if (notification.PhoneNumberVerified.HasValue) { payload[OpenIdConnectConstants.Claims.PhoneNumberVerified] = notification.PhoneNumberVerified.Value; } if (!string.IsNullOrEmpty(notification.PreferredUsername)) { payload[OpenIdConnectConstants.Claims.PreferredUsername] = notification.PreferredUsername; } if (!string.IsNullOrEmpty(notification.Profile)) { payload[OpenIdConnectConstants.Claims.Profile] = notification.Profile; } if (!string.IsNullOrEmpty(notification.Website)) { payload[OpenIdConnectConstants.Claims.Website] = notification.Website; } switch (notification.Audiences.Count) { case 0: break; case 1: payload.Add(JwtRegisteredClaimNames.Aud, notification.Audiences[0]); break; default: payload.Add(JwtRegisteredClaimNames.Aud, JArray.FromObject(notification.Audiences)); break; } foreach (var claim in notification.Claims) { // Ignore claims whose value is null. if (claim.Value == null) { continue; } payload.Add(claim.Key, claim.Value); } var context = new ApplyUserinfoResponseContext(Context, Options, request, payload); await Options.Provider.ApplyUserinfoResponse(context); if (context.HandledResponse) { return(true); } else if (context.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); }