fuzzyworld

hawk / files / public / js / signin.js

signin.js

Blame: the commit that last changed each line · file · history

601035fb 2023-10-12 Pavel Safronov1const clientId = '6c4c04a3751b4e409ddad7d71aad9949';
cc1af884 2023-10-13 Pavel Safronov2const scope = 'user-library-read user-modify-playback-state user-read-playback-state';
3bf5fcbb 2023-10-13 Pavel Safronov3const prematureRefreshSeconds = 60 * 5; // 5 minutes before expiration
4
8edcc887 2023-10-13 Pavel Safronov5function getSpotifyRedirectURL() {
6 // If the current hostname is 'localhost'
7 if (window.location.hostname === 'localhost') {
8 return 'http://localhost:5500/public/index.html';
9 } else if (window.location.hostname === '127.0.0.1') {
10 return 'http://127.0.0.1:5500/public/index.html';
11 } else {
dd354806 2026-10-27 Pavel Safronov12 return 'https://apps.fuzzyworld.net/hawk/index.html';
8edcc887 2023-10-13 Pavel Safronov13 }
14}
601035fb 2023-10-12 Pavel Safronov15
16async function signOut() {
17 localStorage.removeItem('access_token');
3bf5fcbb 2023-10-13 Pavel Safronov18 localStorage.removeItem('refresh_token');
19 localStorage.removeItem('expires_at');
6e3f55a5 2023-10-13 Pavel Safronov20 await deleteDb();
714f908c 2023-10-13 Pavel Safronov21 window.location = getSpotifyRedirectURL();
601035fb 2023-10-12 Pavel Safronov22}
23
24async function signIn() {
25 let codeVerifier = generateRandomString(128);
26 const codeChallenge = await generateCodeChallenge(codeVerifier)
27 let state = generateRandomString(16);
28
29 localStorage.setItem('code_verifier', codeVerifier);
30
8edcc887 2023-10-13 Pavel Safronov31 const redirectUrl = getSpotifyRedirectURL();
601035fb 2023-10-12 Pavel Safronov32 let args = new URLSearchParams({
33 response_type: 'code',
34 client_id: clientId,
35 scope: scope,
8edcc887 2023-10-13 Pavel Safronov36 redirect_uri: redirectUrl,
601035fb 2023-10-12 Pavel Safronov37 state: state,
38 code_challenge_method: 'S256',
39 code_challenge: codeChallenge
40 });
41
42 window.location = 'https://accounts.spotify.com/authorize?' + args;
43}
44
45async function getToken(code) {
46 let codeVerifier = localStorage.getItem('code_verifier');
47
8edcc887 2023-10-13 Pavel Safronov48 const redirectUrl = getSpotifyRedirectURL();
601035fb 2023-10-12 Pavel Safronov49 let body = new URLSearchParams({
50 grant_type: 'authorization_code',
51 code: code,
8edcc887 2023-10-13 Pavel Safronov52 redirect_uri: redirectUrl,
601035fb 2023-10-12 Pavel Safronov53 client_id: clientId,
54 code_verifier: codeVerifier
55 });
56
57 try {
58 const response = await fetch('https://accounts.spotify.com/api/token', {
59 method: 'POST',
60 headers: {
61 'Content-Type': 'application/x-www-form-urlencoded'
62 },
63 body: body
64 });
65 if (!response.ok) {
66 console.error('HTTP status ' + response.status);
67 throw new Error('HTTP status ' + response.status);
68 }
69 const data = await response.json();
3bf5fcbb 2023-10-13 Pavel Safronov70 const expiresInSeconds = data.expires_in;
601035fb 2023-10-12 Pavel Safronov71 localStorage.setItem('access_token', data.access_token);
09b64e4c 2023-10-14 Pavel Safronov72 localStorage.setItem('refresh_token', data.refresh_token);
3bf5fcbb 2023-10-13 Pavel Safronov73 localStorage.setItem('expires_at', Date.now() + expiresInSeconds * 1000);
601035fb 2023-10-12 Pavel Safronov74 } catch (error) {
75 console.error('Error:', error);
76 }
77}
78
79function generateRandomString(length) {
80 let text = '';
81 let possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
82
83 for (let i = 0; i < length; i++) {
84 text += possible.charAt(Math.floor(Math.random() * possible.length));
85 }
86 return text;
87}
88
89async function generateCodeChallenge(codeVerifier) {
90 function base64encode(string) {
91 return btoa(String.fromCharCode.apply(null, new Uint8Array(string)))
92 .replace(/\+/g, '-')
93 .replace(/\//g, '_')
94 .replace(/=+$/, '');
95 }
96
97 const encoder = new TextEncoder();
98 const data = encoder.encode(codeVerifier);
99 const digest = await window.crypto.subtle.digest('SHA-256', data);
100
101 return base64encode(digest);
102}
3bf5fcbb 2023-10-13 Pavel Safronov103
104async function getAccessToken() {
791409a6 2023-10-13 Pavel Safronov105 let accessToken = localStorage.getItem('access_token');
3bf5fcbb 2023-10-13 Pavel Safronov106 if (!accessToken) {
107 throw 'No access token found';
108 }
109
110 const expiresAtText = localStorage.getItem('expires_at');
111 if (!expiresAtText) {
112 throw 'No expiration found';
113 }
114 const expiresAt = parseInt(expiresAtText);
115 const shouldRefresh = Date.now() > expiresAt - prematureRefreshSeconds * 1000;
116 if (shouldRefresh) {
117 console.log('Refreshing access token');
118 accessToken = await refreshToken();
119 }
120
121 return accessToken;
122}
123
124async function refreshToken() {
125 const refreshToken = localStorage.getItem('refresh_token');
126 if (!refreshToken) {
127 throw 'No refresh token found';
128 }
129
130 let body = new URLSearchParams({
131 grant_type: 'refresh_token',
132 refresh_token: refreshToken,
133 client_id: clientId
134 });
135
09b64e4c 2023-10-14 Pavel Safronov136 const response = await fetch('https://accounts.spotify.com/api/token', {
137 method: 'POST',
138 headers: {
139 'Content-Type': 'application/x-www-form-urlencoded'
140 },
141 body: body,
142 });
143 if (!response.ok) {
144 console.error('HTTP status ' + response.status);
145 throw new Error('HTTP status ' + response.status);
3bf5fcbb 2023-10-13 Pavel Safronov146 }
09b64e4c 2023-10-14 Pavel Safronov147 const data = await response.json();
148 const expiresInSeconds = data.expires_in;
149 localStorage.setItem('access_token', data.access_token);
150 localStorage.setItem('refresh_token', data.refresh_token);
151 localStorage.setItem('expires_at', Date.now() + expiresInSeconds * 1000);
152 return data.access_token;
3bf5fcbb 2023-10-13 Pavel Safronov153}