mirror of
https://github.com/Sarsoo/Spotify.NET.git
synced 2024-12-24 15:06:26 +00:00
Refactor ASP.NET Example to use AspNet.Security.OAuth.Spotify
This commit is contained in:
parent
9a6bd1b456
commit
58de9d9a4e
@ -1,15 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Security.Claims;
|
|
||||||
|
|
||||||
namespace SpotifyAPI.Web.Examples.ASP
|
|
||||||
{
|
|
||||||
public static class ClaimsExtensions
|
|
||||||
{
|
|
||||||
public static string GetSpecificClaim(this ClaimsIdentity claimsIdentity, string claimType)
|
|
||||||
{
|
|
||||||
Claim claim = claimsIdentity.Claims.FirstOrDefault(x => x.Type == claimType);
|
|
||||||
|
|
||||||
return claim != null ? claim.Value : string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using SpotifyAPI.Web.Auth;
|
|
||||||
using SpotifyAPI.Web.Enums;
|
|
||||||
using SpotifyAPI.Web.Examples.ASP.Models;
|
|
||||||
using SpotifyAPI.Web.Models;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
|
|
||||||
namespace SpotifyAPI.Web.Examples.ASP.Controllers
|
|
||||||
{
|
|
||||||
public class Auth : Controller
|
|
||||||
{
|
|
||||||
public IConfiguration Configuration { get; set; }
|
|
||||||
|
|
||||||
public Auth(IConfiguration config)
|
|
||||||
{
|
|
||||||
Configuration = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<string, AuthorizationCodeAuth> LoginRequests { get; } = new Dictionary<string, AuthorizationCodeAuth>();
|
|
||||||
|
|
||||||
// GET
|
|
||||||
public IActionResult Login()
|
|
||||||
{
|
|
||||||
string guid = Guid.NewGuid().ToString();
|
|
||||||
var auth = new AuthorizationCodeAuth( // TODO: Extract to own method
|
|
||||||
Configuration["spotify_client_id"], Configuration["spotify_client_secret"],
|
|
||||||
"http://localhost:5000/auth/callback", "", Scope.PlaylistReadPrivate | Scope.PlaylistReadPrivate, guid);
|
|
||||||
|
|
||||||
LoginRequests.Add(guid, auth); // TODO: Clean up after a timeout, else: DOS possible
|
|
||||||
|
|
||||||
return Redirect(auth.GetUri());
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<IActionResult> Logout()
|
|
||||||
{
|
|
||||||
await HttpContext.SignOutAsync();
|
|
||||||
|
|
||||||
return RedirectToAction("Index", "Home");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IActionResult> Callback([FromQuery(Name = "code")] string code, [FromQuery(Name = "state")] string state,
|
|
||||||
[FromQuery(Name = "error")] string error)
|
|
||||||
{
|
|
||||||
if (error != null)
|
|
||||||
return View("Error", new ErrorViewModel { Message = error, RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
|
|
||||||
|
|
||||||
if(!LoginRequests.ContainsKey(state))
|
|
||||||
return View("Error", new ErrorViewModel { Message = "Unknown login request", RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
|
|
||||||
|
|
||||||
AuthorizationCodeAuth auth = LoginRequests[state];
|
|
||||||
Token token = await auth.ExchangeCode(code);
|
|
||||||
|
|
||||||
if (token.HasError())
|
|
||||||
{
|
|
||||||
return View("Error",
|
|
||||||
new ErrorViewModel
|
|
||||||
{
|
|
||||||
Message = $"Unable to exchange Code: ${token.Error}",
|
|
||||||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var api = new SpotifyWebAPI
|
|
||||||
{
|
|
||||||
AccessToken = token.AccessToken,
|
|
||||||
TokenType = token.TokenType
|
|
||||||
};
|
|
||||||
PrivateProfile profile = await api.GetPrivateProfileAsync();
|
|
||||||
|
|
||||||
var claims = new List<Claim>
|
|
||||||
{
|
|
||||||
// TODO: Extract claim types to either Enum or class
|
|
||||||
new Claim("user_id", profile.Id),
|
|
||||||
new Claim("display_name", profile.DisplayName ?? profile.Id),
|
|
||||||
new Claim("access_token", token.AccessToken),
|
|
||||||
new Claim("token_type", token.TokenType),
|
|
||||||
new Claim("refresh_token", token.RefreshToken),
|
|
||||||
new Claim("expires_in", token.ExpiresIn.ToString(CultureInfo.InvariantCulture))
|
|
||||||
};
|
|
||||||
await HttpContext.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity(claims, "Cookies", "user_id", "")));
|
|
||||||
|
|
||||||
return Redirect("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +1,48 @@
|
|||||||
using System;
|
using System.Diagnostics;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using SpotifyAPI.Web.Auth;
|
using Microsoft.Extensions.Logging;
|
||||||
using SpotifyAPI.Web.Enums;
|
|
||||||
using SpotifyAPI.Web.Examples.ASP.Models;
|
using SpotifyAPI.Web.Examples.ASP.Models;
|
||||||
|
|
||||||
namespace SpotifyAPI.Web.Examples.ASP.Controllers
|
namespace SpotifyAPI.Web.Examples.ASP.Controllers
|
||||||
{
|
{
|
||||||
[Authorize]
|
|
||||||
public class HomeController : Controller
|
public class HomeController : Controller
|
||||||
{
|
{
|
||||||
public async Task<ViewResult> Index()
|
private readonly ILogger<HomeController> _logger;
|
||||||
{
|
|
||||||
var claimsIdent = User.Identity as ClaimsIdentity;
|
|
||||||
|
|
||||||
SpotifyWebAPI api = GetSpotifyApiFromUser(claimsIdent);
|
public HomeController(ILogger<HomeController> logger)
|
||||||
if (api == null)
|
{
|
||||||
return View(new HomeModel());
|
_logger = logger;
|
||||||
|
}
|
||||||
string userId = claimsIdent.GetSpecificClaim("user_id");
|
|
||||||
var playlists = await api.GetUserPlaylistsAsync(userId);
|
public async Task<IActionResult> Index()
|
||||||
return View(new HomeModel
|
{
|
||||||
|
if(!User.Identity.IsAuthenticated)
|
||||||
|
return Challenge(new AuthenticationProperties { RedirectUri = "/" }, "Spotify");
|
||||||
|
|
||||||
|
var accessToken = await HttpContext.GetTokenAsync("Spotify", "access_token");
|
||||||
|
SpotifyWebAPI api = new SpotifyWebAPI
|
||||||
{
|
{
|
||||||
Playlists = playlists
|
AccessToken = accessToken,
|
||||||
});
|
TokenType = "Bearer"
|
||||||
|
};
|
||||||
|
|
||||||
|
var savedTracks = await api.GetSavedTracksAsync(50);
|
||||||
|
|
||||||
|
return View(new IndexModel { SavedTracks = savedTracks });
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult Privacy()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||||
public IActionResult Error()
|
public IActionResult Error()
|
||||||
{
|
{
|
||||||
return View(new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
|
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||||
}
|
|
||||||
|
|
||||||
private static SpotifyWebAPI GetSpotifyApiFromUser(ClaimsIdentity claimsIdent)
|
|
||||||
{
|
|
||||||
// TODO: Add expires_in logic
|
|
||||||
|
|
||||||
string accessToken = claimsIdent.GetSpecificClaim("access_token");
|
|
||||||
string tokenType = claimsIdent.GetSpecificClaim("token_type");
|
|
||||||
return new SpotifyWebAPI
|
|
||||||
{
|
|
||||||
AccessToken = accessToken,
|
|
||||||
TokenType = tokenType
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,5 @@ namespace SpotifyAPI.Web.Examples.ASP.Models
|
|||||||
public string RequestId { get; set; }
|
public string RequestId { get; set; }
|
||||||
|
|
||||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||||
|
|
||||||
public string Message { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
using SpotifyAPI.Web.Models;
|
|
||||||
|
|
||||||
namespace SpotifyAPI.Web.Examples.ASP.Models
|
|
||||||
{
|
|
||||||
public class HomeModel
|
|
||||||
{
|
|
||||||
public Paging<SimplePlaylist> Playlists;
|
|
||||||
}
|
|
||||||
}
|
|
10
SpotifyAPI.Web.Examples.ASP/Models/IndexModel.cs
Normal file
10
SpotifyAPI.Web.Examples.ASP/Models/IndexModel.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using SpotifyAPI.Web.Models;
|
||||||
|
|
||||||
|
namespace SpotifyAPI.Web.Examples.ASP.Models
|
||||||
|
{
|
||||||
|
public class IndexModel
|
||||||
|
{
|
||||||
|
public Paging<SavedTrack> SavedTracks;
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace SpotifyAPI.Web.Examples.ASP
|
namespace SpotifyAPI.Web.Examples.ASP
|
||||||
@ -14,11 +13,14 @@ namespace SpotifyAPI.Web.Examples.ASP
|
|||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
CreateWebHostBuilder(args).Build().Run();
|
CreateHostBuilder(args).Build().Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
WebHost.CreateDefaultBuilder(args)
|
Host.CreateDefaultBuilder(args)
|
||||||
.UseStartup<Startup>();
|
.ConfigureWebHostDefaults(webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder.UseStartup<Startup>();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"iisSettings": {
|
"iisSettings": {
|
||||||
"windowsAuthentication": false,
|
"windowsAuthentication": false,
|
||||||
"anonymousAuthentication": true,
|
"anonymousAuthentication": true,
|
||||||
"iisExpress": {
|
"iisExpress": {
|
||||||
"applicationUrl": "http://localhost:7731",
|
"applicationUrl": "http://localhost:55802",
|
||||||
"sslPort": 44310
|
"sslPort": 44320
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"profiles": {
|
"profiles": {
|
||||||
@ -18,10 +18,10 @@
|
|||||||
"SpotifyAPI.Web.Examples.ASP": {
|
"SpotifyAPI.Web.Examples.ASP": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
"applicationUrl": "http://localhost:5000",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
<UserSecretsId>201e6943-a5b3-473c-b1da-a579b36ca283</UserSecretsId>
|
||||||
<UserSecretsId>52b27bb1-dc8f-4151-8ad6-b6efdb8edbb7</UserSecretsId>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\SpotifyAPI.Web.Auth\SpotifyAPI.Web.Auth.csproj" />
|
<ProjectReference Include="..\SpotifyAPI.Web\SpotifyAPI.Web.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AspNet.Security.OAuth.Spotify" Version="3.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -1,23 +1,15 @@
|
|||||||
using System;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.HttpsPolicy;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using EnvironmentName = Microsoft.AspNetCore.Hosting.EnvironmentName;
|
using SpotifyAPI.Web.Enums;
|
||||||
|
|
||||||
namespace SpotifyAPI.Web.Examples.ASP
|
namespace SpotifyAPI.Web.Examples.ASP
|
||||||
{
|
{
|
||||||
public class Startup
|
public class Startup
|
||||||
{
|
{
|
||||||
private const string CookieScheme = "YourSchemeName";
|
|
||||||
|
|
||||||
public Startup(IConfiguration configuration)
|
public Startup(IConfiguration configuration)
|
||||||
{
|
{
|
||||||
Configuration = configuration;
|
Configuration = configuration;
|
||||||
@ -28,25 +20,24 @@ namespace SpotifyAPI.Web.Examples.ASP
|
|||||||
// 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<CookiePolicyOptions>(options =>
|
services.AddControllersWithViews();
|
||||||
{
|
|
||||||
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
|
services.AddAuthentication(o => o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme)
|
||||||
options.CheckConsentNeeded = context => true;
|
.AddCookie(options =>
|
||||||
options.MinimumSameSitePolicy = SameSiteMode.None;
|
|
||||||
});
|
|
||||||
|
|
||||||
services
|
|
||||||
.AddAuthentication(CookieScheme) // Sets the default scheme to cookies
|
|
||||||
.AddCookie(CookieScheme, options =>
|
|
||||||
{
|
{
|
||||||
options.LoginPath = "/auth/login";
|
options.LoginPath = "/signin";
|
||||||
|
options.LogoutPath = "/signout";
|
||||||
|
})
|
||||||
|
.AddSpotify(options =>
|
||||||
|
{
|
||||||
|
var scopes = Scope.UserLibraryRead;
|
||||||
|
options.Scope.Add(scopes.GetStringAttribute(","));
|
||||||
|
|
||||||
|
options.SaveTokens = true;
|
||||||
|
options.ClientId = Configuration["client_id"];
|
||||||
|
options.ClientSecret = Configuration["client_secret"];
|
||||||
|
options.CallbackPath = "/callback";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
services
|
|
||||||
.AddMvc(options => options.EnableEndpointRouting = false)
|
|
||||||
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
@ -58,24 +49,24 @@ namespace SpotifyAPI.Web.Examples.ASP
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
app.UseHttpsRedirection();
|
|
||||||
app.UseExceptionHandler("/Home/Error");
|
app.UseExceptionHandler("/Home/Error");
|
||||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||||
app.UseHsts();
|
app.UseHsts();
|
||||||
app.UseHttpsRedirection();
|
|
||||||
}
|
}
|
||||||
|
// app.UseHttpsRedirection();
|
||||||
|
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
app.UseCookiePolicy();
|
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseMvc(routes =>
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.UseEndpoints(endpoints =>
|
||||||
{
|
{
|
||||||
routes.MapRoute(
|
endpoints.MapControllerRoute(
|
||||||
name: "default",
|
name: "default",
|
||||||
template: "{controller=Home}/{action=Index}/{id?}");
|
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,15 @@
|
|||||||
@model HomeModel
|
@model IndexModel
|
||||||
@using System.Security.Claims
|
@{
|
||||||
@using Microsoft.AspNetCore.Authentication
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Home Page";
|
ViewData["Title"] = "Home Page";
|
||||||
}
|
}
|
||||||
|
|
||||||
<h2>Some Playlists (first 20)</h2>
|
|
||||||
|
|
||||||
@if (Model.Playlists != null && !Model.Playlists.HasError())
|
|
||||||
{
|
|
||||||
<dl>
|
|
||||||
@foreach (var playlist in Model.Playlists.Items)
|
|
||||||
{
|
|
||||||
<dt>@playlist.Name</dt>
|
|
||||||
<dd>@(playlist.Owner.DisplayName ?? playlist.Owner.Id)</dd>
|
|
||||||
}
|
|
||||||
</dl>
|
|
||||||
}
|
|
||||||
|
|
||||||
<h2>HttpContext.User.Claims</h2>
|
<div class="text-center">
|
||||||
|
You have @Model.SavedTracks.Total saved tracks in your library! Here are 50 of them:
|
||||||
|
|
||||||
<dl>
|
<ul>
|
||||||
@foreach (Claim claim in User.Claims)
|
@foreach (var item in Model.SavedTracks.Items)
|
||||||
{
|
{
|
||||||
<dt>@claim.Type</dt>
|
<li>@item.Track.Name</li>
|
||||||
<dd>@claim.Value</dd>
|
}
|
||||||
}
|
</ul>
|
||||||
</dl>
|
</div>
|
||||||
|
|
||||||
<h2>AuthenticationProperties</h2>
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
@foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)
|
|
||||||
{
|
|
||||||
<dt>@prop.Key</dt>
|
|
||||||
<dd>@prop.Value</dd>
|
|
||||||
}
|
|
||||||
</dl>
|
|
||||||
|
6
SpotifyAPI.Web.Examples.ASP/Views/Home/Privacy.cshtml
Normal file
6
SpotifyAPI.Web.Examples.ASP/Views/Home/Privacy.cshtml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Privacy Policy";
|
||||||
|
}
|
||||||
|
<h1>@ViewData["Title"]</h1>
|
||||||
|
|
||||||
|
<p>Use this page to detail your site's privacy policy.</p>
|
@ -9,7 +9,6 @@
|
|||||||
@if (Model.ShowRequestId)
|
@if (Model.ShowRequestId)
|
||||||
{
|
{
|
||||||
<p>
|
<p>
|
||||||
<strong>Message:</strong> <code>@Model.Message</code>
|
|
||||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,10 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>@ViewData["Title"] - SpotifyAPI.Web.Examples.ASP</title>
|
<title>@ViewData["Title"] - SpotifyAPI.Web.Examples.ASP</title>
|
||||||
|
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||||
<environment include="Development">
|
|
||||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
|
|
||||||
</environment>
|
|
||||||
<environment exclude="Development">
|
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
|
||||||
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
|
|
||||||
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"/>
|
|
||||||
</environment>
|
|
||||||
<link rel="stylesheet" href="~/css/site.css" />
|
<link rel="stylesheet" href="~/css/site.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -31,14 +21,9 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
|
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
|
||||||
</li>
|
</li>
|
||||||
@if (User.Identity.IsAuthenticated)
|
<li class="nav-item">
|
||||||
{
|
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||||
<li>
|
</li>
|
||||||
<form class="form-inline" asp-controller="Auth" asp-action="Logout" method="post" >
|
|
||||||
<button type="submit" class="nav-link btn btn-link">Logout</button>
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -51,28 +36,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="border-top footer text-muted">
|
<footer class="border-top footer text-muted">
|
||||||
|
<div class="container">
|
||||||
|
© 2019 - SpotifyAPI.Web.Examples.ASP - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||||
<environment include="Development">
|
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="~/lib/jquery/dist/jquery.js"></script>
|
|
||||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
|
|
||||||
</environment>
|
|
||||||
<environment exclude="Development">
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
|
|
||||||
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
|
|
||||||
asp-fallback-test="window.jQuery"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
|
|
||||||
</script>
|
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"
|
|
||||||
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"
|
|
||||||
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
integrity="sha384-xrRywqdh3PHs8keKZN+8zzc5TX0GRTLCcmivcbNJWm2rs5C8PRhcEn3czEjhAO9o">
|
|
||||||
</script>
|
|
||||||
</environment>
|
|
||||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||||
|
|
||||||
@RenderSection("Scripts", required: false)
|
@RenderSection("Scripts", required: false)
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,18 +1,2 @@
|
|||||||
<environment include="Development">
|
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
|
||||||
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
|
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
||||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
|
|
||||||
</environment>
|
|
||||||
<environment exclude="Development">
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0/jquery.validate.min.js"
|
|
||||||
asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js"
|
|
||||||
asp-fallback-test="window.jQuery && window.jQuery.validator"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
integrity="sha256-F6h55Qw6sweK+t7SiOJX+2bpSAa3b/fnlrVCJvmEj1A=">
|
|
||||||
</script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"
|
|
||||||
asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
|
|
||||||
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
integrity="sha256-9GycpJnliUjJDVDqP0UEu/bsm9U+3dnQUH8+3W10vkY=">
|
|
||||||
</script>
|
|
||||||
</environment>
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
{
|
{
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Warning"
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*"
|
||||||
|
@ -7,6 +7,23 @@ a.navbar-brand {
|
|||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Provide sufficient contrast against white background */
|
||||||
|
a {
|
||||||
|
color: #0366d6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #1b6ec2;
|
||||||
|
border-color: #1861ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #1b6ec2;
|
||||||
|
border-color: #1861ac;
|
||||||
|
}
|
||||||
|
|
||||||
/* Sticky footer styles
|
/* Sticky footer styles
|
||||||
-------------------------------------------------- */
|
-------------------------------------------------- */
|
||||||
html {
|
html {
|
||||||
@ -50,7 +67,5 @@ body {
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
/* Set the fixed height of the footer here */
|
|
||||||
height: 60px;
|
|
||||||
line-height: 60px; /* Vertically center the text there */
|
line-height: 60px; /* Vertically center the text there */
|
||||||
}
|
}
|
||||||
|
@ -138,14 +138,14 @@ small {
|
|||||||
|
|
||||||
sub,
|
sub,
|
||||||
sup {
|
sup {
|
||||||
|
position: relative;
|
||||||
font-size: 75%;
|
font-size: 75%;
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
position: relative;
|
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub {
|
sub {
|
||||||
bottom: -0.25em;
|
bottom: -.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
sup {
|
sup {
|
||||||
@ -305,7 +305,6 @@ fieldset {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
padding: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
legend {
|
legend {
|
||||||
|
Loading…
Reference in New Issue
Block a user