Skip to content

Commit 70cd0af

Browse files
zAfLukevinchalet
authored andcommitted
Fix the Twitch provider to use the correct user information endpoint and add new claims
1 parent 48f7a50 commit 70cd0af

File tree

5 files changed

+128
-5
lines changed

5 files changed

+128
-5
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
namespace AspNet.Security.OAuth.Twitch
8+
{
9+
/// <summary>
10+
/// Contains constants specific to the <see cref="TwitchAuthenticationHandler"/>.
11+
/// </summary>
12+
public static class TwitchAuthenticationConstants
13+
{
14+
public static class Claims
15+
{
16+
public const string BroadcasterType = "urn:twitch:broadcastertype";
17+
public const string Description = "urn:twitch:description";
18+
public const string DisplayName = "urn:twitch:displayname";
19+
public const string OfflineImageUrl = "urn:twitch:offlineimageurl";
20+
public const string ProfileImageUrl = "urn:twitch:profileimageurl";
21+
public const string Type = "urn:twitch:type";
22+
}
23+
}
24+
}

src/AspNet.Security.OAuth.Twitch/TwitchAuthenticationDefaults.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@ public static class TwitchAuthenticationDefaults
4646
/// <summary>
4747
/// Default value for <see cref="OAuthOptions.UserInformationEndpoint"/>.
4848
/// </summary>
49-
public const string UserInformationEndpoint = "https://api.twitch.tv/helix/user/";
49+
public const string UserInformationEndpoint = "https://api.twitch.tv/helix/users/";
5050
}
5151
}

src/AspNet.Security.OAuth.Twitch/TwitchAuthenticationHandler.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using Microsoft.AspNetCore.Http.Authentication;
1616
using Microsoft.Extensions.Logging;
1717
using Newtonsoft.Json.Linq;
18+
using static AspNet.Security.OAuth.Twitch.TwitchAuthenticationConstants;
1819

1920
namespace AspNet.Security.OAuth.Twitch
2021
{
@@ -47,7 +48,14 @@ protected override async Task<AuthenticationTicket> CreateTicketAsync([NotNull]
4748
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
4849

4950
identity.AddOptionalClaim(ClaimTypes.NameIdentifier, TwitchAuthenticationHelper.GetIdentifier(payload), Options.ClaimsIssuer)
50-
.AddOptionalClaim(ClaimTypes.Name, TwitchAuthenticationHelper.GetName(payload), Options.ClaimsIssuer);
51+
.AddOptionalClaim(ClaimTypes.Name, TwitchAuthenticationHelper.GetName(payload), Options.ClaimsIssuer)
52+
.AddOptionalClaim(Claims.DisplayName, TwitchAuthenticationHelper.GetDisplayName(payload), Options.ClaimsIssuer)
53+
.AddOptionalClaim(ClaimTypes.Email, TwitchAuthenticationHelper.GetEmail(payload), Options.ClaimsIssuer)
54+
.AddOptionalClaim(Claims.Type, TwitchAuthenticationHelper.GetType(payload), Options.ClaimsIssuer)
55+
.AddOptionalClaim(Claims.BroadcasterType, TwitchAuthenticationHelper.GetBroadcastType(payload), Options.ClaimsIssuer)
56+
.AddOptionalClaim(Claims.Description, TwitchAuthenticationHelper.GetDescription(payload), Options.ClaimsIssuer)
57+
.AddOptionalClaim(Claims.ProfileImageUrl, TwitchAuthenticationHelper.GetProfileImageUrl(payload), Options.ClaimsIssuer)
58+
.AddOptionalClaim(Claims.OfflineImageUrl, TwitchAuthenticationHelper.GetOfflineImageUrl(payload), Options.ClaimsIssuer);
5159

5260
var principal = new ClaimsPrincipal(identity);
5361
var ticket = new AuthenticationTicket(principal, properties, Options.AuthenticationScheme);

src/AspNet.Security.OAuth.Twitch/TwitchAuthenticationHelper.cs

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public static string GetIdentifier([NotNull] JObject user)
2626
throw new ArgumentNullException(nameof(user));
2727
}
2828

29-
return user.Value<string>("_id");
29+
return user["data"]?[0]?.Value<string>("id");
3030
}
3131

