|
| 1 | +"""Exemplo de código para autenticação com o Bling de forma interativa. |
| 2 | +
|
| 3 | +Usa o pacote bling_jwt_auth e valida o ``state`` OAuth com os helpers de |
| 4 | +``bling_erp_api.auth`` (``parse_oauth_callback`` + ``verify_oauth_state``). |
| 5 | +
|
| 6 | +Documentação: https://github.com/tempont/bling-jwt-auth-python |
| 7 | +Variáveis (obrigatórias): |
| 8 | + - BLING_CLIENT_ID |
| 9 | + - BLING_CLIENT_SECRET |
| 10 | + - BLING_REDIRECT_URI |
| 11 | +
|
| 12 | +Podem vir do ambiente ou de um arquivo `.env` no diretório corrente. |
| 13 | +`pydantic-settings` resolve o `.env` a partir do CWD, não do diretório |
| 14 | +deste script. |
| 15 | +""" # noqa |
| 16 | + |
| 17 | +import os |
| 18 | +import sys |
| 19 | + |
| 20 | +from bling_jwt_auth import BlingAuthSettings, OAuthClient, TokenManager, create_token_store |
| 21 | +from pydantic import ValidationError |
| 22 | + |
| 23 | +from bling_erp_api.auth import parse_oauth_callback, verify_oauth_state |
| 24 | + |
| 25 | +_FIELD_TO_ENV = { |
| 26 | + "client_id": "BLING_CLIENT_ID", |
| 27 | + "client_secret": "BLING_CLIENT_SECRET", |
| 28 | + "redirect_uri": "BLING_REDIRECT_URI", |
| 29 | +} |
| 30 | + |
| 31 | +# 1. Carrega os settings de variáveis de ambiente. |
| 32 | +try: |
| 33 | + settings = BlingAuthSettings.load() |
| 34 | +except ValidationError as exc: |
| 35 | + missing = sorted( |
| 36 | + { |
| 37 | + _FIELD_TO_ENV.get(str(err["loc"][0]), str(err["loc"][0])) |
| 38 | + for err in exc.errors() |
| 39 | + if err["type"] == "missing" |
| 40 | + } |
| 41 | + ) |
| 42 | + details = "; ".join(f"{'.'.join(map(str, err['loc']))}: {err['msg']}" for err in exc.errors()) |
| 43 | + print( |
| 44 | + "\n⚠️ Falha ao carregar as credenciais OAuth do Bling.\n\n" |
| 45 | + f"Variáveis obrigatórias ausentes: {', '.join(missing)}\n" |
| 46 | + f"Detalhes: {details}\n\n" |
| 47 | + "As credenciais são lidas das variáveis de ambiente `BLING_*` ou de um\n" |
| 48 | + "arquivo `.env` no diretório corrente (de onde o comando foi executado).\n\n" |
| 49 | + "Como resolver:\n" |
| 50 | + " 1. Exporte as variáveis `BLING_*` no ambiente antes de executar\n" |
| 51 | + " este script; ou\n" |
| 52 | + " 2. Copie `.env.example` para `.env` na raiz do projeto e preencha\n" |
| 53 | + " as variáveis com os dados da aplicação OAuth registrada no Bling.\n\n" |
| 54 | + "Nota: o `.env` é resolvido a partir do diretório corrente (CWD),\n" |
| 55 | + "não do diretório deste script. Se as credenciais vêm de um\n" |
| 56 | + "gerenciador de segredos, garanta que as variáveis sejam injetadas\n" |
| 57 | + "no processo que executa este comando.\n", |
| 58 | + file=sys.stderr, |
| 59 | + ) |
| 60 | + sys.exit(1) |
| 61 | + |
| 62 | +# 2. Cria o armazenamento de tokens. (default: SQLite) |
| 63 | +store = create_token_store(settings) |
| 64 | + |
| 65 | +# 3. Cria o cliente Oauth e o TokenManager e inicia a autenticação via CLI/Browser. |
| 66 | +with OAuthClient(settings) as oauth: |
| 67 | + manager = TokenManager(oauth, store, settings) |
| 68 | + |
| 69 | + # 4. Build authorization URL for the browser with a CSRF state value. |
| 70 | + state = os.urandom(16).hex() |
| 71 | + auth_url = oauth.build_authorization_url(state=state) |
| 72 | + print(f"Open in browser: {auth_url}") |
| 73 | + # User opens URL, approves access, Bling redirects to |
| 74 | + # BLING_REDIRECT_URI?code=...&state=... |
| 75 | + |
| 76 | + # 5. Parse the callback (full URL or query string), verify the state |
| 77 | + # against this session and exchange the code for tokens |
| 78 | + # (saves to store automatically). |
| 79 | + raw_callback = input( |
| 80 | + "Paste callback URL or query string (code=...&state=... even from error page): " |
| 81 | + ).strip() |
| 82 | + code, received_state = parse_oauth_callback(raw_callback) |
| 83 | + verify_oauth_state(received_state, state) |
| 84 | + manager.save_from_code(code) |
| 85 | + |
| 86 | + # 6. Now ready — every subsequent call auto-refreshes |
| 87 | + token = manager.get_access_token() |
| 88 | + print("Access token acquired successfully.") |
0 commit comments