Segurança - Primeiros Passos¶
Vamos imaginar que você tem a sua API de backend em algum domínio.
E você tem um frontend em outro domínio ou em um path diferente no mesmo domínio (ou em uma aplicação mobile).
E você quer uma maneira de o frontend autenticar com o backend, usando um username e password.
Podemos usar OAuth2 para construir isso com o FastAPI.
Mas vamos poupar o seu tempo de ler toda a especificação extensa apenas para achar as pequenas informações de que você precisa.
Vamos usar as ferramentas fornecidas pelo FastAPI para lidar com segurança.
Como Parece¶
Vamos primeiro usar o código e ver como funciona, e depois voltaremos para entender o que está acontecendo.
Crie um main.py¶
Copie o exemplo em um arquivo main.py:
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
🤓 Other versions and variants
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from typing_extensions import Annotated
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
Execute-o¶
Informação
O pacote python-multipart é instalado automaticamente com o FastAPI quando você executa o comando pip install "fastapi[standard]".
Entretanto, se você usar o comando pip install fastapi, o pacote python-multipart não é incluído por padrão.
Para instalá-lo manualmente, certifique-se de criar um ambiente virtual, ativá-lo e então instalá-lo com:
$ pip install python-multipart
Isso ocorre porque o OAuth2 usa "form data" para enviar o username e o password.
Execute o exemplo com:
$ fastapi dev main.py
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Verifique-o¶
Vá até a documentação interativa em: http://127.0.0.1:8000/docs.
Você verá algo deste tipo:

Botão Autorizar!
Você já tem um novo botão 'Authorize'.
E sua operação de rota tem um pequeno cadeado no canto superior direito em que você pode clicar.
E se você clicar, verá um pequeno formulário de autorização para digitar um username e um password (e outros campos opcionais):

