Permalink
Browse files

New trakt auth - issue #713

  • Loading branch information...
hidden4003 committed Apr 4, 2018
1 parent 1e3ea45 commit 9e47308685d374ed660230e0047a29e38001fac0
@@ -1154,6 +1154,24 @@ public string EnterTraktPIN(string pin)
}
}
public CL_TraktDeviceCode GetTraktDeviceCode()
{
try
{
var response = TraktTVHelper.GetTraktDeviceCode();
return new CL_TraktDeviceCode
{
VerificationUrl = response.VerificationUrl,
UserCode = response.UserCode
};
}
catch (Exception ex)
{
logger.Error(ex, "Error in GetTraktDeviceCode: " + ex.ToString());
return null;
}
}
#endregion
#region MAL
@@ -0,0 +1,16 @@
using System.Runtime.Serialization;
namespace Shoko.Server.Providers.TraktTV.Contracts
{
[DataContract]
public class TraktAuthDeviceCode
{
[DataMember(Name = "client_id")]
public string ClientID { get; set; }
public TraktAuthDeviceCode()
{
ClientID = TraktConstants.ClientID;
}
}
}
@@ -0,0 +1,23 @@
using System.Runtime.Serialization;
namespace Shoko.Server.Providers.TraktTV.Contracts
{
[DataContract]
public class TraktAuthDeviceCodePoll
{
[DataMember(Name = "client_id")]
public string ClientID { get; set; }
[DataMember(Name = "client_secret")]
public string ClientSecret { get; set; }
[DataMember(Name = "code")]
public string DeviceCode { get; set; }
public TraktAuthDeviceCodePoll()
{
ClientID = TraktConstants.ClientID;
ClientSecret = TraktConstants.ClientSecret;
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Runtime.Serialization;
namespace Shoko.Server.Providers.TraktTV.Contracts
{
[DataContract]
public class TraktAuthDeviceCodeToken
{
[DataMember(Name = "device_code")]
public string DeviceCode { get; set; }
[DataMember(Name = "user_code")]
public string UserCode { get; set; }
[DataMember(Name = "verification_url")]
public string VerificationUrl { get; set; }
[DataMember(Name = "expires_in")]
public int ExpiresIn { get; set; }
[DataMember(Name = "interval")]
public int Interval { get; set; }
public DateTime RequestTime;
public TraktAuthDeviceCodeToken()
{
RequestTime = DateTime.Now;
}
}
}
@@ -8,7 +8,7 @@ public static class TraktConstants
public const string ClientID = "a20707fa9666bea4acd86bc6ea2123bd6ffdbe996b4927cfdba96f4d3fca3042";
public const string ClientSecret = "7ef5eec766070fa0b34a4a4a2fea2ad0ddbe9bb1bc1e8eb621551c52fb288739";
public const string BaseAPIURL = @"https://api-v2launch.trakt.tv";
public const string BaseAPIURL = @"https://api.trakt.tv";
public const string BaseWebsiteURL = @"https://trakt.tv";
public const string PINAuth = BaseWebsiteURL + @"/pin/5309";
@@ -4,6 +4,8 @@
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NHibernate;
using NLog;
using Shoko.Commons.Extensions;
@@ -319,6 +321,125 @@ public static string EnterTraktPIN(string pin)
#endregion
#region New Authorization
/*
* Trakt Auth Flow
*
* 1. Generate codes. Your app calls /oauth/device/code to generate new codes. Save this entire response for later use.
* 2. Display the code. Display the user_code and instruct the user to visit the verification_url on their computer or mobile device.
* 3. Poll for authorization. Poll the /oauth/device/token method to see if the user successfully authorizes your app.
* Use the device_code and poll at the interval (in seconds) to check if the user has authorized your app.
* Use expires_in to stop polling after that many seconds, and gracefully instruct the user to restart the process.
* It is important to poll at the correct interval and also stop polling when expired.
* Status Codes
* This method will send various HTTP status codes that you should handle accordingly.
* Code Description
* 200 Success - save the access_token
* 400 Pending - waiting for the user to authorize your app
* 404 Not Found - invalid device_code
* 409 Already Used - user already approved this code
* 410 Expired - the tokens have expired, restart the process
* 418 Denied - user explicitly denied this code
* 429 Slow Down - your app is polling too quickly
* 4. Successful authorization.
* When you receive a 200 success response, save the access_token so your app can authenticate the user in methods that require it.
* The access_token is valid for 3 months.
*/
public static TraktAuthDeviceCodeToken GetTraktDeviceCode()
{
try
{
var obj = new TraktAuthDeviceCode();
string json = JSONHelper.Serialize(obj);
Dictionary<string, string> headers = new Dictionary<string, string>();
string retData = string.Empty;
int response = SendData(TraktURIs.OAuthDeviceCode, json, "POST", headers, ref retData);
if (response != TraktStatusCodes.Success && response != TraktStatusCodes.Success_Post)
{
throw new Exception($"Error returned from Trakt: {response}");
}
var deviceCode = retData.FromJSON<TraktAuthDeviceCodeToken>();
Task.Run(() => { TraktAuthPollDeviceToken(deviceCode); });
return deviceCode;
}
catch (Exception ex)
{
logger.Error(ex, "Error in TraktTVHelper.GetTraktDeviceCode: " + ex);
throw ex;
}
}
private static void TraktAuthPollDeviceToken(TraktAuthDeviceCodeToken deviceCode)
{
if (deviceCode == null)
{
return;
}
var task = Task.Run(() => { TraktAutoDeviceTokenWorker(deviceCode); });
if (!task.Wait(TimeSpan.FromSeconds(deviceCode.ExpiresIn)))
{
logger.Error("Error in TraktTVHelper.TraktAuthPollDeviceToken: Timed out");
}
}
private static void TraktAutoDeviceTokenWorker(TraktAuthDeviceCodeToken deviceCode)
{
try
{
var pollInterval = TimeSpan.FromSeconds(deviceCode.Interval);
var obj = new TraktAuthDeviceCodePoll
{
DeviceCode = deviceCode.DeviceCode
};
string json = JSONHelper.Serialize(obj);
Dictionary<string, string> headers = new Dictionary<string, string>();
while (true)
{
Thread.Sleep(pollInterval);
headers.Clear();
string retData = string.Empty;
int response = SendData(TraktURIs.OAuthDeviceToken, json, "POST", headers, ref retData);
if (response == TraktStatusCodes.Success)
{
var tokenResponse = retData.FromJSON<TraktAuthToken>();
ServerSettings.Trakt_AuthToken = tokenResponse.AccessToken;
ServerSettings.Trakt_RefreshToken = tokenResponse.RefreshToken;
long.TryParse(tokenResponse.CreatedAt, out long createdAt);
long.TryParse(tokenResponse.ExpiresIn, out long validity);
long expireDate = createdAt + validity;
ServerSettings.Trakt_TokenExpirationDate = expireDate.ToString();
break;
}
if (response == TraktStatusCodes.Rate_Limit_Exceeded)
{
//Temporarily increase poll interval
Thread.Sleep(TimeSpan.FromSeconds(1));
}
if (response != TraktStatusCodes.Bad_Request && response != TraktStatusCodes.Rate_Limit_Exceeded)
{
throw new Exception($"Error returned from Trakt: {response}");
}
}
}
catch (Exception ex)
{
logger.Error(ex, "Error in TraktTVHelper.TraktAuthDeviceCodeToken: " + ex);
throw ex;
}
}
#endregion
#region Linking
public static string LinkAniDBTrakt(int animeID, EpisodeType aniEpType, int aniEpNumber, string traktID,
@@ -4,6 +4,9 @@ public static class TraktURIs
{
public const string Oauth = TraktConstants.BaseAPIURL + @"/oauth/token";
public const string OAuthDeviceCode = TraktConstants.BaseAPIURL + @"/oauth/device/code";
public const string OAuthDeviceToken = TraktConstants.BaseAPIURL + @"/oauth/device/token";
// Website links
// http://docs.trakt.apiary.io/#introduction/website-media-links
@@ -411,6 +411,9 @@
<Compile Include="Plex\PlexHelper.cs" />
<Compile Include="Plex\Libraries\SVR_Directory.cs" />
<Compile Include="Plex\Collection\SVR_PlexLibrary.cs" />
<Compile Include="Providers\TraktTV\Contracts\Auth\TraktAuthDeviceCode.cs" />
<Compile Include="Providers\TraktTV\Contracts\Auth\TraktAuthDeviceCodePoll.cs" />
<Compile Include="Providers\TraktTV\Contracts\Auth\TraktAuthDeviceCodeToken.cs" />
<Compile Include="Providers\TvDB\TvDBApiHelper.cs" />
<Compile Include="Providers\TvDB\TvDBRateLimiter.cs" />
<Compile Include="Renamer\IRenamer.cs" />

0 comments on commit 9e47308

Please sign in to comment.