Skip to content

Commit 9a6bd1b

Browse files
committed
Restructured Examples and added new one
* Added new ASP.NET Core 3 example This should show how this API can be implemented on the backend. It's not complete yet, features like token reloading is still missing. If you want to run this, use `dotnet user-secrets init` to generate your secrets file and update it with `spotify_client_id` and `spotify_secret_id` * Fixed CLI Example The last page was never processed.
1 parent 7ff69ac commit 9a6bd1b

58 files changed

Lines changed: 39865 additions & 42 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎SpotifyAPI.Web.Example/SpotifyAPI.Web.Example.csproj‎

Lines changed: 0 additions & 14 deletions
This file was deleted.

‎SpotifyAPI.Web.Example/SpotifyAPI.Web.Example.csproj.DotSettings‎

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Linq;
2+
using System.Security.Claims;
3+
4+
namespace SpotifyAPI.Web.Examples.ASP
5+
{
6+
public static class ClaimsExtensions
7+
{
8+
public static string GetSpecificClaim(this ClaimsIdentity claimsIdentity, string claimType)
9+
{
10+
Claim claim = claimsIdentity.Claims.FirstOrDefault(x => x.Type == claimType);
11+
12+
return claim != null ? claim.Value : string.Empty;
13+
}
14+
}
15+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Globalization;
5+
using System.Security.Claims;
6+
using System.Threading.Tasks;
7+
using Microsoft.AspNetCore.Authentication;
8+
using Microsoft.AspNetCore.Mvc;
9+
using SpotifyAPI.Web.Auth;
10+
using SpotifyAPI.Web.Enums;
11+
using SpotifyAPI.Web.Examples.ASP.Models;
12+
using SpotifyAPI.Web.Models;
13+
using Microsoft.Extensions.Configuration;
14+
15+
namespace SpotifyAPI.Web.Examples.ASP.Controllers
16+
{
17+
public class Auth : Controller
18+
{
19+
public IConfiguration Configuration { get; set; }
20+
21+
public Auth(IConfiguration config)
22+
{
23+
Configuration = config;
24+
}
25+
26+
private static Dictionary<string, AuthorizationCodeAuth> LoginRequests { get; } = new Dictionary<string, AuthorizationCodeAuth>();
27+
28+
// GET
29+
public IActionResult Login()
30+
{
31+
string guid = Guid.NewGuid().ToString();
32+
var auth = new AuthorizationCodeAuth( // TODO: Extract to own method
33+
Configuration["spotify_client_id"], Configuration["spotify_client_secret"],
34+
"http://localhost:5000/auth/callback", "", Scope.PlaylistReadPrivate | Scope.PlaylistReadPrivate, guid);
35+
36+
LoginRequests.Add(guid, auth); // TODO: Clean up after a timeout, else: DOS possible
37+
38+
return Redirect(auth.GetUri());
39+
}
40+
41+
[HttpPost]
42+
public async Task<IActionResult> Logout()
43+
{
44+
await HttpContext.SignOutAsync();
45+
46+
return RedirectToAction("Index", "Home");
47+
}
48+
49+
public async Task<IActionResult> Callback([FromQuery(Name = "code")] string code, [FromQuery(Name = "state")] string state,
50+
[FromQuery(Name = "error")] string error)
51+
{
52+
if (error != null)
53+
return View("Error", new ErrorViewModel { Message = error, RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
54+
55+
if(!LoginRequests.ContainsKey(state))
56+
return View("Error", new ErrorViewModel { Message = "Unknown login request", RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
57+
58+
AuthorizationCodeAuth auth = LoginRequests[state];
59+
Token token = await auth.ExchangeCode(code);
60+
61+
if (token.HasError())
62+
{
63+
return View("Error",
64+
new ErrorViewModel
65+
{
66+
Message = $"Unable to exchange Code: ${token.Error}",
67+
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier
68+
});
69+
}
70+
71+
var api = new SpotifyWebAPI
72+
{
73+
AccessToken = token.AccessToken,
74+
TokenType = token.TokenType
75+
};
76+
PrivateProfile profile = await api.GetPrivateProfileAsync();
77+
78+
var claims = new List<Claim>
79+
{
80+
// TODO: Extract claim types to either Enum or class
81+
new Claim("user_id", profile.Id),
82+
new Claim("display_name", profile.DisplayName ?? profile.Id),
83+
new Claim("access_token", token.AccessToken),
84+
new Claim("token_type", token.TokenType),
85+
new Claim("refresh_token", token.RefreshToken),
86+
new Claim("expires_in", token.ExpiresIn.ToString(CultureInfo.InvariantCulture))
87+
};
88+
await HttpContext.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity(claims, "Cookies", "user_id", "")));
89+
90+
return Redirect("/");
91+
}
92+
}
93+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Linq;
5+
using System.Security.Claims;
6+
using System.Threading.Tasks;
7+
using Microsoft.AspNetCore.Authentication;
8+
using Microsoft.AspNetCore.Authorization;
9+
using Microsoft.AspNetCore.Mvc;
10+
using SpotifyAPI.Web.Auth;
11+
using SpotifyAPI.Web.Enums;
12+
using SpotifyAPI.Web.Examples.ASP.Models;
13+
14+
namespace SpotifyAPI.Web.Examples.ASP.Controllers
15+
{
16+
[Authorize]
17+
public class HomeController : Controller
18+
{
19+
public async Task<ViewResult> Index()
20+
{
21+
var claimsIdent = User.Identity as ClaimsIdentity;
22+
23+
SpotifyWebAPI api = GetSpotifyApiFromUser(claimsIdent);
24+
if (api == null)
25+
return View(new HomeModel());
26+
27+
string userId = claimsIdent.GetSpecificClaim("user_id");
28+
var playlists = await api.GetUserPlaylistsAsync(userId);
29+
return View(new HomeModel
30+
{
31+
Playlists = playlists
32+
});
33+
}
34+
35+
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
36+
public IActionResult Error()
37+
{
38+
return View(new ErrorViewModel {RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier});
39+
}
40+
41+
private static SpotifyWebAPI GetSpotifyApiFromUser(ClaimsIdentity claimsIdent)
42+
{
43+
// TODO: Add expires_in logic
44+
45+
string accessToken = claimsIdent.GetSpecificClaim("access_token");
46+
string tokenType = claimsIdent.GetSpecificClaim("token_type");
47+
return new SpotifyWebAPI
48+
{
49+
AccessToken = accessToken,
50+
TokenType = tokenType
51+
};
52+
}
53+
}
54+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
3+
namespace SpotifyAPI.Web.Examples.ASP.Models
4+
{
5+
public class ErrorViewModel
6+
{
7+
public string RequestId { get; set; }
8+
9+
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
10+
11+
public string Message { get; set; }
12+
}
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using SpotifyAPI.Web.Models;
2+
3+
namespace SpotifyAPI.Web.Examples.ASP.Models
4+
{
5+
public class HomeModel
6+
{
7+
public Paging<SimplePlaylist> Playlists;
8+
}
9+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Threading.Tasks;
6+
using Microsoft.AspNetCore;
7+
using Microsoft.AspNetCore.Hosting;
8+
using Microsoft.Extensions.Configuration;
9+
using Microsoft.Extensions.Logging;
10+
11+
namespace SpotifyAPI.Web.Examples.ASP
12+
{
13+
public class Program
14+
{
15+
public static void Main(string[] args)
16+
{
17+
CreateWebHostBuilder(args).Build().Run();
18+
}
19+
20+
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
21+
WebHost.CreateDefaultBuilder(args)
22+
.UseStartup<Startup>();
23+
}
24+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"iisSettings": {
3+
"windowsAuthentication": false,
4+
"anonymousAuthentication": true,
5+
"iisExpress": {
6+
"applicationUrl": "http://localhost:7731",
7+
"sslPort": 44310
8+
}
9+
},
10+
"profiles": {
11+
"IIS Express": {
12+
"commandName": "IISExpress",
13+
"launchBrowser": true,
14+
"environmentVariables": {
15+
"ASPNETCORE_ENVIRONMENT": "Development"
16+
}
17+
},
18+
"SpotifyAPI.Web.Examples.ASP": {
19+
"commandName": "Project",
20+
"launchBrowser": true,
21+
"applicationUrl": "https://localhost:5001;http://localhost:5000",
22+
"environmentVariables": {
23+
"ASPNETCORE_ENVIRONMENT": "Development"
24+
}
25+
}
26+
}
27+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project Sdk="Microsoft.NET.Sdk.Web">
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp3.0</TargetFramework>
5+
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
6+
<UserSecretsId>52b27bb1-dc8f-4151-8ad6-b6efdb8edbb7</UserSecretsId>
7+
</PropertyGroup>
8+
<ItemGroup>
9+
<ProjectReference Include="..\SpotifyAPI.Web.Auth\SpotifyAPI.Web.Auth.csproj" />
10+
</ItemGroup>
11+
</Project>

0 commit comments

Comments
 (0)