A integração de sistemas de autenticação de terceiros é uma solução amplamente utilizada para simplificar o processo de login em aplicações web. O uso de provedores como GitHub, Google ou Twitter oferece uma experiência mais fluida aos usuários, ao permitir que eles façam login usando suas credenciais de plataformas que já utilizam. Neste capítulo, abordaremos como implementar a autenticação de terceiros com GitHub, utilizando OAuth2, em uma aplicação FastAPI.

Criação do Aplicativo OAuth no GitHub

O primeiro passo para implementar a autenticação de terceiros com o GitHub é criar um aplicativo OAuth no GitHub. Isso permitirá que você autentique os usuários através de suas credenciais do GitHub.

  1. Acesse sua página pessoal no GitHub e clique no ícone de perfil no canto superior direito. Em seguida, vá até Settings > Developer Settings > OAuth Apps e clique em New OAuth App.

  2. Preencha os campos necessários:

    • Application Name: Dê um nome para o aplicativo, como "SaasFastAPIapp".

    • Homepage URL: A URL da sua página inicial, como http://localhost:8000/.

    • Authorization Callback URL: A URL de callback que será chamada para atualizar o token, como http://localhost:8000/github/auth/token.

  3. Após registrar o aplicativo, anote o Client ID e clique em Generate a new client secret para criar o Client Secret. Armazene esses dados com segurança, pois serão necessários na implementação da autenticação.

Implementação do Login com GitHub na Aplicação

Com o Client ID e o Client Secret em mãos, podemos iniciar a implementação do login com GitHub. Primeiro, criamos um módulo chamado third_party_login.py, onde armazenaremos as variáveis e funções auxiliares para a autenticação.

  1. No módulo third_party_login.py, defina as seguintes variáveis para autenticação:

python
GITHUB_CLIENT_ID = "your_github_client_id"
GITHUB_CLIENT_SECRET = "your_github_client_secret" GITHUB_REDIRECT_URI = "http://localhost:8000/github/auth/token" GITHUB_AUTHORIZATION_URL = "https://github.com/login/oauth/authorize"
  1. Em seguida, crie uma função auxiliar resolve_github_token, que resolverá o token do GitHub e retornará as informações do usuário. Essa função faz uma requisição para a API do GitHub para obter os dados do usuário, como o login e o e-mail, e valida se o usuário já está registrado em sua aplicação.

python
import httpx
from fastapi import Depends, HTTPException from sqlalchemy.orm import Session from models import User, get_session from operations import get_user def resolve_github_token(access_token: str = Depends(OAuth2()), session: Session = Depends(get_session)) -> User: user_response = httpx.get("https://api.github.com/user", headers={"Authorization": access_token}).json() username = user_response.get("login", " ") user = get_user(session, username) if not user: email = user_response.get("email", " ") user = get_user(session, email) if not user: raise HTTPException(status_code=403, detail="Token not valid") return user

Endpoint para Redirecionar ao Login do GitHub

Agora que temos a função para resolver o token, criamos um novo módulo chamado github_login.py, que gerenciará os endpoints relacionados à autenticação com GitHub. O primeiro endpoint que criamos é o /auth/url, que retorna a URL usada para redirecionar o usuário à página de login do GitHub.

python
import httpx
from fastapi import APIRouter router = APIRouter() @router.get("/auth/url") def github_login(): return { "auth_url": GITHUB_AUTHORIZATION_URL + f"?client_id={GITHUB_CLIENT_ID}" }

Callback e Geração do Token de Acesso

Após o usuário se autenticar no GitHub, ele será redirecionado para a URL de callback. No nosso caso, será a URL http://localhost:8000/github/auth/token, que chamará um novo endpoint para obter o token de acesso do GitHub.

python
@router.get("/github/auth/token")
async def github_callback(code: str):
token_response = httpx.post(
"https://github.com/login/oauth/access_token", data={ "client_id": GITHUB_CLIENT_ID, "client_secret": GITHUB_CLIENT_SECRET, "code": code, "redirect_uri": GITHUB_REDIRECT_URI }, headers={"Accept": "application/json"}).json() access_token = token_response.get("access_token") if not access_token: raise HTTPException(status_code=401, detail="User not registered") token_type = token_response.get("token_type", "bearer") return { "access_token": access_token, "token_type": token_type, }

