Skip to content

Commit 284f743

Browse files
authored
Fix Alipay user_id upgrade open_id issue (#1015)
Handle migration to OpenID.
1 parent f35ef2c commit 284f743

File tree

3 files changed

+131
-7
lines changed

3 files changed

+131
-7
lines changed

src/AspNet.Security.OAuth.Alipay/AlipayAuthenticationHandler.cs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ protected override async Task<OAuthTokenResponse> ExchangeCodeAsync([NotNull] OA
8181
using var document = await JsonDocument.ParseAsync(stream);
8282

8383
var mainElement = document.RootElement.GetProperty("alipay_system_oauth_token_response");
84-
if (!ValidateReturnCode(mainElement, out var code))
84+
if (!ValidateReturnCode(mainElement, out var code, out var subCode))
8585
{
86-
return OAuthTokenResponse.Failed(new Exception($"An error (Code:{code}) occurred while retrieving an access token."));
86+
return OAuthTokenResponse.Failed(new Exception($"An error (Code:{code} subCode:{subCode}) occurred while retrieving an access token."));
8787
}
8888

8989
var payload = JsonDocument.Parse(mainElement.GetRawText());
@@ -126,15 +126,16 @@ protected override async Task<AuthenticationTicket> CreateTicketAsync(
126126
if (!rootElement.TryGetProperty("alipay_user_info_share_response", out JsonElement mainElement))
127127
{
128128
var errorCode = rootElement.GetProperty("error_response").GetProperty("code").GetString()!;
129-
throw new AuthenticationFailureException($"An error (Code:{errorCode}) occurred while retrieving user information.");
129+
var errorSubCode = rootElement.GetProperty("error_response").GetProperty("sub_code").GetString()!;
130+
throw new AuthenticationFailureException($"An error response (Code:{errorCode} subCode:{errorSubCode}) occurred while retrieving user information.");
130131
}
131132

132-
if (!ValidateReturnCode(mainElement, out var code))
133+
if (!ValidateReturnCode(mainElement, out var code, out var subCode))
133134
{
134-
throw new AuthenticationFailureException($"An error (Code:{code}) occurred while retrieving user information.");
135+
throw new AuthenticationFailureException($"An error (Code:{code} subCode:{subCode}) occurred while retrieving user information.");
135136
}
136137

137-
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, mainElement.GetString("user_id")!, ClaimValueTypes.String, Options.ClaimsIssuer));
138+
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, GetUserIdentifier(mainElement), ClaimValueTypes.String, Options.ClaimsIssuer));
138139

139140
var principal = new ClaimsPrincipal(identity);
140141
var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, mainElement);
@@ -153,17 +154,28 @@ protected override async Task<AuthenticationTicket> CreateTicketAsync(
153154
/// </summary>
154155
/// <param name="element">Main part of json document from response</param>
155156
/// <param name="code">Returned code from server</param>
157+
/// <param name="subCode">Returned sub_code from server</param>
156158
/// <remarks>See https://opendocs.alipay.com/open/common/105806 for details.</remarks>
157159
/// <returns>True if succeed, otherwise false.</returns>
158-
private static bool ValidateReturnCode(JsonElement element, out string code)
160+
private static bool ValidateReturnCode(JsonElement element, out string code, out string subCode)
159161
{
160162
if (!element.TryGetProperty("code", out JsonElement codeElement))
161163
{
162164
code = string.Empty;
165+
subCode = string.Empty;
163166
return true;
164167
}
165168

166169
code = codeElement.GetString()!;
170+
171+
if (!element.TryGetProperty("sub_code", out JsonElement subCodeElement))
172+
{
173+
subCode = string.Empty;
174+
return true;
175+
}
176+
177+
subCode = subCodeElement.GetString()!;
178+
167179
return code == "10000";
168180
}
169181

@@ -200,6 +212,22 @@ private string GetRSA2Signature([NotNull] SortedDictionary<string, string?> sort
200212
return Convert.ToBase64String(encryptedBytes);
201213
}
202214

215+
/// <summary>
216+
/// Get user identifier from response.
217+
/// </summary>
218+
/// <param name="element">Main part of json document from response</param>
219+
/// <remarks>See https://opendocs.alipay.com/common/0ai2i6?pathHash=cba76ebf for details.</remarks>
220+
/// <returns>UserId or OpenId</returns>
221+
private static string GetUserIdentifier(JsonElement element)
222+
{
223+
if (element.TryGetProperty("user_id", out JsonElement userIdElement))
224+
{
225+
return userIdElement.GetString()!;
226+
}
227+
228+
return element.GetString("open_id")!;
229+
}
230+
203231
/// <inheritdoc />
204232
protected override string BuildChallengeUrl([NotNull] AuthenticationProperties properties, [NotNull] string redirectUri)
205233
{

test/AspNet.Security.OAuth.Providers.Tests/Alipay/AlipayTests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,46 @@ public async Task BuildChallengeUrl_Generates_Correct_Url(bool usePkce)
7777
}
7878
}
7979

