/// <summary>
        /// Validates that an OpenIdConnect Response from "token_endpoint" is valid as per http://openid.net/specs/openid-connect-core-1_0.html
        /// </summary>
        /// <param name="validationContext">the <see cref="OpenIdConnectProtocolValidationContext"/> that contains expected values.</param>
        /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
        /// <exception cref="OpenIdConnectProtocolException">If the response is not spec compliant.</exception>
        /// <remarks>It is assumed that the IdToken had ('aud', 'iss', 'signature', 'lifetime') validated.</remarks>
        public virtual void ValidateTokenResponse(OpenIdConnectProtocolValidationContext validationContext)
        {
            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException(nameof(validationContext));
            }

            // no 'response' is recieved
            if (validationContext.ProtocolMessage == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21333));
            }

            // both 'id_token' and 'access_token' are required
            if (string.IsNullOrEmpty(validationContext.ProtocolMessage.IdToken) || string.IsNullOrEmpty(validationContext.ProtocolMessage.AccessToken))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21336));
            }

            if (validationContext.ValidatedIdToken == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21332));
            }

            ValidateIdToken(validationContext);
            ValidateNonce(validationContext);

            // only if 'at_hash' claim exist. 'at_hash' is not required in token response.
            object atHashClaim;

            if (validationContext.ValidatedIdToken.Payload.TryGetValue(JwtRegisteredClaimNames.AtHash, out atHashClaim))
            {
                ValidateAtHash(validationContext);
            }
        }
        /// <summary>
        /// Validates that an OpenIdConnect Response from "useinfo_endpoint" is valid as per http://openid.net/specs/openid-connect-core-1_0.html
        /// </summary>
        /// <param name="validationContext">the <see cref="OpenIdConnectProtocolValidationContext"/> that contains expected values.</param>
        /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
        /// <exception cref="OpenIdConnectProtocolException">If the response is not spec compliant.</exception>
        public virtual void ValidateUserInfoResponse(OpenIdConnectProtocolValidationContext validationContext)
        {
            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException(nameof(validationContext));
            }

            if (string.IsNullOrEmpty(validationContext.UserInfoEndpointResponse))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21337));
            }

            if (validationContext.ValidatedIdToken == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21332));
            }

            string sub = string.Empty;

            try
            {
                // if user info response is a jwt token
                var handler = new JwtSecurityTokenHandler();
                if (handler.CanReadToken(validationContext.UserInfoEndpointResponse))
                {
                    var token = handler.ReadToken(validationContext.UserInfoEndpointResponse) as JwtSecurityToken;
                    sub = token.Payload.Sub;
                }
                else
                {
                    // if the response is not a jwt, it should be json
                    var payload = JwtPayload.Deserialize(validationContext.UserInfoEndpointResponse);
                    sub = payload.Sub;
                }
            }
            catch (Exception ex)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21343, validationContext.UserInfoEndpointResponse), ex));
            }

            if (string.IsNullOrEmpty(sub))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21345));
            }

            if (string.IsNullOrEmpty(validationContext.ValidatedIdToken.Payload.Sub))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21346));
            }

            if (!string.Equals(validationContext.ValidatedIdToken.Payload.Sub, sub, StringComparison.Ordinal))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21338, validationContext.ValidatedIdToken.Payload.Sub, sub)));
            }
        }
        /// <summary>
        /// Validates the 'token' according to http://openid.net/specs/openid-connect-core-1_0.html
        /// </summary>
        /// <param name="validationContext">A <see cref="OpenIdConnectProtocolValidationContext"/> that contains the protocol message to validate.</param>
        /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
        /// <exception cref="ArgumentNullException">If 'validationContext.ValidatedIdToken' is null.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidAtHashException">If the validationContext contains a 'token' and there is no 'at_hash' claim in the id_token.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidAtHashException">If the validationContext contains a 'token' and the 'at_hash' claim is not a string in the 'id_token'.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidAtHashException">If the 'at_hash' claim in the 'id_token' does not correspond to the 'access_token' in the <see cref="OpenIdConnectMessage"/> response.</exception>
        protected virtual void ValidateAtHash(OpenIdConnectProtocolValidationContext validationContext)
        {
            if (LogHelper.Logger.IsTraceLevelEnabled())
            {
                LogHelper.Logger.LogTrace(LogMessages.IDX10309);
            }

            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext");
            }

            if (validationContext.ValidatedIdToken == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext.ValidatedIdToken");
            }

            if (validationContext.ProtocolMessage == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX10333));
            }

            if (string.IsNullOrEmpty(validationContext.ProtocolMessage.AccessToken))
            {
                if (LogHelper.Logger.IsInformationLevelEnabled())
                {
                    LogHelper.Logger.LogInformation(LogMessages.IDX10310);
                }
                return;
            }

            object atHashClaim;

            if (!validationContext.ValidatedIdToken.Payload.TryGetValue(JwtRegisteredClaimNames.AtHash, out atHashClaim))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidAtHashException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10312, validationContext.ValidatedIdToken)));
            }

            var atHash = atHashClaim as string;

            if (atHash == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidAtHashException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10311, validationContext.ValidatedIdToken)));
            }

            try
            {
                ValidateHash(atHash, validationContext.ProtocolMessage.AccessToken, validationContext.ValidatedIdToken.Header.Alg);
            }
            catch (OpenIdConnectProtocolException ex)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidAtHashException(LogMessages.IDX10348, ex));
            }
        }
        /// <summary>
        /// Validates the 'code' according to http://openid.net/specs/openid-connect-core-1_0.html
        /// </summary>
        /// <param name="validationContext">A <see cref="OpenIdConnectProtocolValidationContext"/> that contains the protocol message to validate.</param>
        /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
        /// <exception cref="ArgumentNullException">If 'validationContext.ValidatedIdToken' is null.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidCHashException">If the validationContext contains a 'code' and there is no 'c_hash' claim in the 'id_token'.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidCHashException">If the validationContext contains a 'code' and the 'c_hash' claim is not a string in the 'id_token'.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidCHashException">If the 'c_hash' claim in the 'id_token' does not correspond to the 'code' in the <see cref="OpenIdConnectMessage"/> response.</exception>
        protected virtual void ValidateCHash(OpenIdConnectProtocolValidationContext validationContext)
        {
            LogHelper.LogVerbose(LogMessages.IDX21304);

            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException(nameof(validationContext));
            }

            if (validationContext.ValidatedIdToken == null)
            {
                throw LogHelper.LogArgumentNullException(nameof(validationContext.ValidatedIdToken));
            }

            if (validationContext.ProtocolMessage == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21333));
            }

            if (string.IsNullOrEmpty(validationContext.ProtocolMessage.Code))
            {
                LogHelper.LogInformation(LogMessages.IDX21305);
                return;
            }

            object cHashClaim;

            if (!validationContext.ValidatedIdToken.Payload.TryGetValue(JwtRegisteredClaimNames.CHash, out cHashClaim))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidCHashException(LogHelper.FormatInvariant(LogMessages.IDX21307, validationContext.ValidatedIdToken)));
            }

            var chash = cHashClaim as string;

            if (chash == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidCHashException(LogHelper.FormatInvariant(LogMessages.IDX21306, validationContext.ValidatedIdToken)));
            }

            try
            {
                ValidateHash(chash, validationContext.ProtocolMessage.Code, validationContext.ValidatedIdToken.Header.Alg);
            }
            catch (OpenIdConnectProtocolException ex)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidCHashException(LogMessages.IDX21347, ex));
            }
        }
        /// <summary>
        /// Validates the 'code' according to http://openid.net/specs/openid-connect-core-1_0.html
        /// </summary>
        /// <param name="validationContext">A <see cref="OpenIdConnectProtocolValidationContext"/> that contains the protocol message to validate.</param>
        /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
        /// <exception cref="ArgumentNullException">If 'validationContext.ValidatedIdToken' is null.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidCHashException">If the validationContext contains a 'code' and there is no 'c_hash' claim in the 'id_token'.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidCHashException">If the validationContext contains a 'code' and the 'c_hash' claim is not a string in the 'id_token'.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidCHashException">If the 'c_hash' claim in the 'id_token' does not correspond to the 'code' in the <see cref="OpenIdConnectMessage"/> response.</exception>
        protected virtual void ValidateCHash(OpenIdConnectProtocolValidationContext validationContext)
        {
            IdentityModelEventSource.Logger.WriteVerbose(LogMessages.IDX10304);

            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext");
            }

            if (validationContext.ValidatedIdToken == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext.ValidatedIdToken");
            }

            if (validationContext.ProtocolMessage == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX10333));
            }

            if (string.IsNullOrEmpty(validationContext.ProtocolMessage.Code))
            {
                IdentityModelEventSource.Logger.WriteInformation(LogMessages.IDX10305);
                return;
            }

            object cHashClaim;

            if (!validationContext.ValidatedIdToken.Payload.TryGetValue(JwtRegisteredClaimNames.CHash, out cHashClaim))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidCHashException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10307, validationContext.ValidatedIdToken)));
            }

            var chash = cHashClaim as string;

            if (chash == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidCHashException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10306, validationContext.ValidatedIdToken)));
            }

            try
            {
                ValidateHash(chash, validationContext.ProtocolMessage.Code, validationContext.ValidatedIdToken.Header.Alg);
            }
            catch (OpenIdConnectProtocolException ex)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidCHashException(LogMessages.IDX10347, ex));
            }
        }
        /// <summary>
        /// Validates the 'token' according to http://openid.net/specs/openid-connect-core-1_0.html
        /// </summary>
        /// <param name="validationContext">A <see cref="OpenIdConnectProtocolValidationContext"/> that contains the protocol message to validate.</param>
        /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
        /// <exception cref="ArgumentNullException">If 'validationContext.ValidatedIdToken' is null.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidAtHashException">If the validationContext contains a 'token' and there is no 'at_hash' claim in the id_token.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidAtHashException">If the validationContext contains a 'token' and the 'at_hash' claim is not a string in the 'id_token'.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidAtHashException">If the 'at_hash' claim in the 'id_token' does not correspond to the 'access_token' in the <see cref="OpenIdConnectMessage"/> response.</exception>
        protected virtual void ValidateAtHash(OpenIdConnectProtocolValidationContext validationContext)
        {
            IdentityModelEventSource.Logger.WriteVerbose(LogMessages.IDX10309);

            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext");
            }

            if (validationContext.ValidatedIdToken == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext.ValidatedIdToken");
            }

            if (validationContext.ProtocolMessage == null)
            {
                throw LogHelper.LogException <OpenIdConnectProtocolException>(LogMessages.IDX10333);
            }

            if (string.IsNullOrEmpty(validationContext.ProtocolMessage.AccessToken))
            {
                IdentityModelEventSource.Logger.WriteInformation(LogMessages.IDX10310);
                return;
            }

            object atHashClaim;

            if (!validationContext.ValidatedIdToken.Payload.TryGetValue(JwtRegisteredClaimNames.AtHash, out atHashClaim))
            {
                throw LogHelper.LogException <OpenIdConnectProtocolInvalidAtHashException>(LogMessages.IDX10312, validationContext.ValidatedIdToken);
            }

            var atHash = atHashClaim as string;

            if (atHash == null)
            {
                throw LogHelper.LogException <OpenIdConnectProtocolInvalidAtHashException>(LogMessages.IDX10311, validationContext.ValidatedIdToken);
            }

            try
            {
                ValidateHash(atHash, validationContext.ProtocolMessage.AccessToken, validationContext.ValidatedIdToken.Header.Alg);
            }
            catch (OpenIdConnectProtocolException ex)
            {
                throw LogHelper.LogException <OpenIdConnectProtocolInvalidAtHashException>(ex, LogMessages.IDX10348);
            }
        }
        /// <summary>
        /// Validates that the 'state' in message is valid.
        /// </summary>
        /// <param name="validationContext">A <see cref="OpenIdConnectProtocolValidationContext"/> that contains the 'state' to validate.</param>
        /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
        /// <exception cref="ArgumentNullException">If 'validationContext.ProtocolMessage ' is null.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidStateException">If 'validationContext.State' is present in <see cref="OpenIdConnectProtocolValidationContext.State"/> but either <see cref="OpenIdConnectProtocolValidationContext.ProtocolMessage"/> or its state property is null.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidStateException">If 'state' in the context does not match the state in the message.</exception>
        protected virtual void ValidateState(OpenIdConnectProtocolValidationContext validationContext)
        {
            if (!RequireStateValidation)
            {
                if (LogHelper.Logger.IsTraceLevelEnabled())
                {
                    LogHelper.Logger.LogTrace(LogMessages.IDX10342);
                }
                return;
            }

            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext");
            }

            if (validationContext.ProtocolMessage == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX10333));
            }

            // if state is missing, but not required just return. Otherwise process it.
            if (!RequireState && string.IsNullOrEmpty(validationContext.State) && string.IsNullOrEmpty(validationContext.ProtocolMessage.State))
            {
                if (LogHelper.Logger.IsInformationLevelEnabled())
                {
                    LogHelper.Logger.LogInformation(LogMessages.IDX10341);
                }
                return;
            }
            else if (string.IsNullOrEmpty(validationContext.State))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidStateException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10329, RequireState)));
            }
            else if (string.IsNullOrEmpty(validationContext.ProtocolMessage.State))
            {
                // 'state' was sent, but message does not contain 'state'
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidStateException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10330, RequireState.ToString())));
            }

            if (!string.Equals(validationContext.State, validationContext.ProtocolMessage.State, StringComparison.Ordinal))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidStateException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10331, validationContext.State, validationContext.ProtocolMessage.State)));
            }
        }
        /// <summary>
        /// Validates that an OpenIdConnect Response from 'authorization_endpoint" is valid as per http://openid.net/specs/openid-connect-core-1_0.html
        /// </summary>
        /// <param name="validationContext">the <see cref="OpenIdConnectProtocolValidationContext"/> that contains expected values.</param>
        /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
        /// <exception cref="OpenIdConnectProtocolException">If the response is not spec compliant.</exception>
        /// <remarks>It is assumed that the IdToken had ('aud', 'iss', 'signature', 'lifetime') validated.</remarks>
        public virtual void ValidateAuthenticationResponse(OpenIdConnectProtocolValidationContext validationContext)
        {
            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext");
            }

            // no 'response' is received or 'id_token' in the response is null
            if (validationContext.ProtocolMessage == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21333));
            }

            if (string.IsNullOrEmpty(validationContext.ProtocolMessage.IdToken))
            {
                // if 'code' is also not present, then throw.
                if (string.IsNullOrEmpty(validationContext.ProtocolMessage.Code))
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21334));
                }
                else
                {
                    ValidateState(validationContext);
                }
                return;
            }

            if (validationContext.ValidatedIdToken == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21332));
            }

            // 'refresh_token' should not be returned from 'authorization_endpoint'. http://tools.ietf.org/html/rfc6749#section-4.2.2.
            if (!string.IsNullOrEmpty(validationContext.ProtocolMessage.RefreshToken))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21335));
            }

            ValidateState(validationContext);
            ValidateIdToken(validationContext);
            ValidateNonce(validationContext);
            ValidateCHash(validationContext);
            ValidateAtHash(validationContext);
        }
        /// <summary>
        /// Validates that the 'state' in message is valid.
        /// </summary>
        /// <param name="validationContext">A <see cref="OpenIdConnectProtocolValidationContext"/> that contains the 'state' to validate.</param>
        /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
        /// <exception cref="ArgumentNullException">If 'validationContext.ProtocolMessage ' is null.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidStateException">If 'validationContext.State' is present in <see cref="OpenIdConnectProtocolValidationContext.State"/> but either <see cref="OpenIdConnectProtocolValidationContext.ProtocolMessage"/> or its state property is null.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidStateException">If 'state' in the context does not match the state in the message.</exception>
        protected virtual void ValidateState(OpenIdConnectProtocolValidationContext validationContext)
        {
            if (!RequireStateValidation)
            {
                LogHelper.LogVerbose(LogMessages.IDX21342);
                return;
            }

            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext");
            }

            if (validationContext.ProtocolMessage == null)
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21333));
            }

            // if state is missing, but not required just return. Otherwise process it.
            if (!RequireState && string.IsNullOrEmpty(validationContext.State) && string.IsNullOrEmpty(validationContext.ProtocolMessage.State))
            {
                LogHelper.LogInformation(LogMessages.IDX21341);
                return;
            }
            else if (string.IsNullOrEmpty(validationContext.State))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidStateException(LogHelper.FormatInvariant(LogMessages.IDX21329, LogHelper.MarkAsNonPII(RequireState))));
            }
            else if (string.IsNullOrEmpty(validationContext.ProtocolMessage.State))
            {
                // 'state' was sent, but message does not contain 'state'
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidStateException(LogHelper.FormatInvariant(LogMessages.IDX21330, LogHelper.MarkAsNonPII(RequireState))));
            }

            if (!string.Equals(validationContext.State, validationContext.ProtocolMessage.State))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidStateException(LogHelper.FormatInvariant(LogMessages.IDX21331, validationContext.State, validationContext.ProtocolMessage.State)));
            }
        }
        /// <summary>
        /// Validates that the 'state' in message is valid.
        /// </summary>
        /// <param name="validationContext">A <see cref="OpenIdConnectProtocolValidationContext"/> that contains the 'state' to validate.</param>
        /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
        /// <exception cref="ArgumentNullException">If 'validationContext.ProtocolMessage ' is null.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidStateException">If 'validationContext.State' is present in <see cref="OpenIdConnectProtocolValidationContext.State"/> but either <see cref="OpenIdConnectProtocolValidationContext.ProtocolMessage"/> or its state property is null.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidStateException">If 'state' in the context does not match the state in the message.</exception>
        protected virtual void ValidateState(OpenIdConnectProtocolValidationContext validationContext)
        {
            if (!RequireStateValidation)
            {
                IdentityModelEventSource.Logger.WriteVerbose(LogMessages.IDX10342);
                return;
            }

            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext");
            }

            if (validationContext.ProtocolMessage == null)
            {
                throw LogHelper.LogException <OpenIdConnectProtocolException>(LogMessages.IDX10333);
            }

            // if state is missing, but not required just return. Otherwise process it.
            if (!RequireState && string.IsNullOrEmpty(validationContext.State) && string.IsNullOrEmpty(validationContext.ProtocolMessage.State))
            {
                IdentityModelEventSource.Logger.WriteInformation(LogMessages.IDX10341);
                return;
            }
            else if (string.IsNullOrEmpty(validationContext.State))
            {
                throw LogHelper.LogException <OpenIdConnectProtocolInvalidStateException>(LogMessages.IDX10329, RequireState);
            }
            else if (string.IsNullOrEmpty(validationContext.ProtocolMessage.State))
            {
                // 'state' was sent, but message does not contain 'state'
                throw LogHelper.LogException <OpenIdConnectProtocolInvalidStateException>(LogMessages.IDX10330, RequireState.ToString());
            }

            if (!string.Equals(validationContext.State, validationContext.ProtocolMessage.State, StringComparison.Ordinal))
            {
                throw LogHelper.LogException <OpenIdConnectProtocolInvalidStateException>(LogMessages.IDX10331, validationContext.State, validationContext.ProtocolMessage.State);
            }
        }
        /// <summary>
        /// Validates that the <see cref="JwtSecurityToken"/> contains the nonce.
        /// </summary>
        /// <param name="validationContext">A <see cref="OpenIdConnectProtocolValidationContext"/> that contains the 'nonce' to validate.</param>
        /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
        /// <exception cref="ArgumentNullException">If 'validationContext.ValidatedIdToken' is null.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidNonceException">If <see cref="OpenIdConnectProtocolValidationContext.Nonce"/> is null and RequireNonce is true.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidNonceException">If the 'nonce' found in the 'id_token' does not match <see cref="OpenIdConnectProtocolValidationContext.Nonce"/>.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidNonceException">If <see cref="RequireTimeStampInNonce"/> is true and a timestamp is not: found, well formed, negatire or expired.</exception>
        /// <remarks>The timestamp is only validated if <see cref="RequireTimeStampInNonce"/> is true.
        /// <para>If <see cref="OpenIdConnectProtocolValidationContext.Nonce"/> is not-null, then a matching 'nonce' must exist in the 'id_token'.</para></remarks>
        protected virtual void ValidateNonce(OpenIdConnectProtocolValidationContext validationContext)
        {
            LogHelper.LogVerbose(LogMessages.IDX21319);

            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException(nameof(validationContext));
            }

            if (validationContext.ValidatedIdToken == null)
            {
                throw LogHelper.LogArgumentNullException(nameof(validationContext.ValidatedIdToken));
            }

            string nonceFoundInJwt = validationContext.ValidatedIdToken.Payload.Nonce;

            // if a nonce is not required AND there is no nonce in the context (which represents what was returned from the IDP) and the token log and return
            if (!RequireNonce && string.IsNullOrEmpty(validationContext.Nonce) && string.IsNullOrEmpty(nonceFoundInJwt))
            {
                LogHelper.LogInformation(LogMessages.IDX21322);
                return;
            }

            // if we get here then RequireNonce == true || validationContext.None != null || nonceFoundInJwt != null
            if (string.IsNullOrEmpty(validationContext.Nonce) && string.IsNullOrEmpty(nonceFoundInJwt))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(LogHelper.FormatInvariant(LogMessages.IDX21320, RequireNonce)));
            }
            else if (string.IsNullOrEmpty(validationContext.Nonce))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(LogHelper.FormatInvariant(LogMessages.IDX21323, RequireNonce)));
            }
            else if (string.IsNullOrEmpty(nonceFoundInJwt))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(LogHelper.FormatInvariant(LogMessages.IDX21349, RequireNonce)));
            }

            if (!string.Equals(nonceFoundInJwt, validationContext.Nonce, StringComparison.Ordinal))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(LogHelper.FormatInvariant(LogMessages.IDX21321, validationContext.Nonce, nonceFoundInJwt, validationContext.ValidatedIdToken)));
            }

            if (RequireTimeStampInNonce)
            {
                int endOfTimestamp = nonceFoundInJwt.IndexOf('.');
                if (endOfTimestamp == -1)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(LogHelper.FormatInvariant(LogMessages.IDX21325, nonceFoundInJwt)));
                }

                string   timestamp = nonceFoundInJwt.Substring(0, endOfTimestamp);
                DateTime nonceTime = new DateTime(1979, 1, 1);          // initializing to some value otherwise it gives an error
                long     ticks     = -1;
                try
                {
                    ticks = Convert.ToInt64(timestamp, CultureInfo.InvariantCulture);
                }
                catch (Exception ex)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(LogHelper.FormatInvariant(LogMessages.IDX21326, timestamp, nonceFoundInJwt), ex));
                }

                if (ticks <= 0)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(LogHelper.FormatInvariant(LogMessages.IDX21326, timestamp, nonceFoundInJwt)));
                }

                try
                {
                    nonceTime = DateTime.FromBinary(ticks);
                }
                catch (Exception ex)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(LogHelper.FormatInvariant(LogMessages.IDX21327, timestamp, DateTime.MinValue.Ticks.ToString(CultureInfo.InvariantCulture), DateTime.MaxValue.Ticks.ToString(CultureInfo.InvariantCulture)), ex));
                }

                DateTime utcNow = DateTime.UtcNow;
                if (nonceTime + NonceLifetime < utcNow)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(LogHelper.FormatInvariant(LogMessages.IDX21324, nonceFoundInJwt, nonceTime.ToString(CultureInfo.InvariantCulture), utcNow.ToString(CultureInfo.InvariantCulture), NonceLifetime.ToString("c", CultureInfo.InvariantCulture))));
                }
            }
        }
        /// <summary>
        /// Validates the claims in the 'id_token' as per http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
        /// </summary>
        /// <param name="validationContext">the <see cref="OpenIdConnectProtocolValidationContext"/> that contains expected values.</param>
        protected virtual void ValidateIdToken(OpenIdConnectProtocolValidationContext validationContext)
        {
            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext");
            }

            if (validationContext.ValidatedIdToken == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext.ValidatedIdToken");
            }

            // if user sets the custom validator, we call the delegate. The default checks for multiple audiences and azp are not executed.
            if (this.IdTokenValidator != null)
            {
                try
                {
                    this.IdTokenValidator(validationContext.ValidatedIdToken, validationContext);
                }
                catch (Exception ex)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21313, validationContext.ValidatedIdToken), ex));
                }
                return;
            }
            else
            {
                JwtSecurityToken idToken = validationContext.ValidatedIdToken;

                // required claims
                if (idToken.Payload.Aud.Count == 0)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21314, JwtRegisteredClaimNames.Aud.ToLowerInvariant(), idToken)));
                }

                if (!idToken.Payload.Exp.HasValue)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21314, JwtRegisteredClaimNames.Exp.ToLowerInvariant(), idToken)));
                }

                if (!idToken.Payload.Iat.HasValue)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21314, JwtRegisteredClaimNames.Iat.ToLowerInvariant(), idToken)));
                }

                if (idToken.Payload.Iss == null)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21314, JwtRegisteredClaimNames.Iss.ToLowerInvariant(), idToken)));
                }

                // sub is required in OpenID spec; but we don't want to block valid idTokens provided by some identity providers
                if (RequireSub && (string.IsNullOrWhiteSpace(idToken.Payload.Sub)))
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21314, JwtRegisteredClaimNames.Sub.ToLowerInvariant(), idToken)));
                }

                // optional claims
                if (RequireAcr && string.IsNullOrWhiteSpace(idToken.Payload.Acr))
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21315, idToken)));
                }

                if (RequireAmr && idToken.Payload.Amr.Count == 0)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21316, idToken)));
                }

                if (RequireAuthTime && !(idToken.Payload.AuthTime.HasValue))
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21317, idToken)));
                }

                if (RequireAzp && string.IsNullOrWhiteSpace(idToken.Payload.Azp))
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21318, idToken)));
                }

                // if multiple audiences are present in the id_token, 'azp' claim should be present
                if (idToken.Payload.Aud.Count > 1 && string.IsNullOrEmpty(idToken.Payload.Azp))
                {
                    LogHelper.LogWarning(LogMessages.IDX21339);
                }

                // if 'azp' claim exist, it should be equal to 'client_id' of the application
                if (!string.IsNullOrEmpty(idToken.Payload.Azp))
                {
                    if (string.IsNullOrEmpty(validationContext.ClientId))
                    {
                        throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogMessages.IDX21308));
                    }
                    else if (!string.Equals(idToken.Payload.Azp, validationContext.ClientId, StringComparison.Ordinal))
                    {
                        throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolException(LogHelper.FormatInvariant(LogMessages.IDX21340, idToken.Payload.Azp, validationContext.ClientId)));
                    }
                }
            }
        }
        /// <summary>
        /// Validates that the <see cref="JwtSecurityToken"/> contains the nonce.
        /// </summary>
        /// <param name="validationContext">A <see cref="OpenIdConnectProtocolValidationContext"/> that contains the 'nonce' to validate.</param>
        /// <exception cref="ArgumentNullException">If 'validationContext' is null.</exception>
        /// <exception cref="ArgumentNullException">If 'validationContext.ValidatedIdToken' is null.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidNonceException">If <see cref="OpenIdConnectProtocolValidationContext.Nonce"/> is null and RequireNonce is true.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidNonceException">If the 'nonce' found in the 'id_token' does not match <see cref="OpenIdConnectProtocolValidationContext.Nonce"/>.</exception>
        /// <exception cref="OpenIdConnectProtocolInvalidNonceException">If <see cref="RequireTimeStampInNonce"/> is true and a timestamp is not: found, well formed, negatire or expired.</exception>
        /// <remarks>The timestamp is only validated if <see cref="RequireTimeStampInNonce"/> is true.
        /// <para>If <see cref="OpenIdConnectProtocolValidationContext.Nonce"/> is not-null, then a matching 'nonce' must exist in the 'id_token'.</para></remarks>
        protected virtual void ValidateNonce(OpenIdConnectProtocolValidationContext validationContext)
        {
            IdentityModelEventSource.Logger.WriteVerbose(LogMessages.IDX10319);

            if (validationContext == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext");
            }

            if (validationContext.ValidatedIdToken == null)
            {
                throw LogHelper.LogArgumentNullException("validationContext.ValidatedIdToken");
            }

            string nonceFoundInJwt = validationContext.ValidatedIdToken.Payload.Nonce;

            if (!RequireNonce && string.IsNullOrEmpty(validationContext.Nonce) && string.IsNullOrEmpty(nonceFoundInJwt))
            {
                IdentityModelEventSource.Logger.WriteInformation(LogMessages.IDX10322);
                return;
            }
            else if (string.IsNullOrEmpty(validationContext.Nonce))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10320, RequireNonce.ToString())));
            }
            else if (string.IsNullOrEmpty(nonceFoundInJwt))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10323, RequireNonce.ToString(), validationContext.ValidatedIdToken)));
            }

            if (!string.Equals(nonceFoundInJwt, validationContext.Nonce, StringComparison.Ordinal))
            {
                throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10321, validationContext.Nonce, nonceFoundInJwt, validationContext.ValidatedIdToken)));
            }

            if (RequireTimeStampInNonce)
            {
                int endOfTimestamp = nonceFoundInJwt.IndexOf('.');
                if (endOfTimestamp == -1)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10325, validationContext.Nonce)));
                }

                string   timestamp = nonceFoundInJwt.Substring(0, endOfTimestamp);
                DateTime nonceTime = new DateTime(1979, 1, 1);          // initializing to some value otherwise it gives an error
                long     ticks     = -1;
                try
                {
                    ticks = Convert.ToInt64(timestamp, CultureInfo.InvariantCulture);
                }
                catch (Exception ex)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10326, timestamp, validationContext.Nonce), ex));
                }

                if (ticks <= 0)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10326, timestamp, validationContext.Nonce)));
                }

                try
                {
                    nonceTime = DateTime.FromBinary(ticks);
                }
                catch (Exception ex)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10327, timestamp, DateTime.MinValue.Ticks.ToString(CultureInfo.InvariantCulture), DateTime.MaxValue.Ticks.ToString(CultureInfo.InvariantCulture)), ex));
                }

                DateTime utcNow = DateTime.UtcNow;
                if (nonceTime + NonceLifetime < utcNow)
                {
                    throw LogHelper.LogExceptionMessage(new OpenIdConnectProtocolInvalidNonceException(String.Format(CultureInfo.InvariantCulture, LogMessages.IDX10324, validationContext.Nonce, nonceTime.ToString(), utcNow.ToString(), NonceLifetime.ToString())));
                }
            }
        }