"""Plain Python integration example for gnexus-gauth.
This example shows:
- redirect to gnexus-auth
- callback exchange
- userinfo fetch
- webhook verification and parsing
"""
import json
from datetime import datetime, timezone
from gnexus_gauth.client import GAuthClient
from gnexus_gauth.config import GAuthConfig
from gnexus_gauth.contracts import PkceStoreInterface, StateStoreInterface
from gnexus_gauth.exceptions import WebhookVerificationException
from gnexus_gauth.oauth import AuthorizationUrlBuilder, HttpTokenEndpoint, PkceGenerator
from gnexus_gauth.runtime import HttpRuntimeUserProvider
from gnexus_gauth.support import SystemClock
from gnexus_gauth.webhook import HmacWebhookVerifier, JsonWebhookParser
class SimpleStateStore(StateStoreInterface):
"""Simple in-memory state store for demonstration."""
def __init__(self) -> None:
self._items: dict[str, dict] = {}
def put(self, state: str, expires_at: datetime, context: dict | None = None) -> None:
self._items[state] = {"expires_at": expires_at, "context": context or {}}
def has(self, state: str) -> bool:
record = self._items.get(state)
if record is None:
return False
if record["expires_at"] < datetime.now(timezone.utc):
del self._items[state]
return False
return True
def get_context(self, state: str) -> dict:
if not self.has(state):
return {}
return self._items[state].get("context", {})
def forget(self, state: str) -> None:
self._items.pop(state, None)
class SimplePkceStore(PkceStoreInterface):
"""Simple in-memory PKCE store for demonstration."""
def __init__(self) -> None:
self._items: dict[str, dict] = {}
def put(self, state: str, verifier: str, expires_at: datetime) -> None:
self._items[state] = {"verifier": verifier, "expires_at": expires_at}
def get(self, state: str) -> str | None:
record = self._items.get(state)
if record is None:
return None
if record["expires_at"] < datetime.now(timezone.utc):
del self._items[state]
return None
return record["verifier"]
def forget(self, state: str) -> None:
self._items.pop(state, None)
def make_client() -> GAuthClient:
config = GAuthConfig(
base_url="https://gnexus-auth.local",
client_id="my-service",
client_secret="my-secret",
redirect_uri="https://my-service.local/callback",
)
token_endpoint = HttpTokenEndpoint(config)
runtime_provider = HttpRuntimeUserProvider(config)
webhook_verifier = HmacWebhookVerifier(config)
webhook_parser = JsonWebhookParser()
return GAuthClient(
config=config,
token_endpoint=token_endpoint,
runtime_user_provider=runtime_provider,
webhook_verifier=webhook_verifier,
webhook_parser=webhook_parser,
state_store=SimpleStateStore(),
pkce_store=SimplePkceStore(),
)
def redirect_example() -> None:
client = make_client()
auth_request = client.build_authorization_request(
return_to="/dashboard",
scopes=["openid", "email", "profile", "roles", "permissions"],
)
print(f"Redirect user to: {auth_request.authorization_url}")
def callback_example(code: str, state: str) -> None:
client = make_client()
try:
token_set = client.exchange_authorization_code(code, state)
user = client.fetch_user(token_set.access_token)
except Exception as exc:
print(f"Authorization failed: {exc}")
return
print(f"User ID: {user.user_id}")
print(f"Email: {user.email}")
print(f"Access token: {token_set.access_token[:10]}...")
def webhook_example(raw_body: str, headers: dict, secret: str) -> None:
client = make_client()
try:
event = client.verify_and_parse_webhook(raw_body, headers, secret)
except WebhookVerificationException:
print("Invalid webhook signature.")
return
print(f"Accepted event: {event.event_type} (id={event.event_id})")
if __name__ == "__main__":
# Demonstrate redirect URL generation
redirect_example()
# Demonstrate webhook handling with a dummy payload
dummy_body = json.dumps({"type": "user.updated", "id": "evt_1"})
dummy_headers = {
"X-Gnexus-Event-Id": "evt_1",
"X-Gnexus-Event-Type": "user.updated",
"X-Gnexus-Event-Timestamp": "2024-01-01T00:00:00Z",
"X-Gnexus-Signature": "t=1704067200,v1=abc123",
}
webhook_example(dummy_body, dummy_headers, "webhook_secret")