@remcostoeten/fync
Version:
A unified TypeScript library for easy access to popular APIs (GitHub, Spotify, GitLab, etc.)
108 lines (107 loc) • 3.62 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SPOTIFY_SCOPES = void 0;
exports.createSpotifyAuth = createSpotifyAuth;
exports.isTokenExpired = isTokenExpired;
exports.shouldRefreshToken = shouldRefreshToken;
const SPOTIFY_SCOPES = exports.SPOTIFY_SCOPES = {
PLAYLIST_READ_PRIVATE: "playlist-read-private",
PLAYLIST_READ_COLLABORATIVE: "playlist-read-collaborative",
PLAYLIST_MODIFY_PUBLIC: "playlist-modify-public",
PLAYLIST_MODIFY_PRIVATE: "playlist-modify-private",
USER_READ_PRIVATE: "user-read-private",
USER_READ_EMAIL: "user-read-email",
USER_READ_PLAYBACK_STATE: "user-read-playback-state",
USER_MODIFY_PLAYBACK_STATE: "user-modify-playback-state",
USER_READ_CURRENTLY_PLAYING: "user-read-currently-playing",
USER_READ_RECENTLY_PLAYED: "user-read-recently-played",
USER_TOP_READ: "user-top-read",
USER_LIBRARY_READ: "user-library-read",
USER_LIBRARY_MODIFY: "user-library-modify",
USER_FOLLOW_READ: "user-follow-read",
USER_FOLLOW_MODIFY: "user-follow-modify",
STREAMING: "streaming"
};
function createSpotifyAuth(config) {
const baseUrl = config.baseUrl || "https://accounts.spotify.com";
const userAgent = config.userAgent || "Fync Spotify Client";
function getAuthorizationUrl(scopes, state) {
const params = new URLSearchParams({
response_type: "code",
client_id: config.clientId,
scope: scopes.join(" "),
redirect_uri: config.redirectUri || "",
...(state && {
state
})
});
return `${baseUrl}/authorize?${params.toString()}`;
}
async function exchangeCodeForToken(code, _state) {
const params = new URLSearchParams({
grant_type: "authorization_code",
code,
redirect_uri: config.redirectUri || "",
client_id: config.clientId,
client_secret: config.clientSecret
});
const response = await fetch(`${baseUrl}/api/token`, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": userAgent
},
body: params.toString()
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Spotify OAuth error: ${error.error_description || error.error}`);
}
const data = await response.json();
return {
...data,
created_at: Math.floor(Date.now() / 1000)
};
}
async function refreshAccessToken(refreshToken) {
const params = new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: config.clientId,
client_secret: config.clientSecret
});
const response = await fetch(`${baseUrl}/api/token`, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": userAgent
},
body: params.toString()
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Spotify token refresh error: ${error.error_description || error.error}`);
}
const data = await response.json();
return {
...data,
created_at: Math.floor(Date.now() / 1000)
};
}
return {
getAuthorizationUrl,
exchangeCodeForToken,
refreshAccessToken
};
}
function isTokenExpired(token, bufferSeconds = 300) {
if (!token.created_at) return true;
const expiresAt = token.created_at + token.expires_in;
const now = Math.floor(Date.now() / 1000);
return now >= expiresAt - bufferSeconds;
}
function shouldRefreshToken(token, bufferSeconds = 300) {
return isTokenExpired(token, bufferSeconds) && !!token.refresh_token;
}