/// <summary> /// Used in web APIs (no user interaction). /// Replies to the client through the HTTP response by sending a 403 (forbidden) and populating the 'WWW-Authenticate' header so that /// the client, in turn, can trigger a user interaction so that the user consents to more scopes. /// </summary> /// <param name="scopes">Scopes to consent to.</param> /// <param name="msalServiceException">The <see cref="MsalUiRequiredException"/> that triggered the challenge.</param> /// <param name="httpResponse">The <see cref="HttpResponse"/> to update.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> public async Task ReplyForbiddenWithWwwAuthenticateHeaderAsync(IEnumerable <string> scopes, MsalUiRequiredException msalServiceException, HttpResponse httpResponse, string additional_info = null) { // A user interaction is required, but we are in a web API, and therefore, we need to report back to the client through a 'WWW-Authenticate' header https://tools.ietf.org/html/rfc6750#section-3.1 try { // Create response header as per https://tools.ietf.org/html/rfc6750#section-3.1 string parameterString = CommonUtil.CreateResponseHeader(AuthenticationConfig, msalServiceException.Claims, scopes); var headers = httpResponse.Headers; httpResponse.StatusCode = (int)HttpStatusCode.Forbidden; headers[HeaderNames.WWWAuthenticate] = new StringValues($"Bearer {parameterString}"); string message = msalServiceException.Message; // Create response content with error details. InsufficientClaimsResponse insufficientClaimsResponse = CommonUtil.CreateErrorResponseMessage(message, additional_info); httpResponse.Write(Newtonsoft.Json.JsonConvert.SerializeObject(insufficientClaimsResponse)); } catch (Exception ex) { var a = ex.Message; } }
/// <summary> /// Checks if the access token has acrs claim with acrsValue. /// If does not exists then adds WWW-Authenticate and throws UnauthorizedAccessException exception. /// </summary> /// <param name="acrsValue"></param> /// <param name="httpResponse"></param> public static void EnsureUserHasAuthenticationContextClassReference(string acrsValue, string additionalInfo = null) { AuthenticationConfig authenticationConfig = new AuthenticationConfig(); HttpContext context = HttpContext.Current; ClaimsPrincipal claimsPrincipal = ClaimsPrincipal.Current; string authenticationContextClassReferencesClaim = "acrs"; if (context == null || context.User == null || claimsPrincipal.Claims == null || !claimsPrincipal.Claims.Any()) { throw new ArgumentNullException("No Usercontext is available to pick claims from"); } Claim acrsClaim = claimsPrincipal.FindFirst(authenticationContextClassReferencesClaim); if (acrsClaim == null || acrsClaim.Value != acrsValue) { context.Response.StatusCode = (int)HttpStatusCode.Forbidden; //string message = string.Format(CultureInfo.InvariantCulture, "The presented access tokens had insufficient claims. Please request for claims requested in the WWW-Authentication header and try again."); var base64str = Convert.ToBase64String(Encoding.UTF8.GetBytes("{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"" + acrsValue + "\"}}}")); // Create response header as per https://tools.ietf.org/html/rfc6750#section-3.1 var authenticateHeader = CommonUtil.CreateResponseHeader(authenticationConfig, base64str); context.Response.Headers.Add("WWW-Authenticate", authenticateHeader); string message = $"The claim 'acrs' is either missing or does not have the value(s) '{acrsValue}'.Please redirect the user to the authorization server for additional processing."; // Create response content with error details. InsufficientClaimsResponse insufficientClaimsResponse = CommonUtil.CreateErrorResponseMessage(message, additionalInfo); if (insufficientClaimsResponse != null) { context.Response.Write(Newtonsoft.Json.JsonConvert.SerializeObject(insufficientClaimsResponse)); } context.Response.End(); throw new UnauthorizedAccessException(message); } }
/// <summary> /// Create Response Content for insufficient claims. /// </summary> /// <param name="message"></param> /// <param name="addionalInfo"></param> /// <param name="requestId"></param> /// <returns></returns> internal static InsufficientClaimsResponse CreateErrorResponseMessage(string message, string addionalInfo = null, string requestId = null) { InsufficientClaimsResponse insufficientClaimsResponse = new InsufficientClaimsResponse(); insufficientClaimsResponse.Code = Constants.InsufficientClaims; insufficientClaimsResponse.Message = message; insufficientClaimsResponse.AdditionalInfo = addionalInfo; insufficientClaimsResponse.InnerError = new ResponseInnerError(); insufficientClaimsResponse.InnerError.Date = DateTime.UtcNow; if (requestId == null) { requestId = Guid.NewGuid().ToString(); } insufficientClaimsResponse.InnerError.RequestId = requestId; insufficientClaimsResponse.InnerError.ClientRequestId = requestId; return(insufficientClaimsResponse); }