/// <inheritdoc/> public Task ReplyForbiddenWithWwwAuthenticateHeaderAsync(IEnumerable <string> scopes, MsalUiRequiredException msalServiceException, HttpResponse?httpResponse = null) { // Not implemented for the moment throw new NotImplementedException(); }
/// <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 = 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 string proposedAction = Constants.Consent; if (msalServiceException.ErrorCode == MsalError.InvalidGrantError) { if (AcceptedTokenVersionMismatch(msalServiceException)) { throw msalServiceException; } } _application = await GetOrBuildConfidentialClientApplicationAsync().ConfigureAwait(false); string consentUrl = $"{_application.Authority}/oauth2/v2.0/authorize?client_id={_applicationOptions.ClientId}" + $"&response_type=code&redirect_uri={_application.AppConfig.RedirectUri}" + $"&response_mode=query&scope=offline_access%20{string.Join("%20", scopes)}"; IDictionary <string, string> parameters = new Dictionary <string, string>() { { Constants.ConsentUrl, consentUrl }, { Constants.Claims, msalServiceException.Claims }, { Constants.Scopes, string.Join(",", scopes) }, { Constants.ProposedAction, proposedAction }, }; string parameterString = string.Join(", ", parameters.Select(p => $"{p.Key}=\"{p.Value}\"")); httpResponse ??= CurrentHttpContext?.Response; if (httpResponse == null) { throw new InvalidOperationException(IDWebErrorMessage.HttpContextAndHttpResponseAreNull); } var headers = httpResponse.Headers; httpResponse.StatusCode = (int)HttpStatusCode.Forbidden; headers[HeaderNames.WWWAuthenticate] = new StringValues($"{Constants.Bearer} {parameterString}"); }
/// <summary> /// Used in Web APIs (which therefore cannot have an interaction with the user). /// Replies to the client through the HttpReponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that /// the client can trigger an iteraction with the user so that the user consents to more scopes /// </summary> /// <param name="httpContext">HttpContext</param> /// <param name="scopes">Scopes to consent to</param> /// <param name="msalSeviceException"><see cref="MsalUiRequiredException"/> triggering the challenge</param> public void ReplyForbiddenWithWwwAuthenticateHeader(HttpContext httpContext, IEnumerable <string> scopes, MsalUiRequiredException msalSeviceException) { // A user interaction is required, but we are in a Web API, and therefore, we need to report back to the client through an wwww-Authenticate header https://tools.ietf.org/html/rfc6750#section-3.1 string proposedAction = "consent"; if (msalSeviceException.ErrorCode == MsalUiRequiredException.InvalidGrantError) { if (AcceptedTokenVersionIsNotTheSameAsTokenVersion(msalSeviceException)) { throw msalSeviceException; } } IDictionary <string, string> parameters = new Dictionary <string, string>() { { "clientId", _azureAdOptions.ClientId }, { "claims", msalSeviceException.Claims }, { "scopes", string.Join(",", scopes) }, { "proposedAction", proposedAction } }; string parameterString = string.Join(", ", parameters.Select(p => $"{p.Key}=\"{p.Value}\"")); string scheme = "Bearer"; StringValues v = new StringValues($"{scheme} {parameterString}"); // StringValues v = new StringValues(new string[] { $"Bearer clientId=\"{jwtToken.Audiences.First()}\", claims=\"{ex.Claims}\", scopes=\" {string.Join(",", scopes)}\"" }); var httpResponse = httpContext.Response; var headers = httpResponse.Headers; httpResponse.StatusCode = (int)HttpStatusCode.Forbidden; if (headers.ContainsKey(HeaderNames.WWWAuthenticate)) { headers.Remove(HeaderNames.WWWAuthenticate); } headers.Add(HeaderNames.WWWAuthenticate, v); }
/// <summary> /// Used in Web APIs (which therefore cannot have an interaction with the user). /// Replies to the client through the HttpReponse by sending a 403 (forbidden) and populating wwwAuthenticateHeaders so that /// the client can trigger an iteraction with the user so that the user consents to more scopes /// </summary> /// <param name="scopes">Scopes to consent to</param> /// <param name="msalServiceException"><see cref="MsalUiRequiredException"/> triggering the challenge</param> public void ReplyForbiddenWithWwwAuthenticateHeader(IEnumerable <string> scopes, MsalUiRequiredException msalServiceException) { // A user interaction is required, but we are in a Web API, and therefore, we need to report back to the client through an wwww-Authenticate header https://tools.ietf.org/html/rfc6750#section-3.1 string proposedAction = "consent"; if (msalServiceException.ErrorCode == MsalError.InvalidGrantError) { if (AcceptedTokenVersionMismatch(msalServiceException)) { throw msalServiceException; } } string consentUrl = $"{application.Authority}/oauth2/v2.0/authorize?client_id={_azureAdOptions.ClientId}" + $"&response_type=code&redirect_uri={application.AppConfig.RedirectUri}" + $"&response_mode=query&scope=offline_access%20{string.Join("%20", scopes)}"; IDictionary <string, string> parameters = new Dictionary <string, string>() { { "consentUri", consentUrl }, { "claims", msalServiceException.Claims }, { "scopes", string.Join(",", scopes) }, { "proposedAction", proposedAction } }; string parameterString = string.Join(", ", parameters.Select(p => $"{p.Key}=\"{p.Value}\"")); string scheme = "Bearer"; StringValues v = new StringValues($"{scheme} {parameterString}"); var httpResponse = CurrentHttpContext.Response; var headers = httpResponse.Headers; httpResponse.StatusCode = (int)HttpStatusCode.Forbidden; if (headers.ContainsKey(HeaderNames.WWWAuthenticate)) { headers.Remove(HeaderNames.WWWAuthenticate); } headers.Add(HeaderNames.WWWAuthenticate, v); }
/// <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; } }
public void ExceptionsArePubliclyCreatable_MsalSilentTokenAcquisitionException() { var ex = new MsalUiRequiredException("code", "message"); Assert.IsNull(ex.InnerException); }
/// <summary> /// Build Authentication properties needed for an incremental consent. /// </summary> /// <param name="scopes">Scopes to request</param> /// <returns>AuthenticationProperties</returns> private AuthenticationProperties BuildAuthenticationPropertiesForIncrementalConsent(string[] scopes, MsalUiRequiredException ex) { AuthenticationProperties properties = new AuthenticationProperties(); // Set the scopes, including the scopes that ADAL.NET / MASL.NET need for the Token cache string[] additionalBuildInScopes = new string[] { "openid", "offline_access", "profile" }; properties.SetParameter <ICollection <string> >(OpenIdConnectParameterNames.Scope, scopes.Union(additionalBuildInScopes).ToList()); // Attempts to set the login_hint to avoid the logged-in user to be presented with an account selection dialog string loginHint = HttpContext.User.GetLoginHint(); if (!string.IsNullOrWhiteSpace(loginHint)) { properties.SetParameter <string>(OpenIdConnectParameterNames.LoginHint, loginHint); string domainHint = HttpContext.User.GetDomainHint(); properties.SetParameter <string>(OpenIdConnectParameterNames.DomainHint, domainHint); } // Additional claims required (for instance MFA) if (!string.IsNullOrEmpty(ex.Claims)) { properties.Items.Add("claims", ex.Claims); } return(properties); }
public void ReplyForbiddenWithWwwAuthenticateHeader(IEnumerable<string> scopes, MsalUiRequiredException msalSeviceException) { throw new NotImplementedException(); }