protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            AuthenticationProperties properties = null;

            try
            {
                string code  = null;
                string state = null;
                string msg   = null;

                // 获取code,多个值的情况下取第一个,没有值的情况下返回null
                var values = Request.Query.GetValues(nameof(code));
                if (values != null && values.Count == 1)
                {
                    code = values[0];
                }

                values = Request.Query.GetValues("msg");
                if (values != null && values.Count == 1)
                {
                    msg = values[0];
                }

                values = Request.Query.GetValues(nameof(state));
                if (values != null && values.Count == 1)
                {
                    state = values[0];
                }

                properties = Options.StateDataFormat.Unprotect(state);
                if (properties == null)
                {
                    return(null);
                }

                // OAuth2 10.12 CSRF
                if (!ValidateCorrelationId(properties, logger))
                {
                    return(new AuthenticationTicket(null, properties));
                }

                if (String.IsNullOrWhiteSpace(code))
                {
                    logger.WriteWarning("Invalid return {0}", msg);
                    return(new AuthenticationTicket(null, properties));
                }

                string requestPrefix = Request.Scheme + "://" + Request.Host;
                string redirectUri   = requestPrefix + Request.PathBase + Options.CallbackPath;

                string tokenRequest = Options.TokenEndpoint +
                                      "?&grant_type=authorization_code" +
                                      "&client_id=" + Uri.EscapeDataString(Options.AppId) +
                                      "&client_secret=" + Uri.EscapeDataString(Options.AppKey) +
                                      "&code=" + Uri.EscapeDataString(code) +
                                      "&redirect_uri=" + Uri.EscapeDataString(redirectUri);

                var response = await httpClient.GetAsync(tokenRequest, Request.CallCancelled);

                response.EnsureSuccessStatusCode();

                var queryString = await response.Content.ReadAsStringAsync();

                // 解析返回的查询字符串查询字符串
                // access_token=FE04************************CCE2&expires_in=7776000
                var nameValueCollection = HttpUtility.ParseQueryString("?" + queryString);

                string accessToken  = nameValueCollection.Get("access_token");  // 授权令牌
                string expires      = nameValueCollection.Get("expires_in");    // 过期时间
                string refreshToken = nameValueCollection.Get("refresh_token"); // 刷新令牌


                var openIdEndpoint = Options.OptionIdEndpoint + "?access_token=" +
                                     Uri.EscapeDataString(accessToken);

                var openIdresponse = await httpClient.GetAsync(openIdEndpoint, Request.CallCancelled);

                openIdresponse.EnsureSuccessStatusCode();

                // 返回值 callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );
                var text = await openIdresponse.Content.ReadAsStringAsync();

                text = text.Substring(text.IndexOf('{'), text.LastIndexOf('}') - text.IndexOf('{') + 1);
                var jsonObject = JObject.Parse(text);
                var openId     = jsonObject.Value <string>("openid");

                var userInformationEndpoint = Options.UserInformationEndpoint +
                                              "?access_token=" + Uri.EscapeDataString(accessToken) +
                                              "&oauth_consumer_key=" + Uri.EscapeDataString(Options.AppId) +
                                              "&openid=" + Uri.EscapeDataString(openId);

                // 请求用户信息
                var userInformationResponse = await httpClient.GetAsync(userInformationEndpoint, Request.CallCancelled);

                userInformationResponse.EnsureSuccessStatusCode();

                var userJson = await userInformationResponse.Content.ReadAsStringAsync();

                jsonObject = JObject.Parse(userJson);

                var context = new TencentAuthenticatedContext(Context, jsonObject, accessToken, refreshToken, expires,
                                                              openId)
                {
                    Identity = new ClaimsIdentity(
                        Options.AuthenticationType,
                        ClaimsIdentity.DefaultNameClaimType,
                        ClaimsIdentity.DefaultRoleClaimType)
                };

                if (!string.IsNullOrEmpty(context.OpenId))
                {
                    context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.OpenId, XmlSchemaString, Options.AuthenticationType));
                }
                if (!string.IsNullOrEmpty(context.NickName))
                {
                    context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.NickName, XmlSchemaString, Options.AuthenticationType));
                }
                if (!string.IsNullOrEmpty(context.Gender))
                {
                    context.Identity.AddClaim(new Claim(ClaimTypes.Gender, context.Gender, XmlSchemaString, Options.AuthenticationType));
                }
                context.Identity.AddClaim(new Claim("urn:tencentaccount:id", context.OpenId, XmlSchemaString, Options.AuthenticationType));
                context.Identity.AddClaim(new Claim("urn:tencentaccount:nickname", context.NickName, XmlSchemaString,
                                                    Options.AuthenticationType));

                context.Properties = properties;

                await Options.Provider.Authenticated(context);

                return(new AuthenticationTicket(context.Identity, context.Properties));
            }
            catch (Exception ex)
            {
                logger.WriteError("Authentication failed", ex);
                return(new AuthenticationTicket(null, properties));
            }
        }
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            AuthenticationProperties properties = null;

            try
            {
                string code  = null;
                string state = null;

                IReadableStringCollection query  = Request.Query;
                IList <string>            values = query.GetValues("code");
                if (values != null && values.Count == 1)
                {
                    code = values[0];
                }
                values = query.GetValues("state");
                if (values != null && values.Count == 1)
                {
                    state = values[0];
                }

                properties = Options.StateDataFormat.Unprotect(state);
                if (properties == null)
                {
                    return(null);
                }

                // OAuth2 10.12 CSRF
                if (!ValidateCorrelationId(properties, logger))
                {
                    return(new AuthenticationTicket(null, properties));
                }

                string requestPrefix = Options.Schema + "://" + Request.Host;
                string redirectUri   = requestPrefix + Request.PathBase + Options.CallbackPath;

                string requestQueryString =
                    "?grant_type=" + "authorization_code"
                    + "&client_id=" + Options.AppId
                    + "&client_secret=" + Options.AppSecret
                    + "&code=" + code
                    + "&redirect_uri=" + redirectUri;
                // Request the token
                var requestMessage = new HttpRequestMessage(HttpMethod.Get, Options.Endpoints.TokenEndpoint + requestQueryString);
                HttpResponseMessage tokenResponse = await httpClient.SendAsync(requestMessage);

                tokenResponse.EnsureSuccessStatusCode();
                string text = await tokenResponse.Content.ReadAsStringAsync();

                var    queryString = HttpUtility.ParseQueryString("?" + text);
                string accessToken = queryString.Get("access_token");
                int    expiresIn   = Convert.ToInt32(queryString.Get("expires_in"));

                // Get the Tencent user
                string              requestInfoQueryString = "?access_token=" + accessToken;
                HttpRequestMessage  userRequest            = new HttpRequestMessage(HttpMethod.Get, Options.Endpoints.UserInfoEndpoint + requestInfoQueryString);
                HttpResponseMessage userResponse           = await httpClient.SendAsync(userRequest, Request.CallCancelled);

                userResponse.EnsureSuccessStatusCode();
                text = await userResponse.Content.ReadAsStringAsync();

                text = text.Substring(text.IndexOf('{'), text.LastIndexOf('}') - text.IndexOf('{') + 1);
                JObject user = JObject.Parse(text);

                var context = new TencentAuthenticatedContext(Context, user, accessToken, expiresIn);
                context.Identity = new ClaimsIdentity(
                    Options.AuthenticationType,
                    ClaimsIdentity.DefaultNameClaimType,
                    ClaimsIdentity.DefaultRoleClaimType);
                if (!string.IsNullOrEmpty(context.Id))
                {
                    context.Identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, context.Id, XmlSchemaString, Options.AuthenticationType));
                }
                if (!string.IsNullOrEmpty(context.Name))
                {
                    context.Identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, context.Name, XmlSchemaString, Options.AuthenticationType));
                }
                if (!string.IsNullOrEmpty(context.Email))
                {
                    context.Identity.AddClaim(new Claim(ClaimTypes.Email, context.Email, XmlSchemaString, Options.AuthenticationType));
                }
                context.Properties = properties;

                await Options.Provider.Authenticated(context);

                return(new AuthenticationTicket(context.Identity, context.Properties));
            }
            catch (Exception ex)
            {
                logger.WriteError(ex.Message);
            }
            return(new AuthenticationTicket(null, properties));
        }