diff --git a/src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationConstants.cs b/src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationConstants.cs index d6188fc4f..54b9cb98f 100644 --- a/src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationConstants.cs +++ b/src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationConstants.cs @@ -25,5 +25,17 @@ public static class Claims /// The user's gender. F: Female; M: Male. /// public const string Gender = "urn:alipay:gender"; + + /// + /// OpenID is the unique identifier for Alipay users at the application level. + /// See https://opendocs.alipay.com/mini/0ai2i6 + /// + public const string OpenId = "urn:alipay:open_id"; + + /// + /// The internal identifier for Alipay users will no longer be independently available going forward and will be replaced by OpenID. + /// See https://opendocs.alipay.com/common/0ai736 + /// + public const string UserId = "urn:alipay:user_id"; } } diff --git a/src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationHandler.cs b/src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationHandler.cs index b33da5630..7e1e31d48 100644 --- a/src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationHandler.cs +++ b/src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationHandler.cs @@ -44,6 +44,16 @@ protected override Task HandleRemoteAuthenticateAsync() return base.HandleRemoteAuthenticateAsync(); } + private const string SignType = "RSA2"; + + private void AddCertificateSignatureParameters(SortedDictionary parameters) + { + ArgumentNullException.ThrowIfNull(Options.ApplicationCertificateSn); + ArgumentNullException.ThrowIfNull(Options.RootCertificateSn); + parameters["app_cert_sn"] = Options.ApplicationCertificateSn; + parameters["alipay_root_cert_sn"] = Options.RootCertificateSn; + } + protected override async Task ExchangeCodeAsync([NotNull] OAuthCodeExchangeContext context) { // See https://opendocs.alipay.com/apis/api_9/alipay.system.oauth.token for details. @@ -55,10 +65,16 @@ protected override async Task ExchangeCodeAsync([NotNull] OA ["format"] = "JSON", ["grant_type"] = "authorization_code", ["method"] = "alipay.system.oauth.token", - ["sign_type"] = "RSA2", + ["sign_type"] = SignType, ["timestamp"] = TimeProvider.GetUtcNow().ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), ["version"] = "1.0", }; + + if (Options.UseCertificateSignatures) + { + AddCertificateSignatureParameters(tokenRequestParameters); + } + tokenRequestParameters.Add("sign", GetRSA2Signature(tokenRequestParameters)); // PKCE https://tools.ietf.org/html/rfc7636#section-4.5, see BuildChallengeUrl @@ -103,10 +119,16 @@ protected override async Task CreateTicketAsync( ["charset"] = "utf-8", ["format"] = "JSON", ["method"] = "alipay.user.info.share", - ["sign_type"] = "RSA2", + ["sign_type"] = SignType, ["timestamp"] = TimeProvider.GetUtcNow().ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), ["version"] = "1.0", }; + + if (Options.UseCertificateSignatures) + { + AddCertificateSignatureParameters(parameters); + } + parameters.Add("sign", GetRSA2Signature(parameters)); var address = QueryHelpers.AddQueryString(Options.UserInformationEndpoint, parameters); diff --git a/src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationOptions.cs b/src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationOptions.cs index 08677ddd5..0552a69c7 100644 --- a/src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationOptions.cs +++ b/src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationOptions.cs @@ -29,5 +29,46 @@ public AlipayAuthenticationOptions() ClaimActions.MapJsonKey(Claims.Gender, "gender"); ClaimActions.MapJsonKey(Claims.Nickname, "nick_name"); ClaimActions.MapJsonKey(Claims.Province, "province"); + ClaimActions.MapJsonKey(Claims.OpenId, "open_id"); + + // https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers/pull/1131#discussion_r2657531257 + ClaimActions.MapJsonKey("urn:alipay:user_id", "user_id"); + } + + /// + /// Gets or sets a value indicating whether to use certificate mode for signing calls. + /// https://opendocs.alipay.com/common/057k53?pathHash=e18d6f77#%E8%AF%81%E4%B9%A6%E6%A8%A1%E5%BC%8F + /// + public bool UseCertificateSignatures { get; set; } + + /// + /// Gets or sets the optional ID for your Sign in with Application Public Key Certificate SN(app_cert_sn). + /// https://opendocs.alipay.com/support/01raux + /// + public string? ApplicationCertificateSn { get; set; } + + /// + /// Gets or sets the optional ID for your Sign in with Alipay Root Certificate SN. + /// https://opendocs.alipay.com/support/01rauy + /// + public string? RootCertificateSn { get; set; } + + /// + public override void Validate() + { + base.Validate(); + + if (UseCertificateSignatures) + { + if (string.IsNullOrEmpty(ApplicationCertificateSn)) + { + throw new ArgumentException($"The '{nameof(ApplicationCertificateSn)}' option must be provided if the '{nameof(UseCertificateSignatures)}' option is set to true.", nameof(ApplicationCertificateSn)); + } + + if (string.IsNullOrEmpty(RootCertificateSn)) + { + throw new ArgumentException($"The '{nameof(RootCertificateSn)}' option must be provided if the '{nameof(UseCertificateSignatures)}' option is set to true.", nameof(RootCertificateSn)); + } + } } }