Validação do Token e Acesso à Página Inicial

Agora que temos o token de acesso, podemos criar um endpoint /home que será acessado apenas por usuários autenticados. Esse endpoint usará a função resolve_github_token para validar o token do GitHub e autenticar o usuário na aplicação.

python
from third_party_login import resolve_github_token @router.get("/home")
def homepage(user: UserCreateResponse = Depends(resolve_github_token)):
return { "message": f"Logged in {user.username}!" }

Testando a Implementação

Depois de implementar o login com GitHub, você pode testar o sistema da seguinte maneira:

  1. Registre um usuário com o mesmo nome de usuário ou e-mail que você usará no GitHub.

  2. Acesse o endpoint /auth/url para gerar o link de autenticação.

  3. Use a ferramenta de sua escolha (como Postman ou curl) para testar o endpoint /home fornecendo o token de acesso no cabeçalho da requisição.

Por exemplo, com o curl:

bash
$ curl --location 'http://localhost:8000/home' --header 'Authorization: Bearer gho_EnHbcmHdCHD1Bf2QzJ2B6gyt'

Se tudo estiver configurado corretamente, você receberá uma resposta indicando que o login foi realizado com sucesso.

Considerações Finais

Além do GitHub, é possível implementar autenticação com outros provedores que oferecem OAuth2, como Google e Twitter. O processo é semelhante, com pequenas diferenças na configuração dos aplicativos e nas URLs de autenticação e callback.

Para a implementação de OAuth2 com outros provedores, é recomendável que você consulte a documentação oficial de cada serviço:

Se você deseja expandir sua aplicação para permitir o login com vários provedores, como o Google e o Twitter, o fluxo básico permanecerá o mesmo. No entanto, você precisará ajustar as URLs de autenticação e configurar as credenciais de cada provedor na sua aplicação.

Como Melhorar a Performance de Consultas com Índices no MongoDB

Quando trabalhamos com grandes volumes de dados em bancos de dados NoSQL, como o MongoDB, uma das abordagens mais eficazes para melhorar o desempenho de consultas é o uso de índices. Índices são estruturas de dados que permitem localizar rapidamente informações dentro de grandes conjuntos de dados, acelerando a execução de consultas. Eles desempenham um papel crucial na otimização de buscas, especialmente quando lidamos com coleções volumosas, como aquelas usadas em plataformas de streaming de música, onde precisamos realizar pesquisas frequentes sobre álbuns, artistas e datas de lançamento.

Um ponto importante a ser observado é que no MongoDB os identificadores de músicas em uma playlist são armazenados como strings, e não como ObjectId. Isso significa que, ao consultar a coleção, precisamos realizar a conversão desses IDs para garantir que os resultados sejam corretos. Quando falamos sobre playlists, por exemplo, pode ser necessário realizar duas consultas distintas: uma para recuperar a playlist e outra para obter as músicas baseadas nos seus IDs. Esse processo pode ser otimizado com o uso adequado de índices.

A partir do momento em que implementamos endpoints para criar e recuperar playlists, podemos testar a eficiência dessas operações. Após rodar o servidor com o comando $ uvicorn app.main:app, podemos acessar a documentação interativa em http://localhost:8000/docs e observar os novos endpoints POST /playlist e GET /playlist. Criar algumas músicas, registrar seus IDs e depois criar uma playlist permite testar o endpoint GET /playlist, verificando se a resposta retorna todas as informações das músicas corretamente, incluindo os detalhes do álbum.

Para otimizar essas consultas, podemos usar índices. A criação de índices em campos específicos da nossa coleção pode reduzir significativamente o tempo necessário para localizar os dados desejados. Um exemplo prático disso seria a criação de um índice baseado no campo release_year do álbum. Isso permite que, ao consultar músicas lançadas em um determinado ano, a busca seja feita de forma mais eficiente. Para isso, criamos um índice no MongoDB da seguinte maneira:

python
@app.on_event("startup") async def startup(): db = mongo_database() await db.songs.create_index({"album.release_year": -1})

