Added UserCredential override with TokenUpdated event
Added Success code parser Updated packages versions
This commit is contained in:
@@ -5,102 +5,106 @@ using Newtonsoft.Json;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace YouTube.Authorization
|
namespace YouTube.Authorization
|
||||||
{
|
{
|
||||||
public static class AuthorizationHelpers
|
public static class AuthorizationHelpers
|
||||||
{
|
{
|
||||||
public static Uri Endpoint => "https://accounts.google.com/o/oauth2/approval".ToUri();
|
public static Uri Endpoint => "https://accounts.google.com/o/oauth2/approval".ToUri();
|
||||||
const string refreshEndpoint = "https://oauth2.googleapis.com/token";
|
private const string refreshEndpoint = "https://oauth2.googleapis.com/token";
|
||||||
const string tokenEndpoint = "https://www.googleapis.com/oauth2/v4/token";
|
private const string tokenEndpoint = "https://www.googleapis.com/oauth2/v4/token";
|
||||||
const string redirectUrl = "urn:ietf:wg:oauth:2.0:oob";
|
private const string redirectUrl = "urn:ietf:wg:oauth:2.0:oob";
|
||||||
|
|
||||||
public static Uri FormQueryString(ClientSecrets clientSecrets, params string[] scopes)
|
public static Uri FormQueryString(ClientSecrets clientSecrets, params string[] scopes)
|
||||||
{
|
{
|
||||||
string clientId = Uri.EscapeDataString(clientSecrets.ClientId);
|
string clientId = Uri.EscapeDataString(clientSecrets.ClientId);
|
||||||
string scopeStr = string.Join(" ", scopes);
|
string scopeStr = string.Join(" ", scopes);
|
||||||
|
|
||||||
return $"https://accounts.google.com/o/oauth2/auth?client_id={clientId}&redirect_uri={redirectUrl}&response_type=code&scope={scopeStr}".ToUri();
|
return $"https://accounts.google.com/o/oauth2/auth?client_id={clientId}&redirect_uri={redirectUrl}&response_type=code&scope={scopeStr}".ToUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<UserCredential> ExchangeToken(ClientSecrets clientSecrets, string responseToken)
|
public static string ParseSuccessCode(string responseData) =>
|
||||||
{
|
new Regex(@"(?<=code=)(.*?)(?=&)").Match(responseData).Value;
|
||||||
using HttpClient client = new HttpClient();
|
|
||||||
|
|
||||||
Dictionary<string, string> requestBody = new Dictionary<string, string>
|
public static async Task<UserCredential> ExchangeToken(ClientSecrets clientSecrets, string responseToken)
|
||||||
{
|
{
|
||||||
{ "code", responseToken },
|
using HttpClient client = new HttpClient();
|
||||||
{ "redirect_uri", redirectUrl },
|
|
||||||
{ "grant_type", "authorization_code" },
|
|
||||||
{ "client_id", clientSecrets.ClientId },
|
|
||||||
{ "client_secret", clientSecrets.ClientSecret }
|
|
||||||
};
|
|
||||||
|
|
||||||
HttpResponseMessage response = await client.PostAsync(tokenEndpoint, new FormUrlEncodedContent(requestBody));
|
Dictionary<string, string> requestBody = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "code", responseToken },
|
||||||
|
{ "redirect_uri", redirectUrl },
|
||||||
|
{ "grant_type", "authorization_code" },
|
||||||
|
{ "client_id", clientSecrets.ClientId },
|
||||||
|
{ "client_secret", clientSecrets.ClientSecret }
|
||||||
|
};
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
HttpResponseMessage response = await client.PostAsync(tokenEndpoint, new FormUrlEncodedContent(requestBody));
|
||||||
throw new Exception(await response.Content.ReadAsStringAsync());
|
|
||||||
|
|
||||||
string responseString = await response.Content.ReadAsStringAsync();
|
if (!response.IsSuccessStatusCode)
|
||||||
dynamic responseData = JsonConvert.DeserializeObject(responseString);
|
throw new Exception(await response.Content.ReadAsStringAsync());
|
||||||
|
|
||||||
TokenResponse tokenResponse = new TokenResponse
|
string responseString = await response.Content.ReadAsStringAsync();
|
||||||
{
|
dynamic responseData = JsonConvert.DeserializeObject(responseString);
|
||||||
AccessToken = responseData.access_token,
|
|
||||||
ExpiresInSeconds = responseData.expires_in,
|
|
||||||
RefreshToken = responseData.refresh_token,
|
|
||||||
Scope = responseData.scope,
|
|
||||||
TokenType = responseData.token_type
|
|
||||||
};
|
|
||||||
|
|
||||||
AuthorizationCodeFlow authorizationCodeFlow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
|
TokenResponse tokenResponse = new TokenResponse
|
||||||
{
|
{
|
||||||
ClientSecrets = clientSecrets,
|
AccessToken = responseData.access_token,
|
||||||
Scopes = responseData.scope.ToString().Split(' ')
|
ExpiresInSeconds = responseData.expires_in,
|
||||||
});
|
RefreshToken = responseData.refresh_token,
|
||||||
|
Scope = responseData.scope,
|
||||||
|
TokenType = responseData.token_type
|
||||||
|
};
|
||||||
|
|
||||||
UserCredential credential = new UserCredential(authorizationCodeFlow, "user", tokenResponse);
|
AuthorizationCodeFlow authorizationCodeFlow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
|
||||||
|
{
|
||||||
|
ClientSecrets = clientSecrets,
|
||||||
|
Scopes = responseData.scope.ToString().Split(' ')
|
||||||
|
});
|
||||||
|
|
||||||
return credential;
|
UserCredential credential = new UserCredential(authorizationCodeFlow, "user", tokenResponse);
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<UserCredential> RestoreUser(ClientSecrets clientSecrets, string refreshToken)
|
return credential;
|
||||||
{
|
}
|
||||||
using HttpClient client = new HttpClient();
|
|
||||||
|
|
||||||
Dictionary<string, string> requestBody = new Dictionary<string, string>
|
public static async Task<UserCredential> RestoreUser(ClientSecrets clientSecrets, string refreshToken)
|
||||||
{
|
{
|
||||||
{ "client_id", clientSecrets.ClientId },
|
using HttpClient client = new HttpClient();
|
||||||
{ "client_secret", clientSecrets.ClientSecret },
|
|
||||||
{ "refresh_token", refreshToken },
|
|
||||||
{ "grant_type", "refresh_token" }
|
|
||||||
};
|
|
||||||
|
|
||||||
HttpResponseMessage response = await client.PostAsync(refreshEndpoint, new FormUrlEncodedContent(requestBody));
|
Dictionary<string, string> requestBody = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "client_id", clientSecrets.ClientId },
|
||||||
|
{ "client_secret", clientSecrets.ClientSecret },
|
||||||
|
{ "refresh_token", refreshToken },
|
||||||
|
{ "grant_type", "refresh_token" }
|
||||||
|
};
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
HttpResponseMessage response = await client.PostAsync(refreshEndpoint, new FormUrlEncodedContent(requestBody));
|
||||||
throw new Exception(await response.Content.ReadAsStringAsync());
|
|
||||||
|
|
||||||
string responseString = await response.Content.ReadAsStringAsync();
|
if (!response.IsSuccessStatusCode)
|
||||||
dynamic responseData = JsonConvert.DeserializeObject(responseString);
|
throw new Exception(await response.Content.ReadAsStringAsync());
|
||||||
|
|
||||||
TokenResponse tokenResponse = new TokenResponse
|
string responseString = await response.Content.ReadAsStringAsync();
|
||||||
{
|
dynamic responseData = JsonConvert.DeserializeObject(responseString);
|
||||||
AccessToken = responseData.access_token,
|
|
||||||
ExpiresInSeconds = responseData.expires_in,
|
|
||||||
RefreshToken = refreshToken,
|
|
||||||
TokenType = responseData.token_type
|
|
||||||
};
|
|
||||||
|
|
||||||
AuthorizationCodeFlow authorizationCodeFlow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
|
TokenResponse tokenResponse = new TokenResponse
|
||||||
{
|
{
|
||||||
ClientSecrets = clientSecrets
|
AccessToken = responseData.access_token,
|
||||||
});
|
ExpiresInSeconds = responseData.expires_in,
|
||||||
|
RefreshToken = refreshToken,
|
||||||
|
TokenType = responseData.token_type
|
||||||
|
};
|
||||||
|
|
||||||
UserCredential credential = new UserCredential(authorizationCodeFlow, "user", tokenResponse);
|
AuthorizationCodeFlow authorizationCodeFlow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
|
||||||
|
{
|
||||||
|
ClientSecrets = clientSecrets
|
||||||
|
});
|
||||||
|
|
||||||
return credential;
|
UserCredential credential = new UserCredential(authorizationCodeFlow, "user", tokenResponse);
|
||||||
}
|
|
||||||
}
|
return credential;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using Google.Apis.Auth.OAuth2.Flows;
|
||||||
|
using Google.Apis.Auth.OAuth2.Responses;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace YouTube.Authorization
|
||||||
|
{
|
||||||
|
public class UserCredential : Google.Apis.Auth.OAuth2.UserCredential
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event is fired when new refresh token is recieved and the old one is no loger valid
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler RefreshTokenUpdated;
|
||||||
|
|
||||||
|
/// <summary>Constructs a new credential instance.</summary>
|
||||||
|
/// <param name="flow">Authorization code flow.</param>
|
||||||
|
/// <param name="userId">User identifier.</param>
|
||||||
|
/// <param name="token">An initial token for the user.</param>
|
||||||
|
public UserCredential(IAuthorizationCodeFlow flow, string userId, TokenResponse token) : base(flow, userId, token) { }
|
||||||
|
|
||||||
|
/// <summary>Constructs a new credential instance.</summary>
|
||||||
|
/// <param name="flow">Authorization code flow.</param>
|
||||||
|
/// <param name="userId">User identifier.</param>
|
||||||
|
/// <param name="token">An initial token for the user.</param>
|
||||||
|
/// <param name="quotaProjectId">The ID of the project associated
|
||||||
|
/// to this credential for the purposes of quota calculation and billing. Can be null.</param>
|
||||||
|
public UserCredential(IAuthorizationCodeFlow flow, string userId, TokenResponse token, string quotaProjectId) : base(flow, userId, token, quotaProjectId) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Refreshes the token by calling to
|
||||||
|
/// <see cref="Google.Apis.Auth.OAuth2.Flows.IAuthorizationCodeFlow.RefreshTokenAsync"/>.
|
||||||
|
/// Then it updates the <see cref="TokenResponse"/> with the new token instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="taskCancellationToken">Cancellation token to cancel an operation.</param>
|
||||||
|
/// <returns><c>true</c> if the token was refreshed.</returns>
|
||||||
|
public new async Task<bool> RefreshTokenAsync(CancellationToken taskCancellationToken)
|
||||||
|
{
|
||||||
|
if (Token.RefreshToken == null)
|
||||||
|
{
|
||||||
|
Logger.Warning("Refresh token is null, can't refresh the token!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's possible that two concurrent calls will be made to refresh the token, in that case the last one
|
||||||
|
// will win.
|
||||||
|
var newToken = await Flow.RefreshTokenAsync(UserId, Token.RefreshToken, taskCancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
|
Logger.Info("Access token was refreshed successfully");
|
||||||
|
|
||||||
|
if (newToken.RefreshToken == null)
|
||||||
|
newToken.RefreshToken = Token.RefreshToken;
|
||||||
|
|
||||||
|
Token = newToken;
|
||||||
|
|
||||||
|
RefreshTokenUpdated?.Invoke(this, null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,15 +21,15 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AngleSharp" Version="0.13.0" />
|
<PackageReference Include="AngleSharp" Version="0.14.0" />
|
||||||
<PackageReference Include="Google.Apis" Version="1.42.0" />
|
<PackageReference Include="Google.Apis" Version="1.45.0" />
|
||||||
<PackageReference Include="Google.Apis.Auth" Version="1.42.0" />
|
<PackageReference Include="Google.Apis.Auth" Version="1.45.0" />
|
||||||
<PackageReference Include="Google.Apis.Core" Version="1.42.0" />
|
<PackageReference Include="Google.Apis.Core" Version="1.45.0" />
|
||||||
<PackageReference Include="Google.Apis.Oauth2.v2" Version="1.42.0.1602" />
|
<PackageReference Include="Google.Apis.Oauth2.v2" Version="1.45.0.1869" />
|
||||||
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.42.0.1758" />
|
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.45.0.1918" />
|
||||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="all" />
|
||||||
<PackageReference Include="YoutubeExplode" Version="4.7.10" />
|
<PackageReference Include="YoutubeExplode" Version="4.7.16" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user