MS Entra Auth para Flask
Extensão Flask reutilizável e estável, para integrar aplicações web ao Microsoft Entra ID por meio do MSAL. O projeto prioriza application factory, estado server-side, identidade imutável, storage substituível, fluxo observável e contratos de segurança explícitos.
Estado atual
O plano mestre está concluído tecnicamente. A versão 1.0.0 fornece autenticação web Microsoft Entra ID para aplicações Flask, com storage substituível, identidade imutável, fluxo MSAL server-side, hooks, auditoria e hardening.
| Componente |
|---|
| Núcleo, configuração e storage |
| Identidade, MSAL e token cache |
| Login, callback, rotas e logout local |
| Hooks, observabilidade e hardening |
| Template consumidor com Redis e Graph |
| Gate de TestPyPI/PyPI |
O status detalhado está em docs/implementation/status.md.
Instalação
Após a publicação no índice escolhido:
python -m pip install "flask-ms-entra-auth>=1.0,<2"
Instalação para desenvolvimento
Requer Python 3.11 ou superior.
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
No PowerShell:
python -m venv venv
venv\Scripts\activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
Application factory
from flask import Flask
from flask_ms_entra_auth import MicrosoftEntraAuth
entra_auth = MicrosoftEntraAuth()
def create_app() -> Flask:
app = Flask(__name__)
app.secret_key = "obtido-de-um-secret-provider"
app.config.from_mapping(
MS_ENTRA_CLIENT_ID="11111111-1111-1111-1111-111111111111",
MS_ENTRA_CLIENT_SECRET="obtido-de-um-secret-provider",
MS_ENTRA_TENANT_ID="22222222-2222-2222-2222-222222222222",
MS_ENTRA_REDIRECT_URI="https://app.example.com/auth/callback",
MS_ENTRA_SCOPES=["User.Read"],
MS_ENTRA_SESSION_NAMESPACE="minha-aplicacao",
MS_ENTRA_TOKEN_CACHE_TTL=28_800,
MS_ENTRA_FLOW_TTL=600,
MS_ENTRA_IDENTITY_TTL=28_800,
MS_ENTRA_EVENT_LOGGING=True,
MS_ENTRA_REQUEST_ID_HEADER="X-Request-ID",
MS_ENTRA_REQUIRE_ATOMIC_STORAGE=True,
MS_ENTRA_STRICT_SECURITY=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE="Lax",
SESSION_COOKIE_SECURE=True,
)
entra_auth.init_app(app)
return app
Por padrão, são registradas as rotas GET /auth/login, GET /auth/callback e POST /auth/logout. A inicialização não cria cliente MSAL nem acessa rede.
Vínculo com usuário local
O hook autenticado roda depois da validação Microsoft e antes da sessão local ser estabelecida:
from flask_ms_entra_auth import AuthenticationRejected, Identity
@entra_auth.on_authenticated
def vincular_usuario(identity: Identity) -> None:
usuario = repositorio.buscar_por_entra(
tenant_id=identity.tenant_id,
object_id=identity.object_id,
)
if usuario is None or not usuario.ativo:
raise AuthenticationRejected("usuário local indisponível")
AuthenticationRejected preserva a decisão da aplicação. Outras falhas do hook viram LocalBindingError, com causa preservada e mensagem pública sanitizada. Nenhum hook concede roles ou permissões automaticamente.
Hook de logout
from flask_ms_entra_auth import Identity
@entra_auth.on_logout
def registrar_logout(identity: Identity | None) -> None:
auditoria.registrar_logout(had_identity=identity is not None)
A limpeza da sessão e do token cache ocorre antes do hook (uma falha posterior não restaura a autenticação removida).
Hooks de erro e evento
from flask_ms_entra_auth import AuthEvent, MicrosoftEntraAuthError
@entra_auth.on_error
def observar_erro(error: MicrosoftEntraAuthError) -> None:
metricas.incrementar(type(error).__name__)
@entra_auth.on_event
def observar_evento(event: AuthEvent) -> None:
metricas.evento(event.name, request_id=event.request_id)
Hooks de erro e evento são best effort. Falhas neles são contabilizadas, mas não substituem o erro original nem interrompem autenticação bem-sucedida.
AuthEvent pode conter apenas nome, timestamp, request ID, endpoint, método, duração, código normalizado e correlation ID. Não contém identidade, claims, tokens, auth code ou payload do provedor.
Proteção de endpoints
from flask_ms_entra_auth import current_identity
@app.get("/conta")
@entra_auth.login_required
def conta() -> dict[str, str]:
return {
"object_id": current_identity.object_id,
"tenant_id": current_identity.tenant_id,
}
Usuários anônimos podem ser redirecionados em GET e HEAD. Métodos que alteram estado geram AuthenticationRequired.
Storage e consumo atômico
O contrato mínimo continua sendo AuthStorage. Backends distribuídos podem implementar AtomicAuthStorage:
from flask_ms_entra_auth import AtomicAuthStorage
class RedisAuthStorage:
def load(self, key: str) -> bytes | None: ...
def save(self, key: str, value: bytes, *, ttl: int | None = None) -> None: ...
def delete(self, key: str) -> None: ...
def take(self, key: str) -> bytes | None: ... # operação única no backend
storage: AtomicAuthStorage = RedisAuthStorage()
take() deve ler e remover o valor em uma única operação do backend. Quando ausente, a extensão mantém fallback compatível protegido apenas dentro do adaptador local. Esse fallback não oferece garantia entre processos.
Use MS_ENTRA_REQUIRE_ATOMIC_STORAGE=True em produção quando callback e sessão puderem atingir múltiplos workers.
Auditoria de segurança
report = entra_auth.audit_security(app)
for finding in report.findings:
print(finding.severity, finding.code, finding.message)
A auditoria é somente leitura e não exibe valores de secrets. Ela verifica:
- Existência e tamanho mínimo do
SECRET_KEY; SESSION_COOKIE_HTTPONLY;SESSION_COOKIE_SAMESITE;SESSION_COOKIE_SECUREfora de loopback;- Uso de
MemoryStoragefora de teste ou debug; - Suporte nativo a consumo atômico.
MS_ENTRA_STRICT_SECURITY=True impede inicialização quando houver achados de severidade error. Avisos continuam visíveis sem bloquear automaticamente o processo.
Logging estruturado
Com MS_ENTRA_EVENT_LOGGING=True, a extensão usa o logger flask_ms_entra_auth e adiciona campos normalizados ao LogRecord. A mensagem é constante e nenhum valor sensível é interpolado.
O header definido por MS_ENTRA_REQUEST_ID_HEADER é aceito somente quando possui formato seguro. Caso contrário, a extensão gera uma referência aleatória por requisição.
Erros públicos
MicrosoftEntraAuthError
├── ConfigurationError
├── StorageError
├── AuthenticationError
│ ├── AuthenticationRequired
│ ├── AuthenticationCancelled
│ ├── AuthenticationRejected
│ ├── InvalidCallbackError
│ ├── InvalidNavigationTarget
│ ├── IdentityValidationError
│ └── ConsentRequired
├── HookExecutionError
│ └── LocalBindingError
├── TokenAcquisitionError
└── ProviderUnavailableError
O blueprint traduz erros previsíveis para respostas curtas. Rejeição local usa 403; falhas de hook e storage usam 503.
Validação local
ruff check .
ruff format --check .
mypy src tests scripts
pytest
bandit -c pyproject.toml -r src scripts
pip-audit .
python -m build
python -m twine check dist/*
DIST_DIR=dist pytest tests/test_distribution.py --no-cov
No PowerShell:
$env:DIST_DIR = "dist"
pytest tests/test_distribution.py --no-cov
Remove-Item Env:DIST_DIR
Release e compatibilidade
O repositório inclui gate de release, validação de tag e workflows separados para TestPyPI e PyPI por Trusted Publishing. Consulte o checklist e o guia de publicação.
O consumidor de referência está em msentraauth.flask-template e valida a série 1.x com Redis, Microsoft Graph, frontend, health checks e Docker.
Limites atuais
-
A extensão autentica e a aplicação autoriza;
-
Hooks não oferecem transação ou rollback automático de banco;
-
Token cache permanece last-write-wins, sem CAS distribuído;
-
MemoryStoragenão é seguro para produção; -
Redis, Graph, interface e deploy permanecem no template consumidor;
-
Logout local não encerra todas as sessões Microsoft;
-
Publicação em índices externos nunca ocorre sem ação explícita do mantenedor.
Consulte o threat model antes de usar a extensão em produção.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flask_ms_entra_auth-1.0.0.tar.gz.
File metadata
- Download URL: flask_ms_entra_auth-1.0.0.tar.gz
- Upload date:
- Size: 80.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
546e69f32ed01c3bbb8d289aff86cf420685df736fc15e1d6031d7d81aa43d20
|
|
| MD5 |
6c1d44f06a52ec16fdb6d2dfdf9c3fd5
|
|
| BLAKE2b-256 |
7de69d67d203585cf5dd1261fd667889cc8bb92e11e138478fe12d468cbb8a2b
|
Provenance
The following attestation bundles were made for flask_ms_entra_auth-1.0.0.tar.gz:
Publisher:
release.yml on vitoroliveirasilva/msentraauth.flask-extension
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flask_ms_entra_auth-1.0.0.tar.gz -
Subject digest:
546e69f32ed01c3bbb8d289aff86cf420685df736fc15e1d6031d7d81aa43d20 - Sigstore transparency entry: 2207145462
- Sigstore integration time:
-
Permalink:
vitoroliveirasilva/msentraauth.flask-extension@0007e8f38874196395a9a28b638ca7c7fc3dd715 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/vitoroliveirasilva
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0007e8f38874196395a9a28b638ca7c7fc3dd715 -
Trigger Event:
release
-
Statement type:
File details
Details for the file flask_ms_entra_auth-1.0.0-py3-none-any.whl.
File metadata
- Download URL: flask_ms_entra_auth-1.0.0-py3-none-any.whl
- Upload date:
- Size: 42.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5de6d70c1732faea13ac6f4bd72d41d0eb7ffef1309e1b2ff32b3a34fea2adf8
|
|
| MD5 |
2c3e5736e90a7c29f240c23e53a72094
|
|
| BLAKE2b-256 |
81f456d917aa97194389e089bc6d1bf0fdd69038a8984865504e7a79bc454757
|
Provenance
The following attestation bundles were made for flask_ms_entra_auth-1.0.0-py3-none-any.whl:
Publisher:
release.yml on vitoroliveirasilva/msentraauth.flask-extension
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flask_ms_entra_auth-1.0.0-py3-none-any.whl -
Subject digest:
5de6d70c1732faea13ac6f4bd72d41d0eb7ffef1309e1b2ff32b3a34fea2adf8 - Sigstore transparency entry: 2207145472
- Sigstore integration time:
-
Permalink:
vitoroliveirasilva/msentraauth.flask-extension@0007e8f38874196395a9a28b638ca7c7fc3dd715 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/vitoroliveirasilva
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0007e8f38874196395a9a28b638ca7c7fc3dd715 -
Trigger Event:
release
-
Statement type: