/// <summary> /// code -> oauth.access_token + openid /// </summary> /// <param name="code">用于换取网页授权access_token。此code只能使用一次,5分钟未被使用自动过期。</param> /// <param name="redirectUri"></param> /// <returns></returns> protected virtual async Task <WeixinOAuthTokenResponse> CustomExchangeCodeAsync(string code, string redirectUri) { var query = new QueryBuilder() { { "appid", Options.AppId }, { "secret", Options.AppSecret }, { "code", code }, { "grant_type", "authorization_code" }, { "redirect_uri", redirectUri } }; var url = Options.TokenEndpoint + query; Logger.LogInformation($"Exchanging code via {url}..."); var response = await Backchannel.GetAsync(url, Context.RequestAborted); if (!response.IsSuccessStatusCode) { var error = "An error occured while exchanging the code."; Logger.LogError($"{error} The remote server returned a {response.StatusCode} response with the following payload: {response.Headers.ToString()} {await response.Content.ReadAsStringAsync()}"); //throw new HttpRequestException($"{error}"); return(WeixinOAuthTokenResponse.Failed(new Exception(error))); } var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); var result = WeixinOAuthTokenResponse.Success(payload); //错误时微信会返回错误JSON数据包,示例如下: { "errcode":40029,"errmsg":"invalid code"} if (string.IsNullOrWhiteSpace(result.AccessToken)) { int errorCode = WeixinOAuthHandlerHelper.GetErrorCode(payload); var errorMessage = WeixinOAuthHandlerHelper.GetErrorMessage(payload); return(WeixinOAuthTokenResponse.Failed(new Exception($"The remote server returned an error while exchanging the code. {errorCode} {errorMessage}"))); } return(result); }
/// <summary> /// Call the OAuthServer and get a user's information. /// The context object will have the Identity, AccessToken, and UserInformationEndpoint available. /// Using this information, we can query the auth server for claims to attach to the identity. /// A particular OAuthServer's endpoint returns a json object with a roles member and a name member. /// We call this endpoint with HttpClient, parse the result, and attach the claims to the Identity. /// </summary> /// <param name="identity"></param> /// <param name="properties"></param> /// <param name="tokens"></param> /// <returns></returns> protected virtual async Task <AuthenticationTicket> CustomCreateTicketAsync( ClaimsIdentity identity, AuthenticationProperties properties, WeixinOAuthTokenResponse tokens) { if (identity == null) { throw new ArgumentNullException(nameof(identity)); } if (properties == null) { throw new ArgumentNullException(nameof(properties)); } if (tokens == null) { throw new ArgumentNullException(nameof(tokens)); } var openId = tokens.OpenId; var scope = tokens.Scope; // std:NameIdentifier identity.AddOptionalClaim(ClaimTypes.NameIdentifier, openId, Options.ClaimsIssuer); identity.AddOptionalClaim(WeixinOAuthClaimTypes.OpenId, openId, Options.ClaimsIssuer); // scope identity.AddOptionalClaim(WeixinOAuthClaimTypes.Scope, scope, Options.ClaimsIssuer); if (SplitScope(scope).Contains(WeixinOAuthScopes.snsapi_userinfo)) { identity = await RetrieveUserInfoAsync(tokens.AccessToken, openId, identity); } var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, properties, Scheme.Name); var context = new WeixinOAuthCreatingTicketContext(Context, Scheme, Options, Backchannel, ticket, tokens); await Options.Events.CreatingTicket(context); return(context.Ticket); }