-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauth.js
More file actions
180 lines (174 loc) · 6.2 KB
/
Copy pathauth.js
File metadata and controls
180 lines (174 loc) · 6.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Used in the 'back end'
import fs from 'fs';
import axios from 'axios';
import crypto from 'crypto';
import http from 'http';
// TODO:
// Handle the case where the user doesn't accept the auth
export const auth = {
client_id: 'f98c6b2ebc6a4d9c969102306d34965b',
redirect_uri: 'http://localhost:3000',
code_verifier: '',
code_challenge: '',
auth_code: '',
access_token: '',
refresh_token: '',
time_token_granted: '',
generateRandomString: async function (length) {
const available_characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const available_characters_length = available_characters.length;
let random_string = '';
let random_int = 0;
for (let i = 0; i < length; i++) {
random_int = Math.floor(Math.random() * available_characters_length);
random_string = random_string + available_characters[random_int];
}
return random_string;
},
sha256: async function (input) { // This function is from the Spotify tutorials https://developer.spotify.com/documentation/web-api/tutorials/code-pkce-flow
const encoder = new TextEncoder();
const data = encoder.encode(input);
return crypto.subtle.digest('SHA-256', data);
},
base64encode: function (input) { // This function is from the Spotify tutorials https://developer.spotify.com/documentation/web-api/tutorials/code-pkce-flow
return btoa(String.fromCharCode(...new Uint8Array(input))).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
},
generateCodeChallenge: async function (input) {
const hashed = await this.sha256(input);
const code_challenge = this.base64encode(hashed);
return code_challenge;
},
requestUserAuthorization: async function () {
const scope = '\
ugc-image-upload \
user-read-playback-state user-modify-playback-state user-read-currently-playing \
app-remote-control streaming \
playlist-read-private playlist-read-collaborative playlist-modify-private playlist-modify-public \
user-follow-modify user-follow-read \
user-read-playback-position user-top-read user-read-recently-played \
user-library-modify user-library-read \
user-read-email user-read-private';
const auth_url = new URL('https://accounts.spotify.com/authorize');
const params = {
client_id: this.client_id,
response_type: 'code',
redirect_uri: this.redirect_uri,
scope: scope,
code_challenge_method: 'S256',
code_challenge: this.code_challenge
}
auth_url.search = new URLSearchParams(params).toString();
console.log('Please follow this link to authorize access: ' + auth_url.toString() + '\n');
let auth_code = new Promise((resolve) => {
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('This page can now be closed, thank you');
const code_recieved = req.url.slice(7);
resolve(code_recieved); // Just need to resolve the value, not do anything with it
server.close();
});
server.listen(port, (error) => {
if (error) {
console.log('Something went wrong', error);
}
});
});
return auth_code;
},
requestAccessToken: async function () {
const { data } = await axios({
method: 'post',
url: 'https://accounts.spotify.com/api/token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: new URLSearchParams({
grant_type: 'authorization_code',
code: this.auth_code,
redirect_uri: this.redirect_uri,
client_id: this.client_id,
code_verifier: this.code_verifier
})
});
return data;
},
setCurrentToken: function (data) {
this.access_token = data.access_token;
this.refresh_token = data.refresh_token;
this.time_token_granted = new Date();
this.writeTokenToFile(data);
return this.time_token_granted;
},
authenticateUser: async function () {
this.code_verifier = await this.generateRandomString(128);
this.code_challenge = await this.generateCodeChallenge(this.code_verifier);
this.auth_code = await this.requestUserAuthorization();
const access_token = await this.requestAccessToken();
return this.setCurrentToken(access_token);
},
requestTokenRefresh: async function () {
const { data } = await axios({
method: 'post',
url: 'https://accounts.spotify.com/api/token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: this.refresh_token,
client_id: this.client_id
})
});
return data;
},
refreshCurrentToken: async function () {
const refreshed_token = await this.requestTokenRefresh();
return this.setCurrentToken(refreshed_token);
},
readSavedToken: async function () {
let token_data = new Promise((resolve) => {
fs.readFile('./access_token.json', 'utf8', (err, data) => {
if (err) {
resolve('err');
} else {
resolve(JSON.parse(data));
}
});
});
return token_data;
},
loadSavedToken: function (data) {
this.access_token = data.access_token;
this.refresh_token = data.refresh_token;
this.time_token_granted = data.time_token_granted;
return this.time_token_granted;
},
writeTokenToFile: async function (data) {
data.time_token_granted = this.time_token_granted.getTime();
return await fs.writeFile('./access_token.json', JSON.stringify(data), (err) => {
if (err) throw err;
});
},
// TODO:
// Handle errors where file exists but bad token
getAuthentication: async function () {
const saved_token = await this.readSavedToken();
if (saved_token !== 'err') { // No token exists yet
return this.loadSavedToken(saved_token);
} else {
return await this.authenticateUser();
}
},
getCurrentToken: async function () {
const current_time = new Date();
// Refresh token if it has been more than 55 minutes
if ((current_time - this.time_token_granted) > (55 * 60 * 1000)) {
await this.refreshCurrentToken();
return this.access_token;
} else {
return this.access_token;
}
}
}