Spotify Auth working, strongly typed web config
This commit is contained in:
parent
d1563c3365
commit
222c738854
@ -30,7 +30,6 @@ namespace Selector.CLI
|
||||
// CONFIG
|
||||
services.Configure<RootOptions>(options =>
|
||||
{
|
||||
|
||||
OptionsHelper.ConfigureOptions(options, 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; }
|
||||
}
|
||||
|
||||
private async Task LoadAsync(ApplicationUser user)
|
||||
private Task LoadAsync(ApplicationUser user)
|
||||
{
|
||||
Input = new InputModel
|
||||
{
|
||||
Username = user.LastFmUsername,
|
||||
};
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> OnGetAsync()
|
||||
@ -74,8 +76,8 @@ namespace Selector.Web.Areas.Identity.Pages.Account.Manage
|
||||
|
||||
if (Input.Username != user.LastFmUsername)
|
||||
{
|
||||
user.LastFmUsername = Input.Username;
|
||||
_userManager.UpdateAsync(user);
|
||||
user.LastFmUsername = Input.Username.Trim();
|
||||
await _userManager.UpdateAsync(user);
|
||||
|
||||
StatusMessage = "Username changed";
|
||||
return RedirectToPage();
|
||||
|
@ -12,17 +12,27 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
|
||||
using Selector.Model;
|
||||
using SpotifyAPI.Web;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Selector.Web.Areas.Identity.Pages.Account.Manage
|
||||
{
|
||||
public partial class SpotifyModel : PageModel
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly ILogger<SpotifyModel> Logger;
|
||||
private readonly RootOptions Config;
|
||||
|
||||
public SpotifyModel(
|
||||
UserManager<ApplicationUser> userManager)
|
||||
UserManager<ApplicationUser> userManager,
|
||||
ILogger<SpotifyModel> logger,
|
||||
IOptions<RootOptions> config
|
||||
)
|
||||
{
|
||||
_userManager = userManager;
|
||||
Logger = logger;
|
||||
Config = config.Value;
|
||||
}
|
||||
|
||||
[BindProperty]
|
||||
@ -46,12 +56,58 @@ namespace Selector.Web.Areas.Identity.Pages.Account.Manage
|
||||
|
||||
public async Task<IActionResult> OnPostLinkAsync()
|
||||
{
|
||||
StatusMessage = "Spotify Linked";
|
||||
return RedirectToPage();
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
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()
|
||||
{
|
||||
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";
|
||||
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.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.Configure<RootOptions>(options =>
|
||||
{
|
||||
OptionsHelper.ConfigureOptions(options, Configuration);
|
||||
});
|
||||
|
||||
services.AddRazorPages().AddRazorRuntimeCompilation();
|
||||
services.AddControllers();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user