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+ }
0 commit comments