/// <summary>
        /// This method handles the response from Google and returns an <see cref="AuthenticationResponse"/> object
        /// </summary>
        /// <returns>AuthenticationResponse with details such as the IDTokenPayload</returns>
        /// <exception cref="InvalidStateException">Thrown when the CSRF token is missing or there is a mismatch</exception>
        /// <exception cref="AuthenticationException">Thrown when there was an error in the process of authentication at Google</exception>
        /// <exception cref="HashAlgorithmNotSupportedException">Thrown when the used algorithm for hashing is not supported</exception>
        /// <exception cref="SignatureVerificationException">Thrown when the signature seems to be broken</exception>
        public static AuthenticationResponse HandleResponse()
        {
            string code  = HttpContext.Current.Request.Params["code"];
            string state = HttpContext.Current.Request.Params["state"];
            string error = HttpContext.Current.Request.Params["error"];

            NameValueCollection stateParameters = HttpUtility.ParseQueryString(state);
            string responseState             = stateParameters[ResponseStateParameter];
            bool   receiveProfileInformation = Boolean.Parse(stateParameters[ReceiveProfileInformation]);

            if (!AuthenticationUtility.VerifyCsrfTokenInState(stateParameters))
            {
                throw new InvalidStateException(responseState);
            }
            if (!String.IsNullOrWhiteSpace(error))
            {
                throw new AuthenticationException(error, responseState);
            }

            NameValueCollection parameters = new NameValueCollection();

            parameters.Add("code", code);
            parameters.Add("client_id", Configuration.ClientID);
            parameters.Add("client_secret", Configuration.ClientSecret);
            parameters.Add("redirect_uri", Configuration.RedirectURI);
            parameters.Add("grant_type", "authorization_code");

            TokenInformation token           = WebClientUtility.GetIDTokenPayload(parameters);
            IDTokenPayload   idTokenPayload  = DecodeAndVerifyIDTokenPayload(token.IDToken, GetRSACryptoServiceProvider(), true);
            UserInformation  userInformation = (receiveProfileInformation)?WebClientUtility.GetUserInformation(token.AccessToken):null;

            return(new AuthenticationResponse(responseState, idTokenPayload, userInformation));
        }
        /// <summary>
        /// Converts the JWKs to a list of <see cref="RSACryptoServiceProvider"/>.
        /// The JWKs are being downloaded directly from the link provided in
        /// the OIDC Discovery Document.
        /// </summary>
        /// <returns>List of RSACryptoServiceProviders</returns>
        private static List <RSACryptoServiceProvider> GetRSACryptoServiceProvider()
        {
            JsonWebKeyIndex jsonWebKeyIndex           = WebClientUtility.GetJsonWebKeyIndex();
            List <RSACryptoServiceProvider> providers = new List <RSACryptoServiceProvider>();

            jsonWebKeyIndex.Keys.ForEach(jsonWebToken =>
            {
                RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
                provider.ImportParameters(new RSAParameters
                {
                    Exponent = ConversionUtility.Base64UrlDecode(jsonWebToken.Exponent),
                    Modulus  = ConversionUtility.Base64UrlDecode(jsonWebToken.Modulus)
                });
                providers.Add(provider);
            });

            return(providers);
        }