Files
tiku-backend.net/Tiku.Infrastructure/Auth/WechatOAuthClient.cs

145 lines
4.3 KiB
C#

using System.Text.Json;
using Senparc.Weixin.MP.AdvancedAPIs;
using Senparc.Weixin.WxOpen.AdvancedAPIs.Sns;
using Tiku.Application.Auth;
namespace Tiku.Infrastructure.Auth;
public sealed class WechatOAuthClient : IWechatOAuthClient
{
public async Task<WechatIdentity> ExchangeWebCodeAsync(
WechatProviderOptions options,
string code,
CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
var token = await OAuthApi.GetAccessTokenAsync(
options.AppId,
options.AppSecret,
code,
"authorization_code");
cancellationToken.ThrowIfCancellationRequested();
EnsureSuccess(token.ErrorCodeValue, token.errmsg);
if (string.IsNullOrWhiteSpace(token.access_token))
{
throw new InvalidCredentialsException("wechat_access_token_missing");
}
if (string.IsNullOrWhiteSpace(token.openid))
{
throw new InvalidCredentialsException("wechat_openid_missing");
}
var user = await OAuthApi.GetUserInfoAsync(
token.access_token,
token.openid,
Senparc.Weixin.Language.zh_CN);
cancellationToken.ThrowIfCancellationRequested();
return new WechatIdentity(
token.openid.Trim(),
FirstNonBlank(user.unionid, token.unionid),
NullIfBlank(user.nickname),
NullIfBlank(user.headimgurl),
null,
SerializeRaw(new
{
accessToken = token,
user
}));
}
catch (InvalidCredentialsException)
{
throw;
}
catch (Exception exception)
{
throw new InvalidCredentialsException(MapWechatException(exception));
}
}
public async Task<WechatIdentity> ExchangeMiniAppCodeAsync(
WechatProviderOptions options,
string code,
CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
var result = await SnsApi.JsCode2JsonAsync(
options.AppId,
options.AppSecret,
code,
"authorization_code");
cancellationToken.ThrowIfCancellationRequested();
EnsureSuccess(result.ErrorCodeValue, result.errmsg);
if (string.IsNullOrWhiteSpace(result.openid))
{
throw new InvalidCredentialsException("wechat_openid_missing");
}
if (string.IsNullOrWhiteSpace(result.session_key))
{
throw new InvalidCredentialsException("wechat_session_key_missing");
}
return new WechatIdentity(
result.openid.Trim(),
NullIfBlank(result.unionid),
null,
null,
result.session_key.Trim(),
SerializeRaw(result));
}
catch (InvalidCredentialsException)
{
throw;
}
catch (Exception exception)
{
throw new InvalidCredentialsException(MapWechatException(exception));
}
}
private static void EnsureSuccess(int errorCode, string? errorMessage)
{
if (errorCode == 0)
{
return;
}
throw new InvalidCredentialsException(
string.IsNullOrWhiteSpace(errorMessage)
? "wechat_code_exchange_failed"
: $"wechat_code_exchange_failed:{errorCode}");
}
private static string SerializeRaw<T>(T value)
{
return JsonSerializer.Serialize(value);
}
private static string? FirstNonBlank(params string?[] values)
{
return values.Select(NullIfBlank).FirstOrDefault(value => value is not null);
}
private static string? NullIfBlank(string? value)
{
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}
private static string MapWechatException(Exception exception)
{
return exception is HttpRequestException
? "wechat_http_error"
: "wechat_code_exchange_failed";
}
}