Configuração de Cache com Redis no Django
Para implementar o fluxo OAuth2 de forma segura, é fundamental armazenar o parâmetro state em um mecanismo de cache rápido e confiável. O Redis é a escolha ideal para essa tarefa no ecossistema Django.
Instalação e Configuração
Instale a biblioteca de integração através do pip:
pip install django-redis
Em seguida, adicione a configuração de cache ao arquivo settings.py do seu projeto:
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
},
}
}
Certifique-se de que o serviço do Redis esteja ativo no seu servidor:
sudo systemctl start redis-server
Fluxo de Autenticação OAuth2
O processo de login de terceiros (neste caso, utilizando o AcWing como provedor de identidade) segue o padrão OAuth2 Authorization Code Grant. O fluxo envolve três entidades: o Navegador (Client), a Aplicação Django (Web) e o Servidor de Autorização (AcWing).
- O usuário clica no botão de login e o frontend solicita à aplicação Django a URL de autorização.
- O Django gera um
statealeatório, armazena no Redis e retorna a URL completa comclient_id,redirect_uri,scopeestate. - O frontend redireciona o usuário para o servidor de autorização.
- Após o consantimento do usuário, o servidor de autorização redireciona de volta para a
redirect_uricom umcodetemporário e ostate. - A aplicação Django valida o
statee troca ocodepor umaccess_tokene um identificador único (openid). - Com o
access_token, o Django consulta a API do provedor para obter os dados do perfil (nome de usuário e avatar). - A aplicação cria ou recupera o usuário no banco de dados local e inicia a sessão.
Atualização do Modelo de Dados
Para vincular usuários locais às suas identidades no provedor externo, precisamos adicionar um campo para o identificador único (provider_uid ou openid) no modelo de perfil.
from django.db import models
from django.contrib.auth.models import User
class Player(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar_url = models.URLField(max_length=256, blank=True)
provider_uid = models.CharField(max_length=50, blank=True, null=True, unique=True)
def __str__(self):
return self.user.username
Aplique as migrações para atualizar o esquema do banco de dados:
python manage.py makemigrations
python manage.py migrate
Geração da URL de Autorização
Criaremos um endpoint para gerar a URL de reidrecionamento. Utilizaremos o módulo secrets para gerar tokens criptograficamente seguros e urllib.parse para construir a URL de forma adequada.
Configuração de Rotas
from django.urls import path
from . import views
urlpatterns = [
path("request/", views.request_authorization, name="oauth_request"),
path("callback/", views.handle_oauth_callback, name="oauth_callback"),
]
View de Solicitação
import secrets
from urllib.parse import urlencode
from django.http import JsonResponse
from django.core.cache import cache
def request_authorization(request):
client_id = "SEU_APP_ID"
redirect_uri = "https://seudominio.com/auth/callback/"
scope = "userinfo"
# Gera um state hex de 8 caracteres
state = secrets.token_hex(4)
cache.set(f"oauth_state_{state}", True, timeout=7200)
auth_endpoint = "https://www.acwing.com/third_party/api/oauth2/web/authorize/"
query_params = urlencode({
"appid": client_id,
"redirect_uri": redirect_uri,
"scope": scope,
"state": state
})
return JsonResponse({
"status": "ok",
"auth_url": f"{auth_endpoint}?{query_params}"
})
Integração com Frontend
No lado do cliente, utilizaremos a API fetch para obter a URL e redirecionar o navegador:
function initiateOAuthLogin() {
fetch('/auth/request/')
.then(response => response.json())
.then(data => {
if (data.status === 'ok') {
window.location.href = data.auth_url;
} else {
console.error("Falha ao obter URL de autorização");
}
})
.catch(error => console.error("Erro na requisição:", error));
}
Processamento do Calblack e Troca de Tokens
Quando o provedor redireciona o usuário de volta, precisamos validar o state, trocar o código pelo token de acesso e buscar as informações do usuário.
import requests
import secrets
from django.shortcuts import redirect
from django.core.cache import cache
from django.contrib.auth import login
from django.contrib.auth.models import User
from .models import Player
def handle_oauth_callback(request):
code = request.GET.get("code")
state = request.GET.get("state")
# Validação do state para prevenir ataques CSRF
cache_key = f"oauth_state_{state}"
if not cache.get(cache_key):
return redirect("home")
cache.delete(cache_key)
# Troca do código pelo access_token
token_endpoint = "https://www.acwing.com/third_party/api/oauth2/access_token/"
token_payload = {
"appid": "SEU_APP_ID",
"secret": "SEU_APP_SECRET",
"code": code
}
token_response = requests.get(token_endpoint, params=token_payload).json()
access_token = token_response.get("access_token")
provider_uid = token_response.get("openid")
# Verifica se o usuário já existe
try:
player = Player.objects.get(provider_uid=provider_uid)
login(request, player.user)
return redirect("home")
except Player.DoesNotExist:
pass
# Busca informações do perfil
info_endpoint = "https://www.acwing.com/third_party/api/meta/identity/getinfo/"
info_payload = {
"access_token": access_token,
"openid": provider_uid
}
user_info = requests.get(info_endpoint, params=info_payload).json()
base_username = user_info.get("username")
avatar = user_info.get("photo")
# Tratamento de colisão de nomes de usuário
final_username = base_username
while User.objects.filter(username=final_username).exists():
final_username = f"{base_username}_{secrets.randbelow(1000)}"
# Criação do novo usuário e perfil
new_user = User.objects.create_user(username=final_username)
Player.objects.create(user=new_user, avatar_url=avatar, provider_uid=provider_uid)
login(request, new_user)
return redirect("home")
Com a implementação das views, modelos e rotas, o fluxo de autenticação de terceiros está completo e pronto para ser reiniciado no servidor de aplicação (como uWSGI ou Gunicorn).