OAuth Authorization & the Happening Proxy
How the app signs in to Google Calendar without ever exposing a secret in the binary — PKCE, CSRF protection, and the role of the stateless proxy.
Why No client_secret in the Binary
Desktop applications are "public clients" under OAuth 2.0 — anything compiled into the installer can be extracted by anyone who downloads it. Embedding the client_secret in the app binary would allow it to be harvested and used to impersonate the application with Google's API.
To avoid this, What's Happening? uses PKCE (Proof Key for Code Exchange, RFC 7636) for the user-facing authorization step, combined with a minimal stateless proxy that holds the client_secret server-side and injects it only at the token exchange step. The app binary contains no secret.
How PKCE Works
Before starting the sign-in flow, the app generates two values locally on your device:
- code_verifier — 32 cryptographically random bytes, base64url-encoded (no padding). This value never leaves your device until the token exchange step.
- code_challenge —
BASE64URL(SHA256(code_verifier))using theS256method. This is sent to Google in the initial authorization URL.
Google stores the challenge server-side when the user authenticates. When the app later presents the code_verifier to exchange for tokens, Google recomputes the SHA-256 hash and checks that it matches the stored challenge. Only the device that started the authorization flow — the one that generated the original code_verifier — can complete it, even if the authorization code is intercepted in transit.
State Parameter & CSRF Protection
Alongside the PKCE values, the app generates a separate random state token (32 bytes, base64url-encoded — same method as the code_verifier) and includes it in the authorization URL sent to Google.
Google echoes the state value back in the redirect to the app's local callback server. The app verifies the returned state matches the one it generated before accepting the authorization code. A mismatch causes the flow to abort with a 400 error. This guard is specified in RFC 6749 §10.12 as protection against cross-site request forgery.
The Happening Proxy never sees the state parameter — it is exchanged only between the app, the system browser, and Google's authorization endpoint.
The Happening Proxy
Google's token endpoint requires a client_secret even when PKCE is used. The proxy bridges this gap without requiring the secret to live in the app binary.
The proxy is a minimal Dart service deployed as a Docker container. It exposes a single endpoint — POST /token — and does exactly one thing: it reads the client_secret from a server-side environment variable, appends it to the incoming token request, and forwards the request to https://oauth2.googleapis.com/token. Google's response is returned to the app unchanged.
The proxy is fully stateless:
- It stores nothing — no tokens, no user identifiers, no session state.
- It has no access to your calendar data — it participates only in the OAuth handshake.
- It never logs credential values.
- It can be restarted or horizontally scaled at any time without data loss, because there is no data.
The client_secret is the only piece of information the proxy holds, and it is read from the deployment environment — not from any database or storage layer. Tokens returned by Google pass through the proxy in a single response body and are immediately written to your device's OS keychain via flutter_secure_storage. The proxy does not retain a copy.
The same proxy endpoint is used for automatic token refresh. When your access token expires, the googleapis_auth library silently obtains a new one via the stored refresh token — routed through the proxy in the same way. Your calendar data is never involved in this exchange.
Authorization Flow Diagram
The sequence below shows every step of the OAuth handshake, including which party holds which value at each stage.
Source Code
The proxy and app authentication code are open source and auditable:
- Proxy — proxy/bin/server.dart — the complete proxy implementation in a single Dart file.
- App auth service — app/lib/features/auth/ — PKCE flow, token store, and proxy client.