diff --git a/src/Nullinside.Api/Shared/Support/TwitchBotLoginErrors.cs b/src/Nullinside.Api.Common/Twitch/Support/TwitchBotLoginErrors.cs similarity index 100% rename from src/Nullinside.Api/Shared/Support/TwitchBotLoginErrors.cs rename to src/Nullinside.Api.Common/Twitch/Support/TwitchBotLoginErrors.cs diff --git a/src/Nullinside.Api.Common/Twitch/TwitchApiProxy.cs b/src/Nullinside.Api.Common/Twitch/TwitchApiProxy.cs index e720d30..56a3ec4 100644 --- a/src/Nullinside.Api.Common/Twitch/TwitchApiProxy.cs +++ b/src/Nullinside.Api.Common/Twitch/TwitchApiProxy.cs @@ -14,6 +14,7 @@ using TwitchLib.Api.Helix.Models.Moderation.GetModerators; using TwitchLib.Api.Helix.Models.Streams.GetStreams; using TwitchLib.Api.Helix.Models.Users.GetUsers; +using TwitchLib.Api.Interfaces; using Stream = TwitchLib.Api.Helix.Models.Streams.GetStreams.Stream; @@ -73,7 +74,7 @@ public TwitchApiProxy(string token, string refreshToken, DateTime tokenExpires) /// public async Task CreateAccessToken(string code, CancellationToken token = new()) { - TwitchAPI api = GetApi(); + ITwitchAPI api = GetApi(); AuthCodeResponse? response = await api.Auth.GetAccessTokenFromCodeAsync(code, ClientSecret, ClientRedirect); if (null == response) { return null; @@ -90,7 +91,7 @@ public TwitchApiProxy(string token, string refreshToken, DateTime tokenExpires) /// public async Task RefreshAccessToken(CancellationToken token = new()) { try { - TwitchAPI api = GetApi(); + ITwitchAPI api = GetApi(); RefreshResponse? response = await api.Auth.RefreshAuthTokenAsync(OAuth?.RefreshToken, ClientSecret, ClientId); if (null == response) { return null; @@ -116,7 +117,7 @@ public TwitchApiProxy(string token, string refreshToken, DateTime tokenExpires) /// public async Task<(string? id, string? username)> GetUser(CancellationToken token = new()) { return await Retry.Execute(async () => { - TwitchAPI api = GetApi(); + ITwitchAPI api = GetApi(); GetUsersResponse? response = await api.Helix.Users.GetUsersAsync(); if (null == response) { return (null, null); @@ -130,7 +131,7 @@ public TwitchApiProxy(string token, string refreshToken, DateTime tokenExpires) /// public async Task GetUserEmail(CancellationToken token = new()) { return await Retry.Execute(async () => { - TwitchAPI api = GetApi(); + ITwitchAPI api = GetApi(); GetUsersResponse? response = await api.Helix.Users.GetUsersAsync(); if (null == response) { return null; @@ -175,7 +176,7 @@ public async Task> GetUserModChannels(string public async Task> BanChannelUsers(string channelId, string botId, IEnumerable<(string Id, string Username)> users, string reason, CancellationToken token = new()) { return await Retry.Execute(async () => { - TwitchAPI api = GetApi(); + ITwitchAPI api = GetApi(); var bannedUsers = new List(); foreach ((string Id, string Username) user in users) { @@ -209,7 +210,7 @@ public async Task> BanChannelUsers(string channelId, str public async Task> GetChannelUsers(string channelId, string botId, CancellationToken token = new()) { return await Retry.Execute(async () => { - TwitchAPI api = GetApi(); + ITwitchAPI api = GetApi(); var chatters = new List(); string? cursor = null; int total = 0; @@ -231,7 +232,7 @@ public async Task> GetChannelUsers(string channelId, string /// public async Task> GetChannelsLive(IEnumerable userIds) { - TwitchAPI api = GetApi(); + ITwitchAPI api = GetApi(); // We can only query 100 at a time, so throttle the search. var liveUsers = new List(); @@ -256,7 +257,7 @@ public async Task> GetChannelsLive(IEnumerable userI /// public async Task> GetChannelMods(string channelId, CancellationToken token = new()) { return await Retry.Execute(async () => { - TwitchAPI api = GetApi(); + ITwitchAPI api = GetApi(); var results = new List(); GetModeratorsResponse? response = null; @@ -283,7 +284,7 @@ public async Task> GetChannelsLive(IEnumerable userI /// public async Task AddChannelMod(string channelId, string userId, CancellationToken token = new()) { return await Retry.Execute(async () => { - TwitchAPI api = GetApi(); + ITwitchAPI api = GetApi(); await api.Helix.Moderation.AddChannelModeratorAsync(channelId, userId); return true; }, Retries, token); @@ -293,7 +294,7 @@ public async Task> GetChannelsLive(IEnumerable userI /// Gets a new instance of the . /// /// A new instance of the . - private TwitchAPI GetApi() { + protected ITwitchAPI GetApi() { var api = new TwitchAPI { Settings = { ClientId = ClientId, diff --git a/src/Nullinside.Api/Controllers/TwitchBotController.cs b/src/Nullinside.Api/Controllers/TwitchBotController.cs deleted file mode 100644 index 38b0a62..0000000 --- a/src/Nullinside.Api/Controllers/TwitchBotController.cs +++ /dev/null @@ -1,86 +0,0 @@ -using log4net; - -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -using Nullinside.Api.Common.Twitch; -using Nullinside.Api.Model; -using Nullinside.Api.Model.Shared; -using Nullinside.Api.Shared.Support; - -namespace Nullinside.Api.Controllers; - -/// -/// Handles user authentication and authorization. -/// -[ApiController] -[Route("[controller]")] -public class TwitchBotController : ControllerBase { - /// - /// The application's configuration file. - /// - private readonly IConfiguration _configuration; - - /// - /// The nullinside database. - /// - private readonly INullinsideContext _dbContext; - - /// - /// The logger. - /// - private readonly ILog _logger = LogManager.GetLogger(typeof(TwitchBotController)); - - /// - /// Initializes a new instance of the class. - /// - /// The application's configuration file. - /// The nullinside database. - public TwitchBotController(IConfiguration configuration, INullinsideContext dbContext) { - _configuration = configuration; - _dbContext = dbContext; - } - - /// - /// **NOT CALLED BY SITE OR USERS** This endpoint is called by twitch as part of their oauth workflow. It - /// redirects users back to the nullinside website. - /// - /// The credentials provided by twitch. - /// The twitch api. - /// The cancellation token. - /// - /// A redirect to the nullinside website. - /// Errors: - /// 2 = Internal error generating token. - /// 3 = Code was invalid - /// 4 = Twitch account has no email - /// - [AllowAnonymous] - [HttpGet] - [Route("login")] - public async Task TwitchLogin([FromQuery] string code, [FromServices] ITwitchApiProxy api, - CancellationToken token) { - string? siteUrl = _configuration.GetValue("Api:SiteUrl"); - if (null == await api.CreateAccessToken(code, token)) { - return Redirect($"{siteUrl}/twitch-bot/config?error={TwitchBotLoginErrors.TwitchErrorWithToken}"); - } - - string? email = await api.GetUserEmail(token); - if (string.IsNullOrWhiteSpace(email)) { - return Redirect($"{siteUrl}/twitch-bot/config?error={TwitchBotLoginErrors.TwitchAccountHasNoEmail}"); - } - - (string? id, string? username) user = await api.GetUser(token); - if (string.IsNullOrWhiteSpace(user.username) || string.IsNullOrWhiteSpace(user.id)) { - return Redirect($"{siteUrl}/twitch-bot/config?error={TwitchBotLoginErrors.InternalError}"); - } - - string? bearerToken = await UserHelpers.GetTokenAndSaveToDatabase(_dbContext, email, token, api.OAuth?.AccessToken, - api.OAuth?.RefreshToken, api.OAuth?.ExpiresUtc, user.username, user.id); - if (string.IsNullOrWhiteSpace(bearerToken)) { - return Redirect($"{siteUrl}/twitch-bot/config?error={TwitchBotLoginErrors.InternalError}"); - } - - return Redirect($"{siteUrl}/twitch-bot/config?token={bearerToken}"); - } -} \ No newline at end of file diff --git a/src/Nullinside.Api/Controllers/UserController.cs b/src/Nullinside.Api/Controllers/UserController.cs index 50e2780..7d30944 100644 --- a/src/Nullinside.Api/Controllers/UserController.cs +++ b/src/Nullinside.Api/Controllers/UserController.cs @@ -96,7 +96,7 @@ public async Task Login([FromForm] GoogleOpenIdToken creds, Cance [HttpGet] [Route("twitch-login")] public async Task TwitchLogin([FromQuery] string code, [FromServices] ITwitchApiProxy api, - CancellationToken token) { + CancellationToken token = new()) { string? siteUrl = _configuration.GetValue("Api:SiteUrl"); if (null == await api.CreateAccessToken(code, token)) { return Redirect($"{siteUrl}/user/login?error=3");