80+
[Fact]
81+
public async Task Cannot_Sign_In_Using_Alipay_With_Sub_Code_Error()
82+
{
83+
// Arrange
84+
static void ConfigureServices(IServiceCollection services)
85+
{
86+
services.PostConfigureAll<AlipayAuthenticationOptions>((options) => options.ClientId = "error-id");
87+
}
88+
89+
using var server = CreateTestServer(ConfigureServices);
90+
91+
// Act
92+
var exception = await Assert.ThrowsAsync<AuthenticationFailureException>(async () => await AuthenticateUserAsync(server));
93+
exception.InnerException!.Message.ShouldBe("An error (Code:40003 subCode:isv.not-online-app) occurred while retrieving user information.");
94+
}
95+
96+
[Theory]
97+
[InlineData(ClaimTypes.NameIdentifier, "open-id")]
98+
[InlineData("urn:alipay:avatar", "http://tfsimg.alipay.com/images/partner/T1uIxXXbpXXXXXXXX")]
99+
[InlineData("urn:alipay:province", "my-province")]
100+
[InlineData("urn:alipay:city", "my-city")]
101+
[InlineData("urn:alipay:nick_name", "my-nickname")]
102+
[InlineData("urn:alipay:gender", "M")]
103+
public async Task Can_Sign_In_Using_Alipay_With_Use_Open_Id(string claimType, string claimValue)
104+
{
105+
// Arrange
106+
static void ConfigureServices(IServiceCollection services)
107+
{
108+
services.PostConfigureAll<AlipayAuthenticationOptions>((options) => options.ClientId = "open-id");
109+
}
110+
111+
using var server = CreateTestServer(ConfigureServices);
112+
113+
// Act
114+
var claims = await AuthenticateUserAsync(server);
115+
116+
// Assert
117+
AssertClaim(claims, claimType, claimValue);
118+
}
119+
80120
private sealed class FixedClock : TimeProvider
81121
{
82122
public override DateTimeOffset GetUtcNow() => new(2019, 12, 14, 22, 22, 22, TimeSpan.Zero);

test/AspNet.Security.OAuth.Providers.Tests/Alipay/bundle.json

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,62 @@
3131
},
3232
"sign": "sign"
3333
}
34+
},
35+
{
36+
"uri": "https://openapi.alipay.com/gateway.do?app_id=error-id&charset=utf-8&code=a6ed8e7f-471f-44f1-903b-65946475f351&format=JSON&grant_type=authorization_code&method=alipay.system.oauth.token&sign=N3%2B7cd6Sln12%2BgfKMFUcsqMQbZIbgG%2Br8Bbf5YImFQ%2B3IkFzSjwyBFuFNH3nshwZpGgyw5CQhar1PZapdJyhUwMwwO3gcC1os6vRe7kFGU84mbje3QnlU%2FS98QjFjViZ8fHLlCb1uFd2oCngkDmKaTTma9eLkiVmlu8eQ3tiU0k4ADG09F2y5YmP6vOg8RJxfsIoeImvnLGd7c5gUqU70ub2ekjR0FrWnjNs3wO8leT3QA1FG9bGqnnV3lB2MkiyZssENN0XvC4is8gG4zFdHRW%2BzIL6CA0Vz2CG0uD1g7f%2BUXQzvNJ26p97U%2F1bHWDw9XkTCbhGToGBNkacKy3xkg%3D%3D&sign_type=RSA2&timestamp=2019-12-14%2022%3A22%3A22&version=1.0",
37+
"contentFormat": "json",
38+
"contentJson": {
39+
"alipay_system_oauth_token_response": {
40+
"open_id": "open-id",
41+
"access_token": "secret-access-token",
42+
"expires_in": "300",
43+
"refresh_token": "secret-refresh-token",
44+
"re_expires_in": "300"
45+
},
46+
"sign": "sign"
47+
}
48+
},
49+
{
50+
"uri": "https://openapi.alipay.com/gateway.do?app_id=error-id&auth_token=secret-access-token&charset=utf-8&format=JSON&method=alipay.user.info.share&sign=4p9fbAnCCa%2FUKDkJH2OZcPhHXvDEwy17G9iZjRM%2BxpPc2%2FUw5it8BuBiU57SIkAwFBJjnC0RXGOlur94jO4b9rMyQWNsNXKjIHe3nHPx6fMFEgqSe6SUohuo%2BvvvdFyK1TkvstLgAJXyfAlMLg1SxLUZDLo%2FPga0Emxy2UXjCyTZBgIc%2BzZEBwly5s4NW0Jln3ZFYlpG4yFiuSBFzTCJ4bypVkQ59yANl3kZGnoXBkddieDLA4DGLUN6PacsdGNBFgIA%2BwU9v95gL80SdcHON7cxDDU33Lev0k%2Fu3%2F7EBBN%2BqaCmQS5wL0Huls3n6JAhZvL%2FFEWTa2gvedcm1AtE6w%3D%3D&sign_type=RSA2&timestamp=2019-12-14%2022%3A22%3A22&version=1.0",
51+
"contentFormat": "json",
52+
"contentJson": {
53+
"alipay_user_info_share_response": {
54+
"code": "40003",
55+
"sub_code": "isv.not-online-app"
56+
},
57+
"sign": "sign"
58+
}
59+
},
60+
{
61+
"uri": "https://openapi.alipay.com/gateway.do?app_id=open-id&charset=utf-8&code=a6ed8e7f-471f-44f1-903b-65946475f351&format=JSON&grant_type=authorization_code&method=alipay.system.oauth.token&sign=tvlLQ%2Brp1tu5z8VFP9YpOZyhyAjAhVDz1s8eJ0x78Z0FWSFw1OPBxqramdbz2ZCgKJTbQ3Ajd1eJLlNTp7XzVjpUCLGYpUaUs23GMhk%2BYlMNAYxVvTvVLq7ceKLygpVt4dS5rdUI0zesscsoAB3YMPNZ2yEj%2BCVMvsHIS0VFgt0wmc8HN8Cc7XWTdIeN3LycBecH8rMGs5iakKzeGMHbUh5KKayEPEZqQNZTzVhTNmuf%2BNsLcnfIVPDBdLDVA8FQEm6mRQvNlZd2w8gfWAQk6chf2j%2FpyrEaGEvpliZzG6GRulcuuJ%2FwhCtLdm5KkzN75dxTpTvKdbcWCHbDa2iTjQ%3D%3D&sign_type=RSA2&timestamp=2019-12-14%2022%3A22%3A22&version=1.0",
62+
"contentFormat": "json",
63+
"contentJson": {
64+
"alipay_system_oauth_token_response": {
65+
"open_id": "open-id",
66+
"access_token": "secret-access-token",
67+
"expires_in": "300",
68+
"refresh_token": "secret-refresh-token",
69+
"re_expires_in": "300"
70+
},
71+
"sign": "sign"
72+
}
73+
},
74+
{
75+
"uri": "https://openapi.alipay.com/gateway.do?app_id=open-id&auth_token=secret-access-token&charset=utf-8&format=JSON&method=alipay.user.info.share&sign=uYIsXQsOZnpqWmy4Roa48LHX6D4g%2BDhz%2BdtQfrFTus2hjDiAOxOZFooUy%2Fw6sXHkPYO6vM5JpmNRdXDdontk%2BNP8szwyMHaGEX8%2FKWDzLGErMkJs1gneepnaVPeoTd57fxS6fY58py%2FsObG0zcxGp61wzI3D4fdN6c2AoFy0mOoqeMZixynHjXQFkPtj68pBcfZNlxujDhYkHtWdjrdR%2BPhRT%2FAazABa%2B%2BHLfM6YMHyd3Ryo%2BvSh7Xr%2BANU9n%2F2Ayw%2FvSxjGdd6UnYJ%2FIgi5XTWoAMvmKB%2BBpiS6cnP96HG27mgU2LGnrDAIYvQORLAOutezDeVHB7xHSGvtSobEmg%3D%3D&sign_type=RSA2&timestamp=2019-12-14 22%3A22%3A22&version=1.0",
76+
"contentFormat": "json",
77+
"contentJson": {
78+
"alipay_user_info_share_response": {
79+
"code": "10000",
80+
"msg": "Success",
81+
"open_id": "open-id",
82+
"avatar": "http://tfsimg.alipay.com/images/partner/T1uIxXXbpXXXXXXXX",
83+
"province": "my-province",
84+
"city": "my-city",
85+
"nick_name": "my-nickname",
86+
"gender": "M"
87+
},
88+
"sign": "sign"
89+
}
3490
}
3591
]
3692
}

0 commit comments

Comments
 (0)