This repository is a minimal, runnable full-stack example that demonstrates how to integrate Auth0 authentication (including Google social login) into a Single Page App (React + Vite) and protect a Node/Express API using JWT validation (JWKS).
Contents
client/— React (Vite) SPA using@auth0/auth0-reactfor auth and requesting access tokens for the API.server/— Express API that validates access tokens usingexpress-jwt+jwks-rsaand exposes public and protected endpoints.
Why this is useful
- Shows an end-to-end flow: SPA → Auth0 (Universal Login / Social) → access token → protected API.
- Demonstrates correct token validation on the backend (issuer, audience, algorithms, JWKS).
- Includes debug helpers and README guidance so you can learn the pieces step-by-step.
Prerequisites
- Node.js 18+ and npm
- An Auth0 account (free tier is fine)
- A Google Cloud Console project if you want Google social login (OAuth credentials)
- A modern browser (Chrome/Firefox/Edge)
Quick local setup (copy-paste)
- Copy environment files and fill with your Auth0 values
cp server/.env.example server/.env
cp client/.env.example client/.env
# Edit server/.env and client/.env and paste values from your Auth0 tenant (see detailed Auth0 setup below)- Start the server and client (two terminals)
Terminal A — server:
cd /Users/anjanmk/authO-socialConnection/server
npm install
node index.js
# or (dev) npx nodemon index.jsTerminal B — client:
cd /Users/anjanmk/authO-socialConnection/client
npm install
npm run dev
# Vite will print the local URL (usually http://localhost:5173)- Open the SPA
- Visit http://localhost:5173
- Click "Login" → Auth0 Universal Login will open. Use Google or a username/password test user.
- After successful login, click "Call Protected API" to call the backend
/api/profilewith the access token.
If everything is configured correctly you will see the protected response JSON with token claims.
Follow these steps to create the Auth0 resources and allow social login.
- Create an API in Auth0 (this defines the audience)
- Auth0 Dashboard → APIs → + Create API
- Name: Example API
- Identifier: https://localhost:3001/api (this exact string is used as AUTH0_AUDIENCE)
- Signing Algorithm: RS256
- Create a Single Page Application in Auth0
- Auth0 Dashboard → Applications → + Create Application
- Name: Auth0 Demo SPA
- Type: Single Page Application
- Save and open Settings
- Allowed Callback URLs: http://localhost:5173
- Allowed Logout URLs: http://localhost:5173
- Allowed Web Origins: http://localhost:5173
- Copy the Client ID → this is AUTH0_CLIENT_ID
- Authorize the SPA to request tokens for the API
- Auth0 Dashboard → APIs → select your API → look for "Authorized Applications" or similar → Authorize Application → select your SPA → Save
- This prevents the "client is not authorized to access resource" error.
- (Optional) Configure Google social login — create OAuth credentials in Google Cloud
- Google Cloud Console → APIs & Services → Credentials → + Create Credentials → OAuth client ID
- Application type: Web application
- Authorized redirect URIs: https://<YOUR_AUTH0_DOMAIN>/login/callback
- (Optional) Authorized JavaScript origins: https://<YOUR_AUTH0_DOMAIN>
- Copy Client ID and Client Secret
- Configure the Google Connection in Auth0
- Auth0 Dashboard → Connections → Social → Google → Create or Edit
- Paste Google Client ID and Client Secret
- Enable the connection for your SPA (toggle your SPA under Applications)
- Save
- (Optional) Add roles and inject them into the access token
- Create a Role: Dashboard → User Management → Roles → + Create Role (e.g., admin)
- Assign role to a test user: Users → select user → Roles → Assign Roles
- Create a Login Action to add roles to the access token (Actions → Flows → Login → add action)
- Example code to add roles claim:
exports.onExecutePostLogin = async (event, api) => {
let roles = [];
if (event.authorization && event.authorization.roles) roles = event.authorization.roles;
api.accessToken.setCustomClaim('https://example.com/roles', roles);
};Env files (what to paste)
- server/.env
AUTH0_DOMAIN=dev-xyz123.us.auth0.com
AUTH0_AUDIENCE=https://localhost:3001/api
PORT=3001
- client/.env (Vite requires VITE_ prefix)
VITE_AUTH0_DOMAIN=dev-xyz123.us.auth0.com
VITE_AUTH0_CLIENT_ID=YOUR_CLIENT_ID
VITE_AUTH0_AUDIENCE=https://localhost:3001/api
VITE_AUTH0_REDIRECT_URI=http://localhost:5173
- Client (
client/src/main.jsx) mountsAuth0Providerfrom@auth0/auth0-reactand passes domain, clientId and authorizationParams (redirect_uri + audience). client/src/App.jsxusesuseAuth0()for loginWithRedirect / loginWithPopup andgetAccessTokenSilently()to get an access token, then calls the server with Authorization: Bearer .- Server (
server/index.js) usesexpress-jwtwithjwks-rsa.expressJwtSecretto fetch JWKS fromhttps://<AUTH0_DOMAIN>/.well-known/jwks.json. It validates issuer, audience and alg (RS256) and exposes/api/profileprotected by the middleware.
- "client is not authorized to access resource": Authorize your SPA in the API settings (APIs → select API → Authorize Application).
redirect_uri_mismatch(Google): Addhttps://<AUTH0_DOMAIN>/login/callbackto the Google OAuth client's Authorized redirect URIs in Google Cloud Console.invalid_stateafter redirect: usually caused by blocked cookies or a browser extension. Try Incognito or disable privacy extensions; popup login can help diagnose./api/profilereturns 401: ensureVITE_AUTH0_AUDIENCE(client) matchesAUTH0_AUDIENCE(server) and that the SPA requests the token with that audience.- No Google button in Universal Login: enable the Google connection for your SPA in the Auth0 Google connection settings.
- Start server:
cd server && node index.js(ornpx nodemon index.js)
- Start client:
cd client && npm run dev(Vite)
- Check public endpoint:
curl -i http://localhost:3001/api/public
- Debug server logs:
tail -f server/server.log
- Never commit
.envfiles with secrets to version control. Add.envto.gitignore(already done). - Use HTTPS in production for both SPA and API.
- For SPAs use Authorization Code + PKCE and consider rotating refresh tokens server-side or using refresh token rotation provided by Auth0.
- Replace the
@auth0/auth0-reactSDK with a manual Authorization Code + PKCE implementation to learn the protocol. - Add
/api/adminwhich checks the namespaced roles claim and restricts access to users withadminrole. - Add unit/integration tests for the server (jest + supertest) that mock a JWT or JWKS endpoint.
- Add GitHub Actions workflow that runs a smoke test for the protected endpoint.
If you want, I can add the admin endpoint and front-end button, or add tests and CI. Tell me which next task you want and I will implement it.
Thanks — good work getting this running. This README documents exactly what we did and why. Happy to expand any section into a tutorial or record a short screencast of the sign-in flow.