spotify-auth.ts•5.01 kB
import SpotifyWebApi from 'spotify-web-api-node';
import { SpotifyConfig, SpotifyTokens } from '../types/index.js';
import fs from 'fs';
import path from 'path';
export class SpotifyAuth {
private spotifyApi: any;
private tokens: SpotifyTokens | null = null;
private tokensFile: string;
constructor(config: SpotifyConfig) {
this.spotifyApi = new SpotifyWebApi({
clientId: config.clientId,
clientSecret: config.clientSecret,
redirectUri: config.redirectUri,
});
this.tokensFile = path.join(process.cwd(), 'spotify-tokens.json');
this.loadTokens();
}
/**
* Gera URL de autorização para o usuário fazer login
*/
getAuthorizationUrl(): string {
const scopes = [
'user-read-private',
'user-read-email',
'user-read-playback-state',
'user-modify-playback-state',
'user-read-currently-playing',
'playlist-read-private',
'playlist-read-collaborative',
'playlist-modify-public',
'playlist-modify-private',
'user-library-read',
'user-library-modify',
'user-top-read',
'user-read-recently-played'
];
return this.spotifyApi.createAuthorizeURL(scopes, 'state');
}
/**
* Troca o código de autorização por tokens de acesso
*/
async exchangeCodeForTokens(code: string): Promise<SpotifyTokens> {
try {
const data = await this.spotifyApi.authorizationCodeGrant(code);
const { access_token, refresh_token, expires_in } = data.body;
this.tokens = {
accessToken: access_token,
refreshToken: refresh_token,
expiresAt: Date.now() + (expires_in * 1000)
};
this.spotifyApi.setAccessToken(access_token);
this.spotifyApi.setRefreshToken(refresh_token);
this.saveTokens();
return this.tokens;
} catch (error) {
throw new Error(`Erro ao trocar código por tokens: ${error}`);
}
}
/**
* Verifica se o token está expirado e renova se necessário
*/
async ensureValidToken(): Promise<string> {
if (!this.tokens) {
throw new Error('Usuário não autenticado');
}
if (Date.now() >= this.tokens.expiresAt) {
await this.refreshAccessToken();
}
return this.tokens.accessToken;
}
/**
* Renova o token de acesso usando o refresh token
*/
private async refreshAccessToken(): Promise<void> {
if (!this.tokens?.refreshToken) {
throw new Error('Refresh token não disponível');
}
try {
const data = await this.spotifyApi.refreshAccessToken();
const { access_token, expires_in } = data.body;
this.tokens.accessToken = access_token;
this.tokens.expiresAt = Date.now() + (expires_in * 1000);
this.spotifyApi.setAccessToken(access_token);
this.saveTokens();
} catch (error) {
throw new Error(`Erro ao renovar token: ${error}`);
}
}
/**
* Define tokens existentes (útil para persistir sessão)
*/
setTokens(tokens: SpotifyTokens): void {
this.tokens = tokens;
this.spotifyApi.setAccessToken(tokens.accessToken);
this.spotifyApi.setRefreshToken(tokens.refreshToken);
}
/**
* Retorna a instância do SpotifyWebApi configurada
*/
getSpotifyApi(): any {
return this.spotifyApi;
}
/**
* Verifica se o usuário está autenticado
*/
isAuthenticated(): boolean {
return this.tokens !== null;
}
/**
* Limpa os tokens (logout)
*/
logout(): void {
this.tokens = null;
this.spotifyApi.setAccessToken('');
this.spotifyApi.setRefreshToken('');
this.saveTokens();
}
/**
* Salva os tokens em arquivo
*/
private saveTokens(): void {
try {
if (this.tokens) {
fs.writeFileSync(this.tokensFile, JSON.stringify(this.tokens, null, 2));
} else {
if (fs.existsSync(this.tokensFile)) {
fs.unlinkSync(this.tokensFile);
}
}
} catch (error) {
console.error('Erro ao salvar tokens:', error);
}
}
/**
* Carrega os tokens do arquivo
*/
private loadTokens(): void {
try {
if (fs.existsSync(this.tokensFile)) {
const data = fs.readFileSync(this.tokensFile, 'utf8');
const tokens = JSON.parse(data) as SpotifyTokens;
// Verifica se o token não expirou
if (tokens.expiresAt > Date.now()) {
this.tokens = tokens;
this.spotifyApi.setAccessToken(tokens.accessToken);
this.spotifyApi.setRefreshToken(tokens.refreshToken);
} else {
// Token expirado, tenta renovar
this.tokens = tokens;
this.spotifyApi.setRefreshToken(tokens.refreshToken);
this.refreshAccessToken().catch(() => {
// Se não conseguir renovar, limpa os tokens
this.tokens = null;
this.spotifyApi.setAccessToken('');
this.spotifyApi.setRefreshToken('');
});
}
}
} catch (error) {
console.error('Erro ao carregar tokens:', error);
}
}
}