Capítulo 6.3: Autenticação Básica
Tempo de estudo: 1 hora
1. Autenticação de API: Passe para o MCC
Autenticação é o processo de verificar a identidade de um usuário. Ao contrário de sites com sessões e cookies, APIs stateless (que não mantêm estado) geralmente usam tokens.
O processo é o seguinte:
- O usuário envia seu login e senha para um endpoint especial (por exemplo,
/login
). - O servidor os verifica. Se tudo estiver correto, ele gera um token único, criptografado (uma string longa) e o envia de volta.
- A cada requisição subsequente a recursos protegidos (por exemplo,
POST /planets
), o usuário deve anexar este token no cabeçalhoAuthorization
. - O servidor verifica a validade do token e, se estiver correto, executa a requisição.
💡 Analogia Espacial:
- Login/senha = Seu scan biométrico para obter um passe.
- Token = Passe eletrônico (cartão de identificação) que você recebe na entrada do MCC.
- Cabeçalho
Authorization: Bearer <token>
= Você encosta seu passe no leitor em cada porta protegida.- Endpoints protegidos (POST, PUT, DELETE) = Portas para a sala do servidor ou para o painel de controle de lançamento.
2. Autenticação no Laravel: Sanctum
Laravel oferece uma solução elegante para autenticação de API — Laravel Sanctum. É ideal para SPA (Single Page Applications), aplicativos móveis e APIs simples baseadas em token.
Passo 1: Instalação e Configuração do Sanctum
O Sanctum já vem instalado na aplicação Laravel padrão, mas vamos verificar a configuração.
- Publicação da configuração (se ainda não o fez):
- Execução das migrações (criará a tabela
personal_access_tokens
): - Adição da trait ao modelo
User
: Abraapp/Models/User.php
e certifique-se de que ele usa a traitHasApiTokens
.
Passo 2: Criação de um endpoint para emissão de tokens Precisamos de uma rota onde o usuário enviará seu login/senha.
Adicione em routes/api.php
:`
// routes/api.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use App\Models\User;
use Illuminate\Validation\ValidationException;
Route::post('/login', function (Request $request) {
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['As credenciais estão incorretas.'],
]);
}
// Retorna o token
return response()->json([
'token' => $user->createToken('api-token')->plainTextToken
]);
});
Para teste, você pode criar um usuário via seeder ou Tinker.
Passo 3: Proteção de rotas
Agora vamos proteger nossas operações CRUD. Modifique routes/api.php
:`
// routes/api.php
use App\Http\Controllers\PlanetController;
// Rota pública para visualização de planetas
Route::get('/planets', [PlanetController::class, 'index']);
Route::get('/planets/{planet}', [PlanetController::class, 'show']);
// Grupo de rotas protegidas
Route::middleware('auth:sanctum')->group(function () {
Route::post('/planets', [PlanetController::class, 'store']);
Route::put('/planets/{planet}', [PlanetController::class, 'update']);
Route::delete('/planets/{planet}', [PlanetController::class, 'destroy']);
// Rota para logout (exclusão do token)
Route::post('/logout', function (Request $request) {
$request->user()->currentAccessToken()->delete();
return response()->json(['message' => 'Você saiu com sucesso'], 200);
});
});
O Middleware auth:sanctum
verificará a presença de um token válido no cabeçalho Authorization
.
3. Autenticação no FastAPI: OAuth2 e JWT
No FastAPI, não há um sistema de autenticação embutido, mas existem ferramentas poderosas para implementá-lo. O padrão de fato é o OAuth2 com tokens JWT.
Passo 1: Instalação das dependências
python-jose
: para criar e verificar tokens JWT.passlib
: para hashing e verificação de senhas.python-multipart
: para processar dados de formulário (username
epassword
).
Passo 2: Criação do módulo de segurança (security.py
)
É uma boa prática mover toda a lógica de autenticação para um arquivo separado.
Crie o arquivo security.py
:
# security.py
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta, timezone
# --- Configurações ---
SECRET_KEY = "your-super-secret-key-that-is-long-and-random" # ⚠️ Substitua pela sua chave!
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# --- Utilitários ---
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/login")
# --- Funções ---
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# --- Função de dependência para verificar o token ---
def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Não foi possível validar as credenciais",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
# Aqui você pode retornar o usuário do DB, por enquanto apenas retornamos o nome
return {"username": username}
Passo 3: Integração no main.py
Agora vamos conectar isso à nossa aplicação.
-
Vamos criar o endpoint
/login
:# main.py from fastapi.security import OAuth2PasswordRequestForm from fastapi import Depends, APIRouter from . import security # Importamos nosso módulo # ... seu código FastAPI ... router = APIRouter(prefix="/api/v1") @router.post("/login") def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()): # Aqui deve haver a verificação do usuário no DB # Por exemplo, temos um usuário de teste is_user_valid = (form_data.username == "testuser" and security.verify_password("testpass", security.get_password_hash("testpass"))) if not is_user_valid: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Nome de usuário ou senha inválidos", ) access_token = security.create_access_token(data={"sub": form_data.username}) return {"access_token": access_token, "token_type": "bearer"} # ... app.include_router(router)
-
Vamos proteger os endpoints:
Usamos nossa dependência
get_current_user
.# main.py ou no seu roteador de planetas @router.post("/planets", status_code=status.HTTP_201_CREATED) def create_planet( planet: PlanetCreate, current_user: dict = Depends(security.get_current_user) # <-- Proteção! ): # Lógica de criação de planetas... print(f"O usuário {current_user['username']} está criando um planeta.") # ... return new_planet # Protegemos PUT e DELETE também
4. Uso de tokens no frontend
Nosso frontend agora deve primeiro obter o token, salvá-lo (por exemplo, em localStorage
) e anexá-lo a cada requisição protegida.
Exemplo em JavaScript (fetch
):
// 1. Fazendo login
async function login(email, password) {
const response = await fetch('http://localhost:8001/api/login', { // Endereço da API Laravel
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email, password})
});
const data = await response.json();
if (data.token) {
localStorage.setItem('api_token', data.token); // Salva o token
}
}
// 2. Fazendo uma requisição protegida
async function createPlanet(planetData) {
const token = localStorage.getItem('api_token');
const response = await fetch('http://localhost:8001/api/planets', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}` // <--- Anexa o token!
},
body: JSON.stringify(planetData)
});
// ...
}
Quiz para fixação
🚀 Resumo do Capítulo:
Você instalou um "sistema de controle de acesso" nas suas APIs. Agora, nem todo mundo pode fazer alterações no seu "banco de dados galáctico".
- ✅ Entendeu o princípio da autenticação baseada em token.
- 🔐 Implementou a emissão de tokens e a proteção de rotas no Laravel Sanctum.
- ⚙️ Configurou a autenticação baseada em OAuth2 e JWT no FastAPI.
- 🛰️ Aprendeu como o frontend deve armazenar e usar o token.
Suas APIs se tornaram não apenas funcionais, mas também seguras. No entanto, para que outros desenvolvedores possam usá-las, eles precisam de "instruções de operação".
📌 Verificação:
- Tente fazer uma requisição
POST
para/api/planets
(no Laravel) ou/api/v1/planets
(no FastAPI) sem um token usando Postman ou Insomnia. Você deve receber um erro401 Unauthorized
.- Faça uma requisição para
/login
, obtenha o token, adicione-o ao cabeçalhoAuthorization
e repita a requisiçãoPOST
. Ela deve ser executada com sucesso.