/// <summary>
        /// Gets a <see cref="GraphRequestContext"/> from <see cref="HttpRequestMessage"/>
        /// </summary>
        /// <param name="httpRequestMessage">The <see cref="HttpRequestMessage"/> representation of the request.</param>
        /// <returns></returns>
        public static GraphRequestContext GetRequestContext(this HttpRequestMessage httpRequestMessage)
        {
            GraphRequestContext requestContext = new GraphRequestContext();

            if (httpRequestMessage.Properties.TryGetValue(typeof(GraphRequestContext).ToString(), out var requestContextObject))
            {
                requestContext = (GraphRequestContext)requestContextObject;
            }
            return(requestContext);
        }
        /// <summary>
        /// Gets a <see cref="IMiddlewareOption"/> from <see cref="HttpRequestMessage"/>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="httpRequestMessage">The <see cref="HttpRequestMessage"/> representation of the request.</param>
        /// <returns>A middleware option</returns>
        public static T GetMiddlewareOption <T>(this HttpRequestMessage httpRequestMessage) where T : IMiddlewareOption
        {
            IMiddlewareOption   option         = null;
            GraphRequestContext requestContext = httpRequestMessage.GetRequestContext();

            if (requestContext.MiddlewareOptions != null)
            {
                requestContext.MiddlewareOptions.TryGetValue(typeof(T).ToString(), out option);
            }
            return((T)option);
        }
        /// <summary>
        /// Adds a <see cref="GraphRequestContext"/> to <see cref="HttpRequestMessage"/> property bag
        /// </summary>
        /// <param name="httpRequestMessage">A <see cref="HttpRequestMessage"/></param>
        /// <param name="cancellationToken">A <see cref="CancellationToken"/></param>
        private void AddRequestContextToRequest(HttpRequestMessage httpRequestMessage, CancellationToken cancellationToken)
        {
            // Creates a request context object
            var requestContext = new GraphRequestContext
            {
                MiddlewareOptions = MiddlewareOptions,
                ClientRequestId   = GetHeaderValue(httpRequestMessage, CoreConstants.Headers.ClientRequestId) ?? Guid.NewGuid().ToString(),
                CancellationToken = cancellationToken,
                FeatureUsage      = httpRequestMessage.GetFeatureFlags()
            };

            httpRequestMessage.Properties.Add(typeof(GraphRequestContext).ToString(), requestContext);
        }
        /// <summary>
        /// Add claims to the request context of the given request message
        /// </summary>
        /// <param name="wwwAuthenticateHeader">String representation of www Authenticate Header</param>
        /// <param name="newRequest">Request message to add claims to</param>
        private void AddClaimsToRequestContext(HttpRequestMessage newRequest, string wwwAuthenticateHeader)
        {
            int claimsStart = wwwAuthenticateHeader.IndexOf("claims=", StringComparison.OrdinalIgnoreCase);

            if (claimsStart < 0)
            {
                return;       // do nothing as there is no claims in www Authenticate Header
            }
            claimsStart += 8; // jump to the index after the opening quotation mark

            // extract and decode the Base 64 encoded claims property
            byte[] bytes           = Convert.FromBase64String(wwwAuthenticateHeader.Substring(claimsStart, wwwAuthenticateHeader.Length - claimsStart - 1));
            string claimsChallenge = Encoding.UTF8.GetString(bytes, 0, bytes.Length);

            // Try to get the current options otherwise create new ones
            AuthenticationHandlerOption   authenticationHandlerOption  = newRequest.GetMiddlewareOption <AuthenticationHandlerOption>() ?? AuthOption;
            IAuthenticationProviderOption authenticationProviderOption = authenticationHandlerOption.AuthenticationProviderOption ?? new CaeAuthenticationProviderOption();

            // make sure that there is no information loss due to casting by copying over the scopes information if necessary
            CaeAuthenticationProviderOption caeAuthenticationProviderOption;

            if (authenticationProviderOption is CaeAuthenticationProviderOption option)
            {
                caeAuthenticationProviderOption = option;
            }
            else
            {
                caeAuthenticationProviderOption = new CaeAuthenticationProviderOption(authenticationProviderOption);
            }

            // update the claims property in the options
            caeAuthenticationProviderOption.Claims = claimsChallenge;
            authenticationHandlerOption.AuthenticationProviderOption = caeAuthenticationProviderOption;

            // update the request context with the updated options
            GraphRequestContext requestContext = newRequest.GetRequestContext();

            requestContext.MiddlewareOptions[typeof(AuthenticationHandlerOption).ToString()] = authenticationHandlerOption;
            newRequest.Properties[typeof(GraphRequestContext).ToString()] = requestContext;
        }