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 devNavigate to http://localhost:3000. Visiting /restricted/ will redirect to /login.
Important: Run
npm installandquarto renderbeforevercel devevery time you start fresh. The dev server serves pre-built files from_site/and requires dependencies to be installed for the Edge Middleware to resolvejose.
Note on
SITE_URL: Comment out or removeSITE_URLfrom your.envfor local development. When unset, the OAuth callback defaults tohttp://localhost:3000. IfSITE_URLis set to your production domain, Google will reject the local OAuth request with aredirect_uri_mismatcherror.
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.
- Go to Google Cloud Console
- Create a new project (or select an existing one)
- 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)
- Go to APIs & Services > Credentials
- Click Create Credentials > OAuth 2.0 Client IDs
- Set Application type to Web application
- 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)
- Click Create and save the Client ID and Client Secret
3. Create the Vercel Project
- Install Vercel CLI globally:
npm install -g vercel - 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
- This creates
.vercel/project.jsonwithorgIdandprojectId— 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_EMAILSvariable. Group membership and document access are managed ingroups.config.js(see section 7 below).
Generate JWT_SECRET:
openssl rand -base64 32Set 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:
- Install Quarto and render
_site/ - Install Node.js and
npm install - Pull Vercel project config
- Bundle everything (static + API functions + middleware) with
vercel build - 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
emailsarray. - Remove a user: remove their email from the array.
- Add a document to a group: add its URL path to the group’s
pathsarray. - 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_SECRETimmediately invalidates all existing sessions. All logged-in users will need to sign in again. Similarly, after changing group membership ingroups.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
- In the Vercel dashboard, go to Project > Settings > Domains
- Add your domain (e.g.,
t-scheithauer.com) - Follow the DNS instructions Vercel provides (usually a CNAME or A record)
- Make sure
SITE_URLin your Vercel environment variables matches the custom domain - 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@v2For 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 pandasIn 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:
- Copy
.env.exampleto.envand fill in your values - Comment out
SITE_URLin.envso OAuth useshttp://localhost:3000 - Run
npm install && quarto renderto install deps and build the static site - Run
vercel dev(notnpm run dev) to start the local server, which emulates Vercel’s runtime including Edge Middleware and the/api/routes - 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