3232
/// <summary>
@@ -39,7 +39,98 @@ public static string GetName([NotNull] JObject user)
3939
throw new ArgumentNullException(nameof(user));
4040
}
4141

42-
return user.Value<string>("name");
42+
return user["data"]?[0]?.Value<string>("login");
43+
}
44+
45+
/// <summary>
46+
/// Gets the display name corresponding to the authenticated user.
47+
/// </summary>
48+
public static string GetDisplayName([NotNull] JObject user)
49+
{
50+
if (user == null)
51+
{
52+
throw new ArgumentNullException(nameof(user));
53+
}
54+
55+
return user["data"]?[0]?.Value<string>("display_name");
56+
}
57+
58+
/// <summary>
59+
/// Gets the email corresponding to the authenticated user.
60+
/// </summary>
61+
public static string GetEmail([NotNull] JObject user)
62+
{
63+
if (user == null)
64+
{
65+
throw new ArgumentNullException(nameof(user));
66+
}
67+
68+
return user["data"]?[0]?.Value<string>("email");
69+
}
70+
71+
/// <summary>
72+
/// Gets the account type corresponding to the authenticated user.
73+
/// </summary>
74+
public static string GetType([NotNull] JObject user)
75+
{
76+
if (user == null)
77+
{
78+
throw new ArgumentNullException(nameof(user));
79+
}
80+
81+
return user["data"]?[0]?.Value<string>("type");
82+
}
83+
84+
/// <summary>
85+
/// Gets the broadcaster type corresponding to the authenticated user.
86+
/// </summary>
87+
public static string GetBroadcastType([NotNull] JObject user)
88+
{
89+
if (user == null)
90+
{
91+
throw new ArgumentNullException(nameof(user));
92+
}
93+
94+
return user["data"]?[0]?.Value<string>("broadcaster_type");
95+
}
96+
97+
/// <summary>
98+
/// Gets the channel description corresponding to the authenticated user.
99+
/// </summary>
100+
public static string GetDescription([NotNull] JObject user)
101+
{
102+
if (user == null)
103+
{
104+
throw new ArgumentNullException(nameof(user));
105+
}
106+
107+
return user["data"]?[0]?.Value<string>("description");
108+
}
109+
110+
/// <summary>
111+
/// Gets the profile image url corresponding to the authenticated user.
112+
/// </summary>
113+
public static string GetProfileImageUrl([NotNull] JObject user)
114+
{
115+
if (user == null)
116+
{
117+
throw new ArgumentNullException(nameof(user));
118+
}
119+
120+
return user["data"]?[0]?.Value<string>("profile_image_url");
121+
}
122+
123+
/// <summary>
124+
/// Gets the offline image url corresponding to the authenticated user.
125+
/// </summary>
126+
public static string GetOfflineImageUrl([NotNull] JObject user)
127+
{
128+
if (user == null)
129+
{
130+
throw new ArgumentNullException(nameof(user));
131+
}
132+
133+
return user["data"]?[0]?.Value<string>("offline_image_url");
43134
}
44135
}
45136
}

src/AspNet.Security.OAuth.Twitch/TwitchAuthenticationOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public TwitchAuthenticationOptions()
2626
TokenEndpoint = TwitchAuthenticationDefaults.TokenEndpoint;
2727
UserInformationEndpoint = TwitchAuthenticationDefaults.UserInformationEndpoint;
2828

29-
Scope.Add("user_read");
29+
Scope.Add("user:read:email");
3030
}
3131
}
3232
}

0 commit comments

Comments
 (0)