Esse índice é ordenado de forma decrescente (-1), o que otimiza a busca para consultas que busquem por músicas lançadas em um ano específico.

Outro exemplo seria a busca por artistas. No caso de buscas baseadas em texto, como ao procurar por músicas de um determinado artista, precisamos criar um índice de texto. Esse tipo de índice facilita a execução de consultas de busca textual, como por exemplo, ao procurar por músicas de “Bruno Mars” no banco de dados:

python
@app.on_event("startup") async def startup(): db = mongo_database() await db.songs.create_index({"artist": "text"})

Com esse índice de texto, conseguimos buscar músicas relacionadas a um artista específico de forma muito mais rápida e eficiente. A consulta para buscar músicas por artista pode ser realizada com o seguinte código:

python
@app.get("/songs/artist")
async def get_songs_by_artist(artist: str, db=Depends(mongo_database)): query = db.songs.find({"$text": {"$search": artist}}) songs = await query.to_list(None) return songs

Esses índices não só melhoram a performance das consultas, mas também garantem que as buscas sejam feitas de maneira eficaz, mesmo em bancos de dados com grandes volumes de dados. Além disso, ao criar índices adequados, garantimos que a busca por músicas, álbuns ou artistas seja realizada em tempo hábil, o que melhora significativamente a experiência do usuário na plataforma.

Para testar essas melhorias, é possível verificar, usando a função explain(), se o MongoDB está utilizando o índice correto durante a execução das consultas. O método explain() retorna informações detalhadas sobre o plano de execução da consulta, como o índice utilizado e o tempo de execução.

python
explained_query = await query.explain()
logger.info("Index used: %s", explained_query.get("queryPlanner", {}).get("winningPlan", {}).get("inputStage", {}).get("indexName", "No index used"))

Quando um índice é utilizado corretamente, o log exibirá uma mensagem como "Index used: artist_text" ou "Index used: album.release_year_-1", confirmando que o MongoDB está utilizando o índice desejado para acelerar a execução da consulta.

Além de índices simples como o de texto ou por campo, o MongoDB permite a criação de índices compostos ou até mesmo índices espaciais (2D ou 3D), que são úteis em cenários mais complexos. É importante sempre revisar a documentação oficial do MongoDB para entender as melhores práticas e explorar todas as opções de indexação que o banco de dados oferece.

Por fim, é essencial lembrar que a criação de índices pode impactar a performance de inserções e atualizações nos dados. Embora os índices melhorem significativamente a consulta e a leitura de dados, cada novo índice adicionado ao banco de dados pode aumentar o custo de escrita, já que o banco de dados precisa manter esses índices atualizados à medida que novos documentos são inseridos ou existentes são modificados. Portanto, é importante encontrar um equilíbrio entre a quantidade de índices e o impacto no desempenho geral do sistema.

Como Executar e Gerenciar Aplicações FastAPI com HTTPS para Testes Locais

Para iniciar a execução de uma aplicação com o FastAPI, é essencial garantir que você tenha um módulo mínimo com pelo menos um endpoint. Vamos trabalhar em uma nova aplicação chamada "Live Application". Para isso, crie um novo diretório de projeto chamado live_application e, dentro dele, uma subpasta app contendo o módulo main.py. O código inicial será o seguinte:

python
from fastapi import FastAPI app = FastAPI(title="FastAPI Live Application") @app.get("/") def read_root(): return {"Hello": "World"}

Verifique também se você possui uma versão do FastAPI superior a 0.111.0 no seu ambiente de desenvolvimento, executando o comando:

bash
$ pip install "fastapi~=0.111.0"

Se você já tiver o FastAPI instalado, assegure-se de ter a versão mais recente executando:

bash
$ pip install fastapi --upgrade

Com a instalação ou atualização concluída, você pode iniciar a execução da aplicação. Para isso, basta rodar o seguinte comando na linha de comando:

bash
$ fastapi dev

Este comando iniciará o servidor e você verá informações detalhadas sendo impressas no terminal. A primeira mensagem será algo como:

pgsql
INFO Using path app\main.py

