Spotify Auth working, strongly typed web config
This commit is contained in:
parent
d1563c3365
commit
222c738854
@ -30,7 +30,6 @@ namespace Selector.CLI
|
|||||||
// CONFIG
|
// CONFIG
|
||||||
services.Configure<RootOptions>(options =>
|
services.Configure<RootOptions>(options =>
|
||||||
{
|
{
|
||||||
|
|
||||||
OptionsHelper.ConfigureOptions(options, context.Configuration);
|
OptionsHelper.ConfigureOptions(options, context.Configuration);
|
||||||
});
|
});
|
||||||
var config = OptionsHelper.ConfigureOptions(context.Configuration);
|
var config = OptionsHelper.ConfigureOptions(context.Configuration);
|
||||||
|
@ -38,12 +38,14 @@ namespace Selector.Web.Areas.Identity.Pages.Account.Manage
|
|||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadAsync(ApplicationUser user)
|
private Task LoadAsync(ApplicationUser user)
|
||||||
{
|
{
|
||||||
Input = new InputModel
|
Input = new InputModel
|
||||||
{
|
{
|
||||||
Username = user.LastFmUsername,
|
Username = user.LastFmUsername,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> OnGetAsync()
|
public async Task<IActionResult> OnGetAsync()
|
||||||
@ -74,8 +76,8 @@ namespace Selector.Web.Areas.Identity.Pages.Account.Manage
|
|||||||
|
|
||||||
if (Input.Username != user.LastFmUsername)
|
if (Input.Username != user.LastFmUsername)
|
||||||
{
|
{
|
||||||
user.LastFmUsername = Input.Username;
|
user.LastFmUsername = Input.Username.Trim();
|
||||||
_userManager.UpdateAsync(user);
|
await _userManager.UpdateAsync(user);
|
||||||
|
|
||||||
StatusMessage = "Username changed";
|
StatusMessage = "Username changed";
|
||||||
return RedirectToPage();
|
return RedirectToPage();
|
||||||
|
@ -12,17 +12,27 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
|||||||
using Microsoft.AspNetCore.WebUtilities;
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
|
||||||
using Selector.Model;
|
using Selector.Model;
|
||||||
|
using SpotifyAPI.Web;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Selector.Web.Areas.Identity.Pages.Account.Manage
|
namespace Selector.Web.Areas.Identity.Pages.Account.Manage
|
||||||
{
|
{
|
||||||
public partial class SpotifyModel : PageModel
|
public partial class SpotifyModel : PageModel
|
||||||
{
|
{
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly ILogger<SpotifyModel> Logger;
|
||||||
|
private readonly RootOptions Config;
|
||||||
|
|
||||||
public SpotifyModel(
|
public SpotifyModel(
|
||||||
UserManager<ApplicationUser> userManager)
|
UserManager<ApplicationUser> userManager,
|
||||||
|
ILogger<SpotifyModel> logger,
|
||||||
|
IOptions<RootOptions> config
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
|
Logger = logger;
|
||||||
|
Config = config.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BindProperty]
|
[BindProperty]
|
||||||
@ -46,12 +56,58 @@ namespace Selector.Web.Areas.Identity.Pages.Account.Manage
|
|||||||
|
|
||||||
public async Task<IActionResult> OnPostLinkAsync()
|
public async Task<IActionResult> OnPostLinkAsync()
|
||||||
{
|
{
|
||||||
StatusMessage = "Spotify Linked";
|
var user = await _userManager.GetUserAsync(User);
|
||||||
return RedirectToPage();
|
if (user == null)
|
||||||
|
{
|
||||||
|
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Config.ClientId is null)
|
||||||
|
{
|
||||||
|
Logger.LogError($"Cannot link user, no Spotify client ID");
|
||||||
|
StatusMessage = "Could not link Spotify, no app credentials";
|
||||||
|
return RedirectToPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.ClientSecret is null)
|
||||||
|
{
|
||||||
|
Logger.LogError($"Cannot link user, no Spotify client secret");
|
||||||
|
StatusMessage = "Could not link Spotify, no app credentials";
|
||||||
|
return RedirectToPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
var loginRequest = new LoginRequest(
|
||||||
|
new Uri(Config.SpotifyCallback),
|
||||||
|
Config.ClientId,
|
||||||
|
LoginRequest.ResponseType.Code
|
||||||
|
) {
|
||||||
|
Scope = new[] {
|
||||||
|
Scopes.UserReadPlaybackState
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Redirect(loginRequest.ToUri().ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> OnPostUnlinkAsync()
|
public async Task<IActionResult> OnPostUnlinkAsync()
|
||||||
{
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: stop users Spotify-linked resources (watchers)
|
||||||
|
|
||||||
|
user.SpotifyIsLinked = false;
|
||||||
|
|
||||||
|
user.SpotifyAccessToken = null;
|
||||||
|
user.SpotifyRefreshToken = null;
|
||||||
|
user.SpotifyTokenExpiry = 0;
|
||||||
|
user.SpotifyLastRefresh = default;
|
||||||
|
|
||||||
|
await _userManager.UpdateAsync(user);
|
||||||
|
|
||||||
StatusMessage = "Spotify Unlinked";
|
StatusMessage = "Spotify Unlinked";
|
||||||
return RedirectToPage();
|
return RedirectToPage();
|
||||||
}
|
}
|
||||||
|
83
Selector.Web/Controller/SpotifyController.cs
Normal file
83
Selector.Web/Controller/SpotifyController.cs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
using Selector.Model;
|
||||||
|
|
||||||
|
using SpotifyAPI.Web;
|
||||||
|
|
||||||
|
namespace Selector.Web.Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]/callback")]
|
||||||
|
public class SpotifyController : BaseAuthController
|
||||||
|
{
|
||||||
|
private readonly RootOptions Config;
|
||||||
|
private const string ManageSpotifyPath = "/Identity/Account/Manage/Spotify";
|
||||||
|
|
||||||
|
public SpotifyController(
|
||||||
|
ApplicationDbContext context,
|
||||||
|
IAuthorizationService auth,
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
ILogger<UsersController> logger,
|
||||||
|
IOptions<RootOptions> config
|
||||||
|
) : base(context, auth, userManager, logger)
|
||||||
|
{
|
||||||
|
Config = config.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<RedirectResult> Callback(string code)
|
||||||
|
{
|
||||||
|
if (Config.ClientId is null)
|
||||||
|
{
|
||||||
|
Logger.LogError($"Cannot link user, no Spotify client ID");
|
||||||
|
TempData["StatusMessage"] = "Could not link Spotify, no app credentials";
|
||||||
|
return Redirect(ManageSpotifyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.ClientSecret is null)
|
||||||
|
{
|
||||||
|
Logger.LogError($"Cannot link user, no Spotify client secret");
|
||||||
|
TempData["StatusMessage"] = "Could not link Spotify, no app credentials";
|
||||||
|
return Redirect(ManageSpotifyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = await UserManager.GetUserAsync(User);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("No user returned");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Authorise
|
||||||
|
var response = await new OAuthClient()
|
||||||
|
.RequestToken(
|
||||||
|
new AuthorizationCodeTokenRequest(
|
||||||
|
Config.ClientId,
|
||||||
|
Config.ClientSecret,
|
||||||
|
code,
|
||||||
|
new Uri(Config.SpotifyCallback)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
user.SpotifyIsLinked = true;
|
||||||
|
|
||||||
|
user.SpotifyAccessToken = response.AccessToken;
|
||||||
|
user.SpotifyRefreshToken = response.RefreshToken;
|
||||||
|
user.SpotifyLastRefresh = response.CreatedAt;
|
||||||
|
user.SpotifyTokenExpiry = response.ExpiresIn;
|
||||||
|
|
||||||
|
await UserManager.UpdateAsync(user);
|
||||||
|
|
||||||
|
TempData["StatusMessage"] = "Spotify Linked";
|
||||||
|
return Redirect(ManageSpotifyPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
Selector.Web/Options.cs
Normal file
39
Selector.Web/Options.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace Selector.Web
|
||||||
|
{
|
||||||
|
public static class OptionsHelper {
|
||||||
|
public static void ConfigureOptions(RootOptions options, IConfiguration config)
|
||||||
|
{
|
||||||
|
config.GetSection(RootOptions.Key).Bind(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RootOptions ConfigureOptions(IConfiguration config)
|
||||||
|
{
|
||||||
|
var options = config.GetSection(RootOptions.Key).Get<RootOptions>();
|
||||||
|
ConfigureOptions(options, config);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatKeys(string[] args) => string.Join(":", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RootOptions
|
||||||
|
{
|
||||||
|
public const string Key = "Selector";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Spotify client ID
|
||||||
|
/// </summary>
|
||||||
|
public string ClientId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Spotify app secret
|
||||||
|
/// </summary>
|
||||||
|
public string ClientSecret { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Spotify callback for authentication
|
||||||
|
/// </summary>
|
||||||
|
public string SpotifyCallback { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -30,6 +30,11 @@ namespace Selector.Web
|
|||||||
// This method gets called by the runtime. Use this method to add services to the container.
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
services.Configure<RootOptions>(options =>
|
||||||
|
{
|
||||||
|
OptionsHelper.ConfigureOptions(options, Configuration);
|
||||||
|
});
|
||||||
|
|
||||||
services.AddRazorPages().AddRazorRuntimeCompilation();
|
services.AddRazorPages().AddRazorRuntimeCompilation();
|
||||||
services.AddControllers();
|
services.AddControllers();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user