O refresh token permite renovar access tokens automaticamente sem precisar fazer login novamente, mantendo a sessão ativa de forma contínua na API da Nuvende.
O refresh token na API da Nuvende é um mecanismo que permite renovar tokens de acesso expirados sem a necessidade de um novo login. Quando você faz refresh:
URL: POST /api/v2/auth/refresh
Ambiente Homologação: https://api-h.nuvende.com.br/api/v2/auth/refresh
Ambiente Produção: https://api.nuvende.com.br/api/v2/auth/refresh
Authorization: Bearer {access_token} - Token de acesso atual (não expirado)curl -X POST https://api-h.nuvende.com.br/api/v2/auth/refresh \
-H "Authorization: Bearer seu_access_token_atual"
{
"access_token": "novo_access_token_gerado",
"token_type": "bearer",
"expires_in": 3600
}
Explicação da Resposta:
Vamos integrar o refresh token à classe NuvendeAuth que criamos na página de login:
const axios = require('axios');
class NuvendeAuth {
constructor(clientId, clientSecret, baseUrl) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.baseUrl = baseUrl;
this.accessToken = null;
this.tokenExpiry = null;
}
// ... outros métodos da classe (getAccessToken, logout, etc.) ...
/**
* Renova o access token usando o token atual
* IMPORTANTE: Na API da Nuvende, o refresh usa o access token atual, não um refresh token separado
*/
async refreshAccessToken() {
// Verificar se temos um token para renovar
if (!this.accessToken) {
throw new Error('Nenhum access token disponível para renovação. Faça login primeiro.');
}
try {
console.log('Renovando access token...');
const response = await axios.post(`${this.baseUrl}/api/v2/auth/refresh`, {}, {
headers: {
'Authorization': `Bearer ${this.accessToken}`
}
});
// Atualizar token e tempo de expiração
this.accessToken = response.data.access_token;
this.tokenExpiry = Date.now() + (response.data.expires_in * 1000);
console.log(`Token renovado com sucesso. Expira em ${response.data.expires_in} segundos`);
return this.accessToken;
} catch (error) {
console.error('Erro na renovação do token:', error.response?.data || error.message);
// Se o refresh falhar, limpar tokens e forçar novo login
this.clearTokens();
throw error;
}
}
/**
* Obtém um access token válido
* Se o token atual é válido, retorna ele
* Se expirou, tenta renovar automaticamente
*/
async getValidAccessToken() {
// Verificar se o token ainda é válido (com margem de 5 minutos)
const now = Date.now();
const margin = 5 * 60 * 1000; // 5 minutos em millisegundos
if (this.accessToken && this.tokenExpiry && (now + margin) < this.tokenExpiry) {
console.log('Usando token existente');
return this.accessToken;
}
// Se não temos token, fazer login
if (!this.accessToken) {
console.log('Nenhum token disponível, fazendo login...');
return await this.getAccessToken();
}
// Se o token expirou, tentar renovar
console.log('Token expirado, tentando renovar...');
try {
return await this.refreshAccessToken();
} catch (error) {
console.log('Falha na renovação, fazendo novo login...');
return await this.getAccessToken();
}
}
/**
* Limpa todos os tokens armazenados
*/
clearTokens() {
this.accessToken = null;
this.tokenExpiry = null;
console.log('Tokens limpos');
}
}
// Exemplo de uso prático
async function exemploRefresh() {
const nuvende = new NuvendeAuth(
process.env.NUVENDE_CLIENT_ID,
process.env.NUVENDE_CLIENT_SECRET,
'https://api-h.nuvende.com.br'
);
try {
// Fazer login inicial
await nuvende.getAccessToken();
console.log('Login realizado');
// Fazer algumas operações...
console.log('Realizando operações...');
// Simular expiração do token (em produção, isso acontece automaticamente)
nuvende.tokenExpiry = Date.now() - 1000; // Forçar expiração
// Tentar fazer uma operação (vai renovar automaticamente)
const token = await nuvende.getValidAccessToken();
console.log('Token renovado automaticamente:', token);
} catch (error) {
console.error('Erro:', error.message);
}
}
import requests
import time
from datetime import datetime, timedelta
class NuvendeAuth:
def __init__(self, client_id, client_secret, base_url):
self.client_id = client_id
self.client_secret = client_secret
self.base_url = base_url
self.access_token = None
self.token_expiry = None
# ... outros métodos da classe (get_access_token, logout, etc.) ...
def refresh_access_token(self):
"""
Renova o access token usando o token atual
IMPORTANTE: Na API da Nuvende, o refresh usa o access token atual, não um refresh token separado
"""
if not self.access_token:
raise Exception('Nenhum access token disponível para renovação. Faça login primeiro.')
try:
print('Renovando access token...')
headers = {
'Authorization': f'Bearer {self.access_token}'
}
response = requests.post(
f"{self.base_url}/api/v2/auth/refresh",
headers=headers
)
response.raise_for_status()
data = response.json()
# Atualizar token e tempo de expiração
self.access_token = data['access_token']
self.token_expiry = datetime.now() + timedelta(seconds=data['expires_in'])
print(f"Token renovado com sucesso. Expira em {data['expires_in']} segundos")
return self.access_token
except requests.exceptions.RequestException as e:
print(f"Erro na renovação do token: {e}")
# Se o refresh falhar, limpar tokens e forçar novo login
self.clear_tokens()
raise
def get_valid_access_token(self):
"""
Obtém um access token válido
Se o token atual é válido, retorna ele
Se expirou, tenta renovar automaticamente
"""
# Verificar se o token ainda é válido (com margem de 5 minutos)
now = datetime.now()
margin = timedelta(minutes=5)
if (self.access_token and self.token_expiry and
(now + margin) < self.token_expiry):
print('Usando token existente')
return self.access_token
# Se não temos token, fazer login
if not self.access_token:
print('Nenhum token disponível, fazendo login...')
return self.get_access_token()
# Se o token expirou, tentar renovar
print('Token expirado, tentando renovar...')
try:
return self.refresh_access_token()
except Exception as error:
print('Falha na renovação, fazendo novo login...')
return self.get_access_token()
def clear_tokens(self):
"""
Limpa todos os tokens armazenados
"""
self.access_token = None
self.token_expiry = None
print('Tokens limpos')
# Exemplo de uso prático
async def exemplo_refresh():
nuvende = NuvendeAuth(
os.getenv('NUVENDE_CLIENT_ID'),
os.getenv('NUVENDE_CLIENT_SECRET'),
'https://api-h.nuvende.com.br'
)
try:
# Fazer login inicial
await nuvende.get_access_token()
print('Login realizado')
# Fazer algumas operações...
print('Realizando operações...')
# Simular expiração do token (em produção, isso acontece automaticamente)
nuvende.token_expiry = datetime.now() - timedelta(seconds=1)
# Tentar fazer uma operação (vai renovar automaticamente)
token = await nuvende.get_valid_access_token()
print('Token renovado automaticamente:', token)
except Exception as error:
print(f'Erro: {error}')
Para aplicações que fazem muitas requisições, é útil implementar um interceptor que renova tokens automaticamente:
// Configurar interceptor global do Axios
axios.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
// Se recebeu 401 e ainda não tentou renovar
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
console.log('Token expirado, tentando renovar automaticamente...');
// Renovar token
await nuvende.refreshAccessToken();
// Atualizar header da requisição original
originalRequest.headers.Authorization = `Bearer ${nuvende.accessToken}`;
// Refazer a requisição original
return axios(originalRequest);
} catch (refreshError) {
console.error('Falha na renovação automática:', refreshError);
// Se falhou, limpar tokens e redirecionar para login
nuvende.clearTokens();
clearStoredTokens();
redirectToLogin();
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
Para aplicações de longa duração, implemente renovação preventiva:
class NuvendeAuth {
constructor(clientId, clientSecret, baseUrl) {
// ... código anterior ...
this.refreshTimer = null;
}
/**
* Inicia renovação automática preventiva
* Renova o token 5 minutos antes de expirar
*/
startAutoRefresh() {
if (!this.tokenExpiry) {
console.log('Nenhum token para renovar automaticamente');
return;
}
// Calcular tempo para renovação (5 minutos antes da expiração)
const refreshTime = this.tokenExpiry - Date.now() - (5 * 60 * 1000);
if (refreshTime <= 0) {
console.log('Token já próximo do vencimento, renovando imediatamente');
this.refreshAccessToken().then(() => {
this.startAutoRefresh(); // Agendar próxima renovação
});
return;
}
console.log(`Agendando renovação automática em ${Math.round(refreshTime / 1000)} segundos`);
this.refreshTimer = setTimeout(async () => {
try {
await this.refreshAccessToken();
this.startAutoRefresh(); // Agendar próxima renovação
} catch (error) {
console.error('Falha na renovação automática:', error);
// Em caso de erro, tentar fazer login novamente
try {
await this.getAccessToken();
this.startAutoRefresh();
} catch (loginError) {
console.error('Falha no login automático:', loginError);
}
}
}, refreshTime);
}
/**
* Para a renovação automática
*/
stopAutoRefresh() {
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
this.refreshTimer = null;
console.log('Renovação automática parada');
}
}
}
| Código | Significado | Quando Ocorre | Como Resolver |
|---|---|---|---|
200 | OK | Token renovado com sucesso | - |
401 | Unauthorized | Token inválido ou expirado | Fazer login novamente |
500 | Internal Server Error | Erro interno do servidor | Tentar novamente mais tarde |
class RefreshErrorHandler {
static async handleRefreshError(error, nuvende) {
const status = error.response?.status;
const data = error.response?.data;
switch (status) {
case 401:
console.error('❌ Token inválido ou expirado para renovação:', data);
// Limpar tokens e forçar novo login
nuvende.clearTokens();
clearStoredTokens();
this.redirectToLogin();
break;
case 500:
console.error('❌ Erro interno do servidor na renovação:', data);
// Tentar novamente após um tempo
await this.retryRefresh(nuvende);
break;
default:
console.error('❌ Erro inesperado na renovação:', error.message);
// Limpar tokens e forçar novo login
nuvende.clearTokens();
clearStoredTokens();
this.redirectToLogin();
}
}
static async retryRefresh(nuvende, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
console.log(`Tentativa ${i + 1} de renovação...`);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); // Backoff linear
await nuvende.refreshAccessToken();
console.log('Renovação bem-sucedida após retry');
return;
} catch (error) {
console.error(`Falha na tentativa ${i + 1}:`, error.message);
if (i === maxRetries - 1) {
console.error('Todas as tentativas de renovação falharam');
nuvende.clearTokens();
clearStoredTokens();
this.redirectToLogin();
}
}
}
}
static redirectToLogin() {
// Implementar redirecionamento para página de login
window.location.href = '/login';
}
}
// Configurar interceptor global
axios.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401 && !error.config._retry) {
error.config._retry = true;
try {
await nuvende.refreshAccessToken();
error.config.headers.Authorization = `Bearer ${nuvende.accessToken}`;
return axios(error.config);
} catch (refreshError) {
RefreshErrorHandler.handleRefreshError(refreshError, nuvende);
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
// Iniciar renovação automática após login
async function iniciarAplicacao() {
try {
await nuvende.getAccessToken();
nuvende.startAutoRefresh(); // Iniciar renovação automática
console.log('Aplicação iniciada com renovação automática');
} catch (error) {
console.error('Erro ao iniciar aplicação:', error);
}
}
// Parar renovação automática ao fechar aplicação
window.addEventListener('beforeunload', () => {
nuvende.stopAutoRefresh();
});
// Salvar token no storage local
function salvarToken(token, expiry) {
localStorage.setItem('nuvende_token', token);
localStorage.setItem('nuvende_token_expiry', expiry.toString());
}
// Carregar token do storage
function carregarToken() {
const token = localStorage.getItem('nuvende_token');
const expiry = localStorage.getItem('nuvende_token_expiry');
if (token && expiry && Date.now() < parseInt(expiry)) {
nuvende.accessToken = token;
nuvende.tokenExpiry = parseInt(expiry);
return true;
}
return false;
}