49 lines
1.8 KiB
Python
49 lines
1.8 KiB
Python
from __future__ import annotations
|
|
|
|
import base64
|
|
|
|
from django.conf import settings
|
|
|
|
|
|
class Microsoft365OAuthError(RuntimeError):
|
|
"""Safe OAuth boundary error that never includes tokens or client secrets."""
|
|
|
|
|
|
def acquire_exchange_access_token(*, client_factory=None) -> str:
|
|
tenant_id = settings.M365_TENANT_ID.strip()
|
|
client_id = settings.M365_CLIENT_ID.strip()
|
|
client_secret = settings.M365_CLIENT_SECRET.strip()
|
|
if not all((tenant_id, client_id, client_secret)):
|
|
raise Microsoft365OAuthError("Microsoft 365 OAuth is niet volledig geconfigureerd.")
|
|
|
|
if client_factory is None:
|
|
import msal
|
|
|
|
client_factory = msal.ConfidentialClientApplication
|
|
factory = client_factory
|
|
client = factory(
|
|
client_id,
|
|
authority=f"https://login.microsoftonline.com/{tenant_id}",
|
|
client_credential=client_secret,
|
|
)
|
|
result = client.acquire_token_for_client(scopes=[settings.M365_EXCHANGE_SCOPE])
|
|
token = result.get("access_token") if isinstance(result, dict) else None
|
|
if not isinstance(token, str) or not token:
|
|
category = "unknown"
|
|
if isinstance(result, dict):
|
|
category = str(result.get("error") or "unknown")[:80]
|
|
raise Microsoft365OAuthError(
|
|
f"Microsoft 365 OAuth-token kon niet worden verkregen ({category})."
|
|
)
|
|
return token
|
|
|
|
|
|
def xoauth2_bytes(username: str, access_token: str) -> bytes:
|
|
if not username or "\x00" in username or "\r" in username or "\n" in username:
|
|
raise Microsoft365OAuthError("Ongeldig Microsoft 365-mailboxadres.")
|
|
return f"user={username}\x01auth=Bearer {access_token}\x01\x01".encode()
|
|
|
|
|
|
def xoauth2_b64(username: str, access_token: str) -> str:
|
|
return base64.b64encode(xoauth2_bytes(username, access_token)).decode("ascii")
|