|
5 | 5 | */
|
6 | 6 |
|
7 | 7 | using System.Collections.Generic;
|
| 8 | +using System.Linq; |
8 | 9 | using System.Net.Http;
|
9 | 10 | using System.Net.Http.Headers;
|
10 | 11 | using System.Security.Claims;
|
@@ -61,17 +62,55 @@ protected override async Task<AuthenticationTicket> CreateTicketAsync([NotNull]
|
61 | 62 | .AddOptionalClaim("urn:weibo:avatar_large", WeiboAuthenticationHelper.GetAvatarLarge(payload), Options.ClaimsIssuer)
|
62 | 63 | .AddOptionalClaim("urn:weibo:avatar_hd", WeiboAuthenticationHelper.GetAvatarHD(payload), Options.ClaimsIssuer)
|
63 | 64 | .AddOptionalClaim("urn:weibo:cover_image_phone", WeiboAuthenticationHelper.GetCoverImagePhone(payload), Options.ClaimsIssuer)
|
64 |
| - .AddOptionalClaim("urn:weibo:location", WeiboAuthenticationHelper.GetLocation(payload), Options.ClaimsIssuer); |
| 65 | + .AddOptionalClaim("urn:weibo:location", WeiboAuthenticationHelper.GetLocation(payload), Options.ClaimsIssuer); |
| 66 | + |
| 67 | + // When the email address is not public, retrieve it from |
| 68 | + // the emails endpoint if the user:email scope is specified. |
| 69 | + if (!string.IsNullOrEmpty(Options.UserEmailsEndpoint) && |
| 70 | + !identity.HasClaim(claim => claim.Type == ClaimTypes.Email) && Options.Scope.Contains("user:email")) |
| 71 | + { |
| 72 | + identity.AddOptionalClaim(ClaimTypes.Email, await GetEmailAsync(tokens), Options.ClaimsIssuer); |
| 73 | + } |
65 | 74 |
|
66 | 75 | var principal = new ClaimsPrincipal(identity);
|
67 | 76 | var ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);
|
68 | 77 |
|
69 |
| - var context = new OAuthCreatingTicketContext(ticket, Context, Options, Backchannel, tokens); |
| 78 | + var context = new OAuthCreatingTicketContext(ticket, Context, Options, Backchannel, tokens, payload); |
70 | 79 | await Options.Events.CreatingTicket(context);
|
71 | 80 |
|
72 | 81 | return context.Ticket;
|
73 | 82 | }
|
74 | 83 |
|
75 | 84 | protected override string FormatScope() => string.Join(",", Options.Scope);
|
| 85 | + |
| 86 | + protected virtual async Task<string> GetEmailAsync([NotNull] OAuthTokenResponse tokens) |
| 87 | + { |
| 88 | + // See http://open.weibo.com/wiki/2/account/profile/email for more information about the /account/profile/email.json endpoint. |
| 89 | + var address = QueryHelpers.AddQueryString(Options.UserEmailsEndpoint, new Dictionary<string, string> |
| 90 | + { |
| 91 | + ["access_token"] = tokens.AccessToken |
| 92 | + }); |
| 93 | + |
| 94 | + var request = new HttpRequestMessage(HttpMethod.Get, address); |
| 95 | + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); |
| 96 | + |
| 97 | + // Failed requests shouldn't cause an error: in this case, return null to indicate that the email address cannot be retrieved. |
| 98 | + var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted); |
| 99 | + if (!response.IsSuccessStatusCode) |
| 100 | + { |
| 101 | + Logger.LogWarning("An error occurred while retrieving the email address associated with the logged in user: " + |
| 102 | + "the remote server returned a {Status} response with the following payload: {Headers} {Body}.", |
| 103 | + /* Status: */ response.StatusCode, |
| 104 | + /* Headers: */ response.Headers.ToString(), |
| 105 | + /* Body: */ await response.Content.ReadAsStringAsync()); |
| 106 | + |
| 107 | + return null; |
| 108 | + } |
| 109 | + |
| 110 | + var payload = JArray.Parse(await response.Content.ReadAsStringAsync()); |
| 111 | + |
| 112 | + return (from email in payload.AsJEnumerable() |
| 113 | + select email.Value<string>("email")).FirstOrDefault(); |
| 114 | + } |
76 | 115 | }
|
77 | 116 | }
|
0 commit comments