Deployment Setup Guide

This guide walks through deploying the site to Vercel with Google OAuth authentication for the /restricted/ section.


Prerequisites

  • Quarto CLI 1.4+
  • Node.js 20+
  • A Google account (for Google Cloud Console)
  • A Vercel account (free tier works)
  • A GitHub account

1. Build the Site Locally

# Install Node dependencies (must run before vercel dev, or jose won't resolve)
npm install

# Render the Quarto site (outputs to _site/)
quarto render

# Start the local dev server (do NOT use `npm run dev`)
vercel dev

Navigate to http://localhost:3000. Visiting /restricted/ will redirect to /login.

Important: Run npm install and quarto render before vercel dev every time you start fresh. The dev server serves pre-built files from _site/ and requires dependencies to be installed for the Edge Middleware to resolve jose.

Note on SITE_URL: Comment out or remove SITE_URL from your .env for local development. When unset, the OAuth callback defaults to http://localhost:3000. If SITE_URL is set to your production domain, Google will reject the local OAuth request with a redirect_uri_mismatch error.


2. Create Google OAuth Credentials

You do not need to enable any API from the library. Google OAuth only requires the consent screen and credentials to be configured.

  1. Go to Google Cloud Console
  2. Create a new project (or select an existing one)
  3. Go to APIs & Services > OAuth consent screen
    • User type: External
    • Fill in app name, your email, and developer contact email
    • On the Scopes step, add: openid, email, profile
    • Add your own Google email as a Test user (required while app is in Testing status)
  4. Go to APIs & Services > Credentials
  5. Click Create Credentials > OAuth 2.0 Client IDs
  6. Set Application type to Web application
  7. Under Authorized redirect URIs, add all of:
    • http://localhost:3000/api/auth/callback (local dev)
    • https://<your-project>.vercel.app/api/auth/callback (Vercel preview URL)
    • https://t-scheithauer.com/api/auth/callback (production custom domain)
  8. Click Create and save the Client ID and Client Secret

3. Create the Vercel Project

  1. Install Vercel CLI globally: npm install -g vercel
  2. In the repo root, run: vercel link
    • Select your Vercel account / team
    • Create a new project or link to an existing one
    • When asked for the output directory, set it to _site
  3. This creates .vercel/project.json with orgId and projectId — you will need these in step 5

Vercel project settings (in the dashboard under Project > Settings > General): - Framework Preset: Other - Build Command: echo skip (GitHub Actions does the build) - Output Directory: _site - Install Command: npm install


4. Set Environment Variables in Vercel

Go to Project > Settings > Environment Variables in the Vercel dashboard and add:

Variable Description Example
GOOGLE_CLIENT_ID From step 2 123456.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET From step 2 GOCSPX-...
JWT_SECRET Random 32+ byte secret (see below) (generate with command below)
SITE_URL Your canonical domain https://t-scheithauer.com

Note: There is no ALLOWED_EMAILS variable. Group membership and document access are managed in groups.config.js (see section 7 below).

Generate JWT_SECRET:

openssl rand -base64 32

Set all variables for Production, Preview, and Development environments.


5. Set Up GitHub Actions Secrets

In GitHub, go to Settings > Secrets and variables > Actions > New repository secret and add:

Secret How to obtain
VERCEL_TOKEN Vercel dashboard > Account Settings > Tokens > Create
VERCEL_ORG_ID From .vercel/project.json after running vercel link
VERCEL_PROJECT_ID From .vercel/project.json after running vercel link

6. Deploy

Push to the main branch (or trigger manually from the Actions tab). GitHub Actions will:

  1. Install Quarto and render _site/
  2. Install Node.js and npm install
  3. Pull Vercel project config
  4. Bundle everything (static + API functions + middleware) with vercel build
  5. Deploy to Vercel production with vercel deploy --prebuilt

You can also trigger a deploy manually from the Actions tab in GitHub.


7. Manage Groups and Document Access

Group membership and per-document access control are configured in groups.config.js at the repo root — this is the only file you need to edit.

// groups.config.js
export const groups = {
  groupA: {
    emails: ['alice@gmail.com', 'bob@example.com'],  // who is in this group
    paths: ['/restricted/abc.html'],                  // what they can access
  },
  groupX: {
    emails: ['carol@gmail.com'],
    paths: ['/restricted/xyz.html'],
  },
};
  • Add a user to a group: add their Google account email to the group’s emails array.
  • Remove a user: remove their email from the array.
  • Add a document to a group: add its URL path to the group’s paths array.
  • A user in multiple groups gets access to the union of all their groups’ paths.
  • After editing, commit and push to trigger a redeployment.

Note: Changing JWT_SECRET immediately invalidates all existing sessions. All logged-in users will need to sign in again. Similarly, after changing group membership in groups.config.js, users with existing sessions must log out and back in for the new group assignments to take effect (the new groups are embedded in the JWT at login time).


8. Custom Domain Setup

  1. In the Vercel dashboard, go to Project > Settings > Domains
  2. Add your domain (e.g., t-scheithauer.com)
  3. Follow the DNS instructions Vercel provides (usually a CNAME or A record)
  4. Make sure SITE_URL in your Vercel environment variables matches the custom domain
  5. Add the new domain’s callback URL to Google Cloud Console: https://t-scheithauer.com/api/auth/callback

Adding R or Python Execution Later

To add executable code blocks (e.g., R or Python data analysis) to .qmd pages:

For R: Add this step to .github/workflows/deploy.yml before the quarto render step:

- name: Set up R
  uses: r-lib/actions/setup-r@v2
  with:
    r-version: "4.4"

- name: Install R packages
  uses: r-lib/actions/setup-r-dependencies@v2

For Python: Add this step before quarto render:

- name: Set up Python
  uses: actions/setup-python@v5
  with:
    python-version: "3.12"

- name: Install Python packages
  run: pip install jupyter matplotlib pandas

In your .qmd files, add jupyter: python3 or engine: knitr to the front matter to activate code execution for that page.


Local Development Without Full OAuth

For local testing without going through Google OAuth every time:

  1. Copy .env.example to .env and fill in your values
  2. Comment out SITE_URL in .env so OAuth uses http://localhost:3000
  3. Run npm install && quarto render to install deps and build the static site
  4. Run vercel dev (not npm run dev) to start the local server, which emulates Vercel’s runtime including Edge Middleware and the /api/ routes
  5. To bypass auth locally, you can temporarily add a local cookie via browser DevTools, or use a test Google account that is listed in groups.config.js

Troubleshooting

“redirect_uri_mismatch” error from Google: The redirect URI used during the OAuth flow does not match what is registered in Google Cloud Console. - For local dev: make sure SITE_URL is commented out or removed from .env so the callback uses http://localhost:3000/api/auth/callback - Make sure http://localhost:3000/api/auth/callback is listed in Google Cloud Console under Authorized redirect URIs (alongside your production domain) - For production: check that SITE_URL in Vercel matches your actual domain exactly (including https://) and that the production callback URL is registered in Google

Users get redirected to login even after signing in: - Check that JWT_SECRET is set correctly in Vercel and matches across all environments - The cookie may have expired (7-day default) — the user needs to sign in again

“Your email address is not authorized” error: - The user’s Google account email is not listed in any group in groups.config.js - Check for typos, and remember the check is case-insensitive - If the email was recently added, make sure the change was committed, pushed, and redeployed

GitHub Actions deployment fails at vercel pull: - Verify VERCEL_TOKEN, VERCEL_ORG_ID, and VERCEL_PROJECT_ID secrets are set correctly in GitHub - Regenerate the Vercel token if it has expired