No comando fastapi dev, não especificamos o argumento app.main:app como era feito com o comando uvicorn. O FastAPI CLI detecta automaticamente a classe do objeto FastAPI no código, com base em um conjunto de caminhos padrão. As mensagens seguintes irão detalhar o processo de construção do servidor, identificando os pacotes e módulos a serem considerados, e mostrará explicitamente a importação resolvida para a classe do objeto FastAPI:

pgsql
INFO Importing module main
INFO Found importable FastAPI app

Depois, você verá mensagens especificando o modo de execução com os endereços principais, como este:

nginx
INFO Serving at: http://127.0.0.1:8000 INFO API docs: http://127.0.0.1:8000/docs

Essas mensagens indicam que a aplicação está rodando em modo de desenvolvimento, o que significa que o servidor será reiniciado automaticamente sempre que houver atualizações no código e será acessível através do endereço local 127.0.0.1.

Para rodar a aplicação em modo de produção, sem reinicializações automáticas e visível para a rede local da máquina que hospeda o servidor, utilize o seguinte comando:

bash
$ fastapi run

A FastAPI CLI depende do comando uvicorn para rodar. Alguns parâmetros são semelhantes, como, por exemplo, a possibilidade de rodar o serviço em uma porta diferente da 8000, usando o parâmetro --port, ou de especificar o endereço do host com --host. Você também pode usar o parâmetro --help para ver a documentação do comando e todos os parâmetros disponíveis. Por exemplo:

bash
$ fastapi run --help

Para tornar a aplicação visível para a rede local, basta passar o endereço não especificado 0.0.0.0 para o host:

bash
$ fastapi run --host 0.0.0.0

Esse comando é equivalente ao seguinte:

bash
$ uvicorn app.main:app --host 0.0.0.0

Agora, a sua aplicação estará visível para a rede local da máquina onde o servidor está hospedado.

No entanto, para garantir a segurança da comunicação entre clientes e servidores, especialmente em ambientes de produção, é essencial habilitar HTTPS em suas aplicações web. O HTTPS, ao criptografar os dados transmitidos, protege contra acessos e modificações não autorizados. Para o desenvolvimento local, podemos usar o mkcert para gerar certificados SSL/TLS e testar a aplicação de forma segura.

Primeiramente, instale o mkcert conforme o sistema operacional, e certifique-se de que o comando funciona corretamente:

bash
$ mkcert

Em seguida, instale a Autoridade Certificadora (CA) local para que o navegador confie nos certificados gerados pelo mkcert:

bash
$ mkcert -install

Depois de instalar, crie os certificados para o domínio localhost e o endereço 127.0.0.1 com o seguinte comando:

bash
$ mkcert localhost 127.0.0.1

Este comando irá gerar dois arquivos: example.com+5-key.pem (para a chave privada) e example.com+5.pem (para o certificado). Atenção: Nunca inclua certificados e chaves no seu repositório Git. Adicione esses arquivos ao .gitignore para evitar que sejam versionados.

Para iniciar o servidor com HTTPS, passe a chave e o certificado gerados para o uvicorn:

bash
$ uvicorn app.main:app --port 443 --ssl-keyfile example.com+5-key.pem --ssl-certfile example.com+5.pem

Com isso, o servidor estará rodando com HTTPS. Ao acessar o endereço localhost, você verá o ícone de cadeado no navegador, indicando que a conexão é segura. Caso tente acessar através de HTTP (http://localhost:443), você receberá um erro.

Para garantir que todas as conexões sejam redirecionadas para HTTPS, utilize o middleware HTTPSRedirectMiddleware fornecido pelo FastAPI. Altere o arquivo main.py da seguinte forma:

python
from fastapi import FastAPI
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware app = FastAPI(title="FastAPI Live Application") app.add_middleware(HTTPSRedirectMiddleware)

Agora, qualquer tentativa de acesso via HTTP será automaticamente redirecionada para HTTPS.

Além disso, é fundamental que, ao migrar para um ambiente de produção, você configure corretamente o HTTPS, incluindo a utilização de certificados válidos de autoridades certificadoras confiáveis. Para isso, recomendo o uso de ferramentas como Let's Encrypt, que oferece certificados SSL/TLS gratuitos e automáticos, ou o uso de certificados pagos que atendem aos requisitos de segurança de alto nível.