forked from xiongyuxing/tiku-backend.net
145 lines
4.3 KiB
C#
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";
|
|
}
|
|
}
|