fusheriff_head.png Live
FuscAuth

Centralized authentication service for the fuscripts ecosystem. Google and Microsoft OAuth, JWT tokens, shared commons packages, and the beginning of it all

Node.js AWS Serverless DynamoDB Svelte

How it works

Here's what happens when you click "Sign in with Google" or "Sign in with Microsoft" on a fuscripts app.

The journey starts before you even see the login page. When the fuscripts app you're trying to access realizes you're not authenticated, it redirects you to FuscAuth with a return address tucked into the URL:

?returnFuscapp=https://fusclock.fuscripts.com

FuscAuth's SvelteKit frontend grabs that parameter and stashes it in localStorage. This is important — the OAuth flow is about to bounce you through Google's servers, and we need to remember where you came from.

You click the button. The frontend calls the backend's /auth endpoint (or /auth/microsoft for Microsoft), which constructs an OAuth URL with the right client ID, callback URL, and scopes, then sends you off with an HTTP 302 redirect. You're on the provider's turf now.

Google or Microsoft does its thing. Credentials, consent screen, the works. When you approve, the provider redirects back to FuscAuth's callback endpoint with an authorization code.

For Microsoft, FuscAuth uses the common tenant — meaning both personal Microsoft accounts (Outlook, Hotmail) and work/school accounts (Azure AD / Microsoft 365) can sign in. No MSAL library needed; it's the same standard OAuth 2.0 flow as Google, just with Microsoft's endpoints and one quirk: the token exchange requires application/x-www-form-urlencoded instead of JSON.

The four-check identity resolution

The backend exchanges that code for an access token, then fetches your profile (from Google's userinfo API or Microsoft's Graph API). For Microsoft, the email comes from the mail field, with a fallback to userPrincipalName for personal accounts where mail can be null. Now it needs to figure out: who are you in our system? This isn't as straightforward as it sounds, because FuscAuth supports multiple identity scenarios. The handleOAuthLogin function runs four checks in sequence:

Check 1: Provider lookup. Query DynamoDB for AUTHPROVIDER#{provider}#{providerId} — for example, AUTHPROVIDER#google#{googleId} or AUTHPROVIDER#microsoft#{microsoftId}. If you've logged in with this provider before, we find your internal UUID instantly. Done.

Check 2: Legacy migration. If the system is in dual-write mode, check the old schema where Google IDs were the primary key. This handles users from before the UUID migration -- more on that in the next post.

Check 3: Email-based auto-linking. This is the clever one. Your email gets hashed with HMAC-SHA256 using a secret pepper, then we look up EMAILHASH#{hash}. If found, it means you already have an account through a different provider with the same email. Say you signed in with Google first, and later try Microsoft with the same email — FuscAuth links Microsoft to your existing account automatically. One person, one identity, regardless of how they sign in.

Check 4: New user. None of the above matched. Generate a UUID with crypto.randomUUID(), create four DynamoDB records (profile, provider link, email hash lookup, and reverse provider lookup), and welcome them in.

What's in the token

Once we know who you are, the backend signs two JWTs: an access token (7 days) and a refresh token (30 days). The payload is deliberately minimal:

json
{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "iat": 1730000000,
  "exp": 1730604800
}

No email. No name. No Google ID. Just an internal UUID. If a token leaks, none of our personal information goes with it.

The homecoming

The tokens land in the SvelteKit callback page, which stores them in localStorage and shows a success screen.

Then it reads back that returnFuscapp URL from earlier and redirects you home. You're back in fusclock with a valid token, and the whole thing felt like one click.

Under the hood: DynamoDB Monotable

All of this runs on one DynamoDB table with four access patterns:

KeyPurpose
USER#{uuid} + PROFILEUser profile and metadata
USER#{uuid}AUTH#{provider}#{id}Provider linkage (Google, Microsoft, etc.)
EMAILHASH#{hash} + USERCross-provider account linking
AUTHPROVIDER#{provider}#{id}USERFast login lookup

Four records per user. Every query is a direct key lookup. The single-table design means one DynamoDB table serves the entire fuscripts ecosystem, and FuscAuth's access patterns don't step on anyone else's.

The whole backend runs on AWS Lambda behind API Gateway, deployed with Serverless Framework. No servers to manage, no idle costs. The sheriff works on demand.