When we first built our admin console for the URL shortener, we kept things simple: a bearer token in an environment variable, rate limiting at the edge, and a static HTML page in S3. It worked — but it wasn’t elegant. A single leaked secret would compromise the whole control plane. And logout didn’t mean much, since anyone with the token could walk right back in.

So we raised the bar. We added a real OAuth flow, using GitHub as the identity provider, and AWS KMS to sign short-lived JSON Web Tokens (JWTs). Now, instead of trusting one secret, we trust the world’s largest code host to verify identity, and we trust AWS to keep our signing key secure.


Why OAuth?

OAuth solves two problems at once:

  1. Identity: I want to know who is using my admin console. GitHub logins provide that, and we can enforce an allowlist of approved maintainers.
  2. Revocation: When someone leaves the project, I don’t have to rotate a global secret. I just remove them from the allowlist, and they’re instantly cut off.

That’s cleaner and safer than the shared bearer token we started with.


The Flow

The high-level flow looks like this:

[ Browser ] → /admin → CloudFront → Lambda@Edge (check cookie)
↳ if no JWT → /v1/admin/oauth/start
↳ redirect to GitHub → /v1/admin/oauth/callback → validate + mint JWT
↳ set cookie → back to /admin

OAuth Flow Diagram


JWTs with KMS

Instead of baking a private key into our code, we use AWS KMS to sign tokens. Lambda builds the JWT header + payload, then calls kms:Sign to generate the signature. That signature is verifiable with the public key that we embed in our Lambda@Edge verifier.

The result is a short-lived JWT stored in a secure, HttpOnly cookie. Every /admin request carries it, and the edge verifies it in microseconds without ever calling KMS.

KMS Flow Diagram


Routes Through the System

Our router now has a few more branches. The data plane (redirects) hasn’t changed, but the control plane is richer:

  • GET /v1/admin/oauth/start
    Generates a state value, sets a short-lived cookie, and redirects the browser to GitHub.

  • GET /v1/admin/oauth/callback
    Exchanges the code for a GitHub access token, fetches the user profile, checks against the allowlist, and issues a new qs_admin JWT cookie.

  • POST /v1/admin/logout
    Clears the cookie and drops you back to /admin.

  • GET /v1/admin/me
    Verifies the JWT against KMS and returns the logged-in username. Our admin UI calls this to show “Logged in as ”.

And, of course, the existing routes:

  • POST /v1/links — create a new short link (requires Authorization header).
  • GET /v1/links — list all links.
  • DELETE /v1/links/{slug} — delete a link.
  • GET /{slug} — the redirect path, cached heavily at CloudFront.

Router Flow Diagram


Our Data Model

All of this control plane and authentication scaffolding sits on top of a very simple data plane: a single DynamoDB table. Each short link is a row keyed by its slug, with attributes for the target URL, the created_at timestamp, a visits counter, and an optional expires_at timestamp for TTL-based cleanup. For richer queries in the admin console we can also add a GSI on created_at, but the core table remains compact and fast.

Dynamo Table Schema


Where We Are Now

We’ve gone from “one secret rules them all” to a much healthier model:

  • User identity tied to real GitHub accounts.
  • JWTs that expire quickly and are signed with a secure, managed key.
  • Edge enforcement that keeps latency low and caching intact.

Admins can log in, see who they are, and log out. Our infra is still defined entirely in Terraform, and Rust is still our language of choice.


What’s Next?

We’ve now got identity nailed down. Next up, we’ll make the admin console more powerful and user-friendly:

  • Add “Create” and “Edit” directly in the UI, so you don’t need to curl.
  • Add custom CloudFront error pages, so a 404 on a missing slug redirects users to a helpful landing page on our site.
  • Add some basic analytics: count clicks, show popular links, and maybe generate QR codes.

Our shortener is growing up — and this is exactly the kind of project that teaches the real-world lessons of modern systems design.