Lexoffice.login | [better]

import hashlib import secrets import requests from urllib.parse import urlencode, urlparse, parse_qs class LexofficeLogin: AUTH_URL = "https://login.lexoffice.io/connect/authorize" TOKEN_URL = "https://login.lexoffice.io/connect/token"

– lexoffice, OAuth 2.0, PKCE, API security, cloud accounting, single-page application (SPA), authentication flow. 1. Introduction lexoffice is a leading cloud accounting software for small and medium-sized enterprises (SMEs) in Germany. Its API (documented at https://developers.lexoffice.io ) enables automated invoice creation, contact management, and financial reporting. All API endpoints require authenticated access, governed by the lexoffice.login process. lexoffice.login

# Exchange data = "grant_type": "authorization_code", "code": auth_code, "redirect_uri": self.redirect_uri, "client_id": self.client_id, "code_verifier": self.code_verifier resp = requests.post(self.TOKEN_URL, data=data) resp.raise_for_status() tokens = resp.json() return tokens # contains access_token, refresh_token, expires_in import hashlib import secrets import requests from urllib

def _generate_pkce_pair(self): """Generate code_verifier and code_challenge (S256).""" self.code_verifier = secrets.token_urlsafe(64)[:128] code_challenge = hashlib.sha256(self.code_verifier.encode()).digest() # Base64url encode without padding code_challenge = secrets.token_urlsafe(32) # simplified; use proper base64url # For correctness, implement: import base64 code_challenge = base64.urlsafe_b64encode( hashlib.sha256(self.code_verifier.encode()).digest() ).decode().rstrip("=") return code_challenge Its API (documented at https://developers