SDK Python
Biblioteca cliente Python (hubsaude-client) para obtenção de access tokens via SMART Backend Services.
Instalação
Ainda não publicado no PyPI. Instale a partir do código-fonte (branch develop):
git clone https://github.com/sesgo-ti/hubsaude-cliente-python.git
cd hubsaude-cliente-python
pip install -e .
O extra opcional hsm (pip install "hubsaude-client[hsm]") traz a dependência python-pkcs11, necessária apenas para assinatura via HSM/smart token.
Uso básico
SmartTokenClientBuilder monta o cliente passo a passo e valida a configuração antes de criar a instância. Reutilize o mesmo cliente durante todo o ciclo de vida da aplicação e chame close() uma única vez ao encerrar, ou use o gerenciador de contexto with.
Informe a base do servidor: a biblioteca consulta /.well-known/smart-configuration e resolve o endpoint de token. Não combine .fhir_base com .token_endpoint.
from hubsaude_client.builder import SmartTokenClientBuilder
client = (
SmartTokenClientBuilder()
.client_id("meu-client-id")
.fhir_base("https://hub.saude.go.gov.br")
.private_key_pem("/caminho/para/chave-privada.pem")
.build()
)
resultado = client.obtain_token(scope="system/Patient.rs")
print(resultado)
client.close()
Endpoint de token explícito
Se preferir fixar o endereço de token, use .token_endpoint no lugar de .fhir_base:
from hubsaude_client.builder import SmartTokenClientBuilder
client = (
SmartTokenClientBuilder()
.client_id("meu-client-id")
.token_endpoint("https://hub.saude.go.gov.br/auth/token")
.private_key_pem("/caminho/para/chave-privada.pem")
.build()
)
resultado = client.obtain_token(scope="system/Patient.rs")
print(resultado)
client.close()
Testar com o Simulador
O Simulador reproduz o HubSaúde no seu computador e permite testar a biblioteca antes de receber a credencial oficial. Ele exige os mesmos cuidados de segurança da produção, então a configuração é praticamente a mesma: muda apenas o endereço, a origem do certificado e a confiança no servidor.
Antes de executar o código
Siga os passos de Preparar seu sistema para o Simulador. Ao final deles, você terá:
- o Simulador em execução, iniciado com
hubsaude sim start; - um certificado de teste (
client-cert.pem) e a respectiva chave (client-key.pem), gerados com o OpenSSL; - o sistema cadastrado no Simulador com o identificador
meu-sistemae a permissãosystem/Patient.rs; - o certificado do próprio Simulador (
simulador-server.pem), baixado do endereçohttps://localhost:8443/certs/simulador-server.pem.
Configurar a biblioteca
from hubsaude_client.builder import SmartTokenClientBuilder
client = (
SmartTokenClientBuilder()
.fhir_base("https://localhost:8443")
.client_id("meu-sistema")
.private_key_pem("client-key.pem")
.certificate_pem("client-cert.pem")
.server_trust_anchor("simulador-server.pem")
.build()
)
token = client.obtain_token("system/Patient.rs")
client.close()
Em relação ao uso com a produção, há três diferenças:
.fhir_baseaponta para o Simulador, e a biblioteca descobre sozinha os demais endereços;- a chave e o certificado são os de teste, cadastrados no Simulador, e o identificador é o informado no cadastro;
.server_trust_anchorfaz a biblioteca confiar no certificado do Simulador, que não é emitido por uma autoridade reconhecida.
Se você gerou o certificado de teste com o Java keytool, a chave fica dentro do arquivo client-keystore.p12. Nesse caso, use .client_key_store(caminho, senha), descrito em PKCS#12 direto.
Passar para a produção
Depois que o credenciamento for aprovado, ajuste a mesma configuração:
- troque o endereço do Simulador por
https://hub.saude.go.gov.br; - use o
client_identregue no credenciamento; - substitua a chave e o certificado de teste pelos da instituição;
- remova a linha da âncora de confiança: o certificado do HubSaúde já é reconhecido automaticamente.
Problemas comuns no Simulador
| Situação | Causa provável | O que fazer |
|---|---|---|
| Erro de certificado ao conectar, como falha na verificação da cadeia | O certificado do Simulador não foi informado | Baixe o simulador-server.pem e configure a âncora de confiança |
401 invalid_client informando que o certificado não corresponde ao cadastrado | O certificado usado é diferente do enviado no cadastro | Use o mesmo par de chave e certificado do cadastro |
401 invalid_client informando que o certificado de cliente é obrigatório | O certificado de cliente não foi configurado | Informe o certificado de teste na configuração da biblioteca |
| Erro de permissão ao pedir o token | A permissão solicitada não foi liberada no cadastro | Cadastre o sistema novamente incluindo a permissão desejada |
| O sistema deixa de ser reconhecido | O Simulador foi reiniciado e apagou os cadastros | Cadastre o sistema novamente |
Fontes de chave suportadas
Sua chave privada pode estar em diferentes lugares: um arquivo, um cofre de segredos ou um dispositivo de hardware. O SDK abstrai isso com um contrato simples, hubsaude_client.SigningStrategy (um Protocol com um único método, sign(bytes) -> bytes), e já traz pronta uma factory para as quatro fontes mais comuns em hubsaude_client.strategy_factory.
PEM com senha
client = (
SmartTokenClientBuilder()
.client_id("meu-client-id")
.token_endpoint("https://hub.saude.go.gov.br/auth/token")
.private_key_pem("/caminho/chave-encrypted.pem", password=bytearray(b"minha-senha"))
.build()
)
PKCS#12 direto
Um único arquivo (.pfx/.p12) já reúne a chave e o certificado, protegido por senha. .client_key_store(path, password) resolve os dois de uma vez: a assinatura e o certificado de cliente para mTLS.
client = (
SmartTokenClientBuilder()
.client_id("meu-client-id")
.token_endpoint("https://hub.saude.go.gov.br/auth/token")
.client_key_store("certificado.pfx", "senha-pfx")
.build()
)
HSM via PKCS#11
Um HSM (Hardware Security Module) é um dispositivo dedicado que guarda a chave e nunca a expõe: toda assinatura acontece dentro dele. Requer o extra opcional hsm; o SDK mantém apenas um handle de sessão PKCS#11.
from hubsaude_client.strategy_factory import from_pkcs11
strategy = from_pkcs11(
pkcs11_module_path="/usr/lib/softhsm/libsofthsm2.so",
token_label="meu-token",
key_label="minha-chave-hsm",
user_pin="123456",
)
client = (
SmartTokenClientBuilder()
.client_id("meu-client-id")
.token_endpoint("https://hub.saude.go.gov.br/auth/token")
.signing_strategy(strategy)
.build()
)
Configuração completa
| Método do builder | Obrigatório | Padrão | Observações |
|---|---|---|---|
.token_endpoint(url) | sim¹ | — | URL completa do endpoint de token |
.fhir_base(url) | sim¹ | — | Base FHIR para descoberta automática |
.client_id(id) | sim | — | Emitido pelo Ganesha |
.private_key_pem(path, password=None) | sim² | — | Caminho do PEM da chave privada |
.signing_strategy(strategy) | sim² | — | Estratégia pronta (HSM, cofre etc.) |
.certificate_pem(path) | não³ | — | Certificado do cliente (PEM) |
.client_key_store(path, password) | não | — | mTLS via bundle PKCS#12 |
.server_trust_anchor(path) | não | trust store do sistema | PEM ou certificado X.509 |
.tls_protocol(protocolo) | não | TLSv1.3 | Ex.: TLSv1.2 |
.jwt_algorithm(alg) | não | RS384 | HubSaúde aceita RS384/ES384 |
.key_id(kid) | não | — | Inclui kid no header do JWT |
.hub_context(ig, versao) | não | — | Inclui o claim hub_ctx |
.connect_timeout(timedelta) | não | 10s | Conexão TCP |
.request_timeout(timedelta) | não | 30s | Requisição HTTP completa |
.assertion_ttl_seconds(n) | não | 60 | Deveria ser ≤300 |
.max_retries(n) | não | 3 | Tentativas totais |
.enable_token_cache(bool) | não | true | Ativa ou desativa o cache de tokens |
.token_cache_margin_seconds(n) | não | 30 | Margem de renovação antes da expiração |
.token_cache_max_entries(n) | não | 1000 | Descarte LRU por scope |
¹ Exatamente um entre token_endpoint e fhir_base. ² Exatamente um entre private_key_pem e signing_strategy. ³ Obrigatório apenas para mTLS via chave em memória.
Exceções
| Tipo | Uso |
|---|---|
SmartTokenError | Configuração criptográfica inválida, resposta HTTP/JSON inválida ou algoritmo não suportado |
SigningError | Falha da estratégia criptográfica ao assinar o client_assertion |