Skip to content

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:

  1. O usuário envia seu login e senha para um endpoint especial (por exemplo, /login).
  2. O servidor os verifica. Se tudo estiver correto, ele gera um token único, criptografado (uma string longa) e o envia de volta.
  3. A cada requisição subsequente a recursos protegidos (por exemplo, POST /planets), o usuário deve anexar este token no cabeçalho Authorization.
  4. 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.

  1. Publicação da configuração (se ainda não o fez):
    php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
    
  2. Execução das migrações (criará a tabela personal_access_tokens):
    php artisan migrate
    
  3. Adição da trait ao modelo User: Abra app/Models/User.php e certifique-se de que ele usa a trait HasApiTokens.
    // app/Models/User.php
    use Laravel\Sanctum\HasApiTokens;
    
    class User extends Authenticatable
    {
        use HasApiTokens, HasFactory, Notifiable;
        // ...
    }
    

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

pip install "python-jose[cryptography]" "passlib[bcrypt]" "python-multipart"

  • 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 e password).

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.

  1. 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)
    

  2. 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

1. APIs sem estado geralmente usam para autenticação:

2. No Laravel, o middleware usado para proteger rotas com tokens é:

3. No FastAPI, a dependência usada para obter dados do formulário de login é:

4. Como o token é transmitido do cliente para o servidor em uma requisição protegida?


🚀 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 erro 401 Unauthorized.
  • Faça uma requisição para /login, obtenha o token, adicione-o ao cabeçalho Authorization e repita a requisição POST. Ela deve ser executada com sucesso.