private void ValidateAccessTokenInput(TraktDevice device, string clientId, string clientSecret) { if (device == null) { throw new ArgumentNullException(nameof(device), "device must not be null"); } if (device.IsExpiredUnused) { throw new ArgumentException("device code expired unused", nameof(device)); } if (!device.IsValid) { throw new ArgumentException("device not valid", nameof(device)); } if (device.DeviceCode.ContainsSpace()) { throw new ArgumentException("device code not valid", nameof(device.DeviceCode)); } if (string.IsNullOrEmpty(clientId) || clientId.ContainsSpace()) { throw new ArgumentException("client id not valid", nameof(clientId)); } if (string.IsNullOrEmpty(clientSecret) || clientSecret.ContainsSpace()) { throw new ArgumentException("client secret not valid", nameof(clientSecret)); } }
/// <summary> /// Polls for a new access token. Assigns the returned <see cref="TraktAuthorization" /> instance to <see cref="TraktAuthentication.Authorization" />, if successful. /// <para> /// See <a href="http://docs.trakt.apiary.io/#reference/authentication-devices/get-token/poll-for-the-access_token">"Trakt API Doc - Devices: Get Token"</a> for more information. /// </para> /// <para> /// See also <seealso cref="PollForAuthorizationAsync()" />, /// <seealso cref="PollForAuthorizationAsync(TraktDevice)" /> and /// <seealso cref="PollForAuthorizationAsync(TraktDevice, string)" />. /// See also <seealso cref="TraktAuthentication.Authorization" />. /// </para> /// </summary> /// <param name="device">The <see cref="TraktDevice" />, which will be used for the request. See also <seealso cref="TraktAuthentication.Device "/>.</param> /// <param name="clientId">The Trakt Client ID, which will be used for the request. See also <seealso cref="TraktAuthentication.ClientId" />.</param> /// <param name="clientSecret">The Trakt Client Secret, which will be used for the request. See also <seealso cref="TraktAuthentication.ClientSecret" />.</param> /// <returns>A new <see cref="TraktAuthorization" /> instance, which contains a new access and refresh token.</returns> /// <exception cref="TraktAuthenticationDeviceException"> /// Thrown, if the given device has an invalid device code. /// Thrown, if the user code of the given device was already approved by the user. /// Thrown, if the given device code is already expired unused. /// Thrown, if the user explicitly denied the user code of the given device. /// </exception> /// <exception cref="TraktException">Thrown, if the request fails.</exception> /// <exception cref="ArgumentException"> /// Thrown, if the given device is null, or already expired unused or invalid or its user code contains spaces. /// Thrown, if the given client id is null, empty or contains spaces. /// Thrown, if the given client secret is null, empty or contains spaces. /// </exception> public async Task <TraktAuthorization> PollForAuthorizationAsync(TraktDevice device, string clientId, string clientSecret) { ValidateAccessTokenInput(device, clientId, clientSecret); var postContent = $"{{ \"code\": \"{device.DeviceCode}\", \"client_id\": \"{clientId}\", \"client_secret\": \"{clientSecret}\" }}"; var httpClient = TraktConfiguration.HTTP_CLIENT ?? new HttpClient(); SetDefaultRequestHeaders(httpClient); var tokenUrl = $"{Client.Configuration.BaseUrl}{TraktConstants.OAuthDeviceTokenUri}"; HttpStatusCode responseCode = default(HttpStatusCode); string responseContent = string.Empty; string reasonPhrase = string.Empty; int totalExpiredSeconds = 0; while (totalExpiredSeconds < device.ExpiresInSeconds) { var content = new StringContent(postContent, Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync(tokenUrl, content).ConfigureAwait(false); responseCode = response.StatusCode; reasonPhrase = response.ReasonPhrase; responseContent = response.Content != null ? await response.Content.ReadAsStringAsync() : string.Empty; if (responseCode == HttpStatusCode.OK) // Success { var token = default(TraktAuthorization); if (!string.IsNullOrEmpty(responseContent)) { token = Json.Deserialize <TraktAuthorization>(responseContent); } Client.Authentication.Authorization = token; return(token); } else if (responseCode == HttpStatusCode.BadRequest) // Pending { await Task.Delay(device.IntervalInSeconds * 1000); totalExpiredSeconds += device.IntervalInSeconds; continue; } switch (responseCode) { case HttpStatusCode.NotFound: throw new TraktAuthenticationDeviceException("Not Found - invalid device_code") { StatusCode = responseCode, RequestUrl = tokenUrl, RequestBody = postContent, ServerReasonPhrase = reasonPhrase }; case HttpStatusCode.Conflict: // Already Used throw new TraktAuthenticationDeviceException("Already Used - user already approved this code") { StatusCode = responseCode, RequestUrl = tokenUrl, RequestBody = postContent, ServerReasonPhrase = reasonPhrase }; case HttpStatusCode.Gone: // Expired throw new TraktAuthenticationDeviceException("Expired - the tokens have expired, restart the process") { StatusCode = responseCode, RequestUrl = tokenUrl, RequestBody = postContent, ServerReasonPhrase = reasonPhrase }; case (HttpStatusCode)418: // Denied throw new TraktAuthenticationDeviceException("Denied - user explicitly denied this code") { StatusCode = (HttpStatusCode)418, RequestUrl = tokenUrl, RequestBody = postContent, ServerReasonPhrase = reasonPhrase }; case (HttpStatusCode)429: // Slow Down throw new TraktAuthenticationDeviceException("Slow Down - your app is polling too quickly") { StatusCode = (HttpStatusCode)429, RequestUrl = tokenUrl, RequestBody = postContent, ServerReasonPhrase = reasonPhrase }; } await ErrorHandlingAsync(response, tokenUrl, postContent, true); break; } throw new TraktAuthenticationDeviceException("unknown exception") { ServerReasonPhrase = reasonPhrase }; }
/// <summary> /// Polls for a new access token. Uses the current <see cref="TraktAuthentication.ClientSecret" /> for the request. /// Assigns the returned <see cref="TraktAuthorization" /> instance to <see cref="TraktAuthentication.Authorization" />, if successful. /// <para> /// See <a href="http://docs.trakt.apiary.io/#reference/authentication-devices/get-token/poll-for-the-access_token">"Trakt API Doc - Devices: Get Token"</a> for more information. /// </para> /// <para> /// See also <seealso cref="PollForAuthorizationAsync()" />, /// <seealso cref="PollForAuthorizationAsync(TraktDevice)" /> and /// <seealso cref="PollForAuthorizationAsync(TraktDevice, string, string)" />. /// See also <seealso cref="TraktAuthentication.Authorization" />. /// </para> /// </summary> /// <param name="device">The <see cref="TraktDevice" />, which will be used for the request. See also <seealso cref="TraktAuthentication.Device "/>.</param> /// <param name="clientId">The Trakt Client ID, which will be used for the request. See also <seealso cref="TraktAuthentication.ClientId" />.</param> /// <returns>A new <see cref="TraktAuthorization" /> instance, which contains a new access and refresh token.</returns> /// <exception cref="TraktAuthenticationDeviceException"> /// Thrown, if the given device has an invalid device code. /// Thrown, if the user code of the given device was already approved by the user. /// Thrown, if the given device code is already expired unused. /// Thrown, if the user explicitly denied the user code of the given device. /// </exception> /// <exception cref="TraktException">Thrown, if the request fails.</exception> /// <exception cref="ArgumentException"> /// Thrown, if the given device is null, or already expired unused or invalid or its user code contains spaces. /// Thrown, if the given client id is null, empty or contains spaces. /// Thrown, if the current client secret is null, empty or contains spaces. /// </exception> public async Task <TraktAuthorization> PollForAuthorizationAsync(TraktDevice device, string clientId) => await PollForAuthorizationAsync(device, clientId, Client.ClientSecret);