Nota
Não importa o que você digite no formulário, ainda não vai funcionar. Mas nós vamos chegar lá.
Claro que este não é o frontend para os usuários finais, mas é uma ótima ferramenta automática para documentar interativamente toda a sua API.
Pode ser usada pelo time de frontend (que pode ser você mesmo).
Pode ser usada por aplicações e sistemas de terceiros.
E também pode ser usada por você mesmo, para depurar, verificar e testar a mesma aplicação.
O fluxo de password¶
Agora vamos voltar um pouco e entender o que é isso tudo.
O "fluxo" password é uma das formas ("fluxos") definidas no OAuth2 para lidar com segurança e autenticação.
O OAuth2 foi projetado para que o backend ou a API pudesse ser independente do servidor que autentica o usuário.
Mas, neste caso, a mesma aplicação FastAPI irá lidar com a API e com a autenticação.
Então, vamos rever de um ponto de vista simplificado:
- O usuário digita o
usernamee opasswordno frontend e pressionaEnter. - O frontend (rodando no navegador do usuário) envia esse
usernameepasswordpara uma URL específica na nossa API (declarada comtokenUrl="token"). - A API verifica esse
usernameepassword, e responde com um "token" (ainda não implementamos nada disso).- Um "token" é apenas uma string com algum conteúdo que podemos usar depois para verificar esse usuário.
- Normalmente, um token é definido para expirar depois de algum tempo.
- Então, o usuário terá que fazer login novamente em algum momento.
- E se o token for roubado, o risco é menor. Não é como uma chave permanente que funcionará para sempre (na maioria dos casos).
- O frontend armazena esse token temporariamente em algum lugar.
- O usuário clica no frontend para ir para outra seção do aplicativo web.
- O frontend precisa buscar mais dados da API.
- Mas precisa de autenticação para aquele endpoint específico.
- Então, para autenticar com nossa API, ele envia um header
Authorizationcom o valorBearermais o token. - Se o token contém
foobar, o conteúdo do headerAuthorizationseria:Bearer foobar.
O OAuth2PasswordBearer do FastAPI¶
O FastAPI fornece várias ferramentas, em diferentes níveis de abstração, para implementar essas funcionalidades de segurança.
Neste exemplo, vamos usar OAuth2, com o fluxo Password, usando um token Bearer. Fazemos isso usando a classe OAuth2PasswordBearer.
Informação
Um token "bearer" não é a única opção.
Mas é a melhor para o nosso caso de uso.
E pode ser a melhor para a maioria dos casos de uso, a menos que você seja um especialista em OAuth2 e saiba exatamente por que existe outra opção que se adapta melhor às suas necessidades.
Nesse caso, o FastAPI também fornece as ferramentas para construí-la.
Quando criamos uma instância da classe OAuth2PasswordBearer, passamos o parâmetro tokenUrl. Esse parâmetro contém a URL que o client (o frontend rodando no navegador do usuário) usará para enviar o username e o password para obter um token.
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
🤓 Other versions and variants
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from typing_extensions import Annotated
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
Dica
Aqui tokenUrl="token" refere-se a uma URL relativa token que ainda não criamos. Como é uma URL relativa, é equivalente a ./token.
Como estamos usando uma URL relativa, se sua API estivesse localizada em https://example.com/, então se referiria a https://example.com/token. Mas se sua API estivesse localizada em https://example.com/api/v1/, então se referiria a https://example.com/api/v1/token.
Usar uma URL relativa é importante para garantir que sua aplicação continue funcionando mesmo em um caso de uso avançado como Atrás de um Proxy.
Esse parâmetro não cria aquele endpoint/operação de rota, mas declara que a URL /token será aquela que o client deve usar para obter o token. Essa informação é usada no OpenAPI e depois nos sistemas de documentação interativa da API.
Em breve também criaremos a operação de rota real.
Informação
Se você é um "Pythonista" muito rigoroso, pode não gostar do estilo do nome do parâmetro tokenUrl em vez de token_url.
Isso ocorre porque ele usa o mesmo nome da especificação do OpenAPI. Assim, se você precisar investigar mais sobre qualquer um desses esquemas de segurança, pode simplesmente copiar e colar para encontrar mais informações sobre isso.
A variável oauth2_scheme é uma instância de OAuth2PasswordBearer, mas também é "chamável" (callable).
Ela pode ser chamada como:
oauth2_scheme(some, parameters)
Então, pode ser usada com Depends.
Use-o¶
Agora você pode passar esse oauth2_scheme em uma dependência com Depends.
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
🤓 Other versions and variants
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from typing_extensions import Annotated
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
Tip
Prefer to use the Annotated version if possible.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
Essa dependência fornecerá uma str que é atribuída ao parâmetro token da função de operação de rota.
O FastAPI saberá que pode usar essa dependência para definir um "esquema de segurança" no esquema OpenAPI (e na documentação automática da API).
Detalhes Técnicos
O FastAPI saberá que pode usar a classe OAuth2PasswordBearer (declarada em uma dependência) para definir o esquema de segurança no OpenAPI porque ela herda de fastapi.security.oauth2.OAuth2, que por sua vez herda de fastapi.security.base.SecurityBase.
Todos os utilitários de segurança que se integram com o OpenAPI (e com a documentação automática da API) herdam de SecurityBase, é assim que o FastAPI sabe como integrá-los ao OpenAPI.
O que ele faz¶
Ele irá procurar na requisição pelo header Authorization, verificar se o valor é Bearer mais algum token e retornará o token como uma str.
Se não houver um header Authorization, ou se o valor não tiver um token Bearer, ele responderá diretamente com um erro de status 401 (UNAUTHORIZED).
Você nem precisa verificar se o token existe para retornar um erro. Você pode ter certeza de que, se sua função for executada, ela terá uma str nesse token.
Você já pode experimentar na documentação interativa:

Ainda não estamos verificando a validade do token, mas isso já é um começo.
Recapitulando¶
Então, com apenas 3 ou 4 linhas extras, você já tem alguma forma primitiva de segurança.