Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 43 additions & 48 deletions plugins/auth0/skills/auth0-fastify/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: auth0-fastify
description: Use when adding authentication (login, logout, protected routes) to Fastify web applications - integrates @auth0/auth0-fastify for session-based auth. For stateless Fastify APIs use auth0-fastify-api instead.
description: "Use when adding authentication (login, logout, protected routes) to Fastify web applications β€” integrates @auth0/auth0-fastify for login, logout, OAuth callback handling, user profile retrieval, and access token management. For stateless Fastify APIs, use auth0-fastify-api instead."
license: Apache-2.0
metadata:
author: Auth0 <support@auth0.com>
Expand All @@ -14,6 +14,11 @@ metadata:

Add authentication to Fastify web applications using @auth0/auth0-fastify.

> **Agent instruction:** Before providing SDK setup instructions, fetch the latest release version by running:
> ```bash
> gh api repos/auth0/auth0-fastify/releases/latest --jq '.tag_name'
> ```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the intention with removing prerequisites and when NOT to use here?

Comment on lines +17 to +20

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor | ⚑ Quick win

Add blank line before the fenced code block.

Markdown specification requires blank lines around fenced code blocks. Add a blank line between line 17 and line 18 to resolve the MD031 linting warning.

πŸ“ Proposed fix
 > **Agent instruction:** Before providing SDK setup instructions, fetch the latest release version by running:
+>
 > ```bash
 > gh api repos/auth0/auth0-fastify/releases/latest --jq '.tag_name'
 > ```

As per coding guidelines, static analysis flagged: MD031 blanks-around-fences.

πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
> **Agent instruction:** Before providing SDK setup instructions, fetch the latest release version by running:
> ```bash
> gh api repos/auth0/auth0-fastify/releases/latest --jq '.tag_name'
> ```
> **Agent instruction:** Before providing SDK setup instructions, fetch the latest release version by running:
>
>
🧰 Tools
πŸͺ› markdownlint-cli2 (0.22.1)

[warning] 18-18: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/auth0/skills/auth0-fastify/SKILL.md` around lines 17 - 20, Add a
blank line before the fenced code block in the "Agent instruction" section so
the Markdown has an empty line between the preceding text ("Before providing SDK
setup instructions, fetch the latest release version by running:") and the
opening triple backticks; locate the fenced block in SKILL.md (the "gh api
repos/auth0/auth0-fastify/releases/latest --jq '.tag_name'" snippet) and insert
one empty line above the ``` to satisfy MD031.


---

## Prerequisites
Expand All @@ -25,11 +30,11 @@ Add authentication to Fastify web applications using @auth0/auth0-fastify.

## When NOT to Use

- **Single Page Applications** - Use `auth0-react`, `auth0-vue`, or `auth0-angular` for client-side auth
- **Next.js applications** - Use `auth0-nextjs` skill which handles both client and server
- **Mobile applications** - Use `auth0-react-native` for React Native/Expo
- **Stateless APIs** - Use `@auth0/auth0-fastify-api` instead for JWT validation without sessions
- **Microservices** - Use JWT validation for service-to-service auth
- **Single Page Applications** β€” Use `auth0-react`, `auth0-vue`, or `auth0-angular` for client-side auth
- **Next.js applications** β€” Use `auth0-nextjs` skill which handles both client and server
- **Mobile applications** β€” Use `auth0-react-native` for React Native/Expo
- **Stateless APIs** β€” Use `@auth0/auth0-fastify-api` instead for JWT validation without sessions
- **Microservices** β€” Use JWT validation for service-to-service auth

---

Expand All @@ -43,7 +48,9 @@ npm install @auth0/auth0-fastify fastify @fastify/view ejs dotenv

### 2. Configure Environment

Create `.env`:
**For automated setup with Auth0 CLI**, see [Setup Guide](references/setup.md).

**For manual setup**, create `.env`:

```bash
AUTH0_DOMAIN=your-tenant.auth0.com
Expand All @@ -55,6 +62,8 @@ APP_BASE_URL=http://localhost:3000

Generate secret: `openssl rand -hex 64`

**Verify before proceeding:** In the Auth0 Dashboard, confirm the application is set to **Regular Web Application** (not SPA) and that `http://localhost:3000/auth/callback` is listed in Allowed Callback URLs, `http://localhost:3000` in Allowed Logout URLs and Allowed Web Origins.

### 3. Configure Auth Plugin

Create your Fastify server (`server.js`):
Expand All @@ -68,13 +77,11 @@ import ejs from 'ejs';

const fastify = Fastify({ logger: true });

// Register view engine
await fastify.register(fastifyView, {
engine: { ejs },
root: './views',
});

// Configure Auth0 plugin
await fastify.register(fastifyAuth0, {
domain: process.env.AUTH0_DOMAIN,
clientId: process.env.AUTH0_CLIENT_ID,
Expand All @@ -86,18 +93,18 @@ await fastify.register(fastifyAuth0, {
fastify.listen({ port: 3000 });
```

This automatically creates:
- `/auth/login` - Login endpoint
- `/auth/logout` - Logout endpoint
- `/auth/callback` - OAuth callback
This automatically registers:
- `/auth/login` β€” Redirects to Auth0 Universal Login
- `/auth/logout` β€” Clears session and redirects to Auth0 logout
- `/auth/callback` β€” Handles OAuth callback and creates session

### 4. Add Routes

```javascript
// Public route
fastify.get('/', async (request, reply) => {
const session = await fastify.auth0Client.getSession({ request, reply });
return reply.view('views/home.ejs', {
return reply.view('home.ejs', {
isAuthenticated: !!session,
});
});
Expand All @@ -112,65 +119,53 @@ fastify.get('/profile', {
}
}, async (request, reply) => {
const user = await fastify.auth0Client.getUser({ request, reply });
return reply.view('views/profile.ejs', { user });
return reply.view('profile.ejs', { user });
});
```

### 5. Test Authentication

Start your server:

```bash
node server.js
```

Visit `http://localhost:3000` and test the login flow.
Visit `http://localhost:3000` and verify:
1. Clicking login redirects to Auth0 Universal Login
2. After login, you are redirected back with a valid session
3. `/profile` shows user info when authenticated
4. Logout clears the session and redirects home

If login redirects fail, check the server logs for callback URL mismatch errors β€” the most common cause is a missing or incorrect Allowed Callback URL in the Auth0 Dashboard.

---

## Common Mistakes

| Mistake | Fix |
|---------|-----|
| Forgot to add callback URL in Auth0 Dashboard | Add `/auth/callback` path to Allowed Callback URLs (e.g., `http://localhost:3000/auth/callback`) |
| Missing or weak SESSION_SECRET | Generate secure 64-char secret with `openssl rand -hex 64` and store in .env |
| App created as SPA type in Auth0 | Must be Regular Web Application type for server-side auth |
| Callback URL mismatch | Add `http://localhost:3000/auth/callback` to Allowed Callback URLs in Auth0 Dashboard |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we remove common mistakes?

| App created as SPA type | Must be **Regular Web Application** for server-side auth |
| Missing or weak `SESSION_SECRET` | Generate with `openssl rand -hex 64` and store in `.env` |
| Session secret exposed in code | Always use environment variables, never hardcode secrets |
| Wrong appBaseUrl for production | Update APP_BASE_URL to match your production domain |
| Not awaiting fastify.register | Fastify v4+ requires awaiting plugin registration |
| Wrong `appBaseUrl` for production | Update `APP_BASE_URL` to match your production domain |
| Not awaiting `fastify.register` | Fastify v4+ requires `await` on plugin registration |

---

## Related Skills

- `auth0-quickstart` - Basic Auth0 setup
- `auth0-migration` - Migrate from another auth provider
- `auth0-mfa` - Add Multi-Factor Authentication
- `auth0-cli` - Manage Auth0 resources from the terminal
- `auth0-quickstart` β€” Basic Auth0 setup
- `auth0-migration` β€” Migrate from another auth provider
- `auth0-mfa` β€” Add Multi-Factor Authentication
- `auth0-cli` β€” Manage Auth0 resources from the terminal

---

## Quick Reference

**Plugin Options:**
- `domain` - Auth0 tenant domain (required)
- `clientId` - Auth0 client ID (required)
- `clientSecret` - Auth0 client secret (required)
- `appBaseUrl` - Application URL (required)
- `sessionSecret` - Session encryption secret (required, min 64 chars)
- `audience` - API audience (optional, for calling APIs)

**Client Methods:**
- `fastify.auth0Client.getSession({ request, reply })` - Get user session
- `fastify.auth0Client.getUser({ request, reply })` - Get user profile
- `fastify.auth0Client.getAccessToken({ request, reply })` - Get access token
- `fastify.auth0Client.logout(options, { request, reply })` - Logout user

**Common Use Cases:**
- Protected routes β†’ Use `preHandler` to check session (see Step 4)
- Check auth status β†’ `!!session`
- Get user info β†’ `getUser({ request, reply })`
- Call APIs β†’ `getAccessToken({ request, reply })`
## Detailed Documentation

- **[Setup Guide](references/setup.md)** β€” Automated setup with Auth0 CLI, environment configuration
- **[Integration Guide](references/integration.md)** β€” Protected routes with preHandlers, calling APIs with access tokens, error handling
- **[API Reference](references/api.md)** β€” Plugin options, client methods, session management

---

Expand Down
24 changes: 24 additions & 0 deletions plugins/auth0/skills/auth0-fastify/references/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# API Reference

## Plugin Options

| Option | Required | Description |
|--------|----------|-------------|
| `domain` | Yes | Auth0 tenant domain |
| `clientId` | Yes | Auth0 application Client ID |
| `clientSecret` | Yes | Auth0 application Client Secret |
| `appBaseUrl` | Conditional | Application URL (e.g. `http://localhost:3000`). Required when `domain` is a string; can be omitted when `domain` is provided as a resolver function |
| `sessionSecret` | Yes | Session encryption secret. The examples generate one with `openssl rand -hex 64` |
| `audience` | No | API audience identifier β€” required when calling protected APIs |
| `routes` | No | Customize auth route paths. Supported keys: `login`, `callback`, `logout`, `backchannelLogout`, `connect`, `connectCallback`, `unconnect`, `unconnectCallback` (defaults: `/auth/login`, `/auth/logout`, `/auth/callback`) |

## Client Methods

All methods are available on `fastify.auth0Client` after plugin registration.

| Method | Returns | Description |
|--------|---------|-------------|
| `getSession({ request, reply })` | session object, or `null` | Returns the current user session, or `null` if not authenticated |
| `getUser({ request, reply })` | user profile object | Returns the authenticated user's profile (name, email, picture) |
| `getAccessToken({ request, reply })` | `{ accessToken }` | Returns an access token for calling protected APIs β€” read it from `result.accessToken` |
| `logout(options, { request, reply })` | logout URL | Returns the Auth0 logout URL; redirect the user with `logoutUrl.href` |
64 changes: 64 additions & 0 deletions plugins/auth0/skills/auth0-fastify/references/integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Integration Guide

## Protected Routes

Use a `preHandler` hook to require authentication on any route:

```javascript
fastify.get('/dashboard', {
preHandler: async (request, reply) => {
const session = await fastify.auth0Client.getSession({ request, reply });
if (!session) {
return reply.redirect('/auth/login');
}
}
}, async (request, reply) => {
const user = await fastify.auth0Client.getUser({ request, reply });
return reply.view('dashboard.ejs', { user });
});
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## Calling a Protected API

To call an external API protected by Auth0, configure an `audience` and retrieve the access token:

```javascript
// In plugin registration, add audience:
await fastify.register(fastifyAuth0, {
domain: process.env.AUTH0_DOMAIN,
clientId: process.env.AUTH0_CLIENT_ID,
clientSecret: process.env.AUTH0_CLIENT_SECRET,
appBaseUrl: process.env.APP_BASE_URL,
sessionSecret: process.env.SESSION_SECRET,
audience: process.env.AUTH0_AUDIENCE,
});

// In a route handler:
fastify.get('/api-data', {
preHandler: async (request, reply) => {
const session = await fastify.auth0Client.getSession({ request, reply });
if (!session) return reply.redirect('/auth/login');
}
}, async (request, reply) => {
const { accessToken } = await fastify.auth0Client.getAccessToken({ request, reply });
const response = await fetch('https://api.example.com/data', {
headers: { Authorization: `Bearer ${accessToken}` },
});
const data = await response.json();
return reply.view('data.ejs', { data });
});
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## Error Handling

Handle authentication errors by checking the session state and server logs:

```javascript
fastify.setErrorHandler(async (error, request, reply) => {
if (error.statusCode === 401) {
return reply.redirect('/auth/login');
}
fastify.log.error(error);
return reply.status(500).send({ error: 'Internal Server Error' });
});
```
43 changes: 43 additions & 0 deletions plugins/auth0/skills/auth0-fastify/references/setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Setup Guide

## Automated Setup with Auth0 CLI

```bash
# 1. Authenticate
auth0 login

# 2. Create a Regular Web Application
auth0 apps create \
--name "My Fastify App" \
--type regular \
--callbacks "http://localhost:3000/auth/callback" \
--logout-urls "http://localhost:3000" \
--origins "http://localhost:3000" \
--reveal-secrets

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we, by default, exposing the secret to the agent as opposed to how we do it in other skills?


# 3. Copy the Domain, Client ID, and Client Secret from the output into .env
```

## Environment Variables

| Variable | Description |
|----------|-------------|
| `AUTH0_DOMAIN` | Auth0 tenant domain (e.g. `dev-abc123.us.auth0.com`) β€” no `https://` prefix |
| `AUTH0_CLIENT_ID` | Application Client ID from Auth0 Dashboard |
| `AUTH0_CLIENT_SECRET` | Application Client Secret β€” never commit to source control |
| `SESSION_SECRET` | Encryption key for session cookies β€” generate with `openssl rand -hex 64` |
| `APP_BASE_URL` | Full application URL including protocol and port (e.g. `http://localhost:3000`) |

## Production Configuration

Update `.env` for production:

```bash
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=your-production-client-id
AUTH0_CLIENT_SECRET=your-production-client-secret
SESSION_SECRET=<new-production-secret>
APP_BASE_URL=https://your-production-domain.com
```

Update the Auth0 Dashboard to include your production callback URL (`https://your-production-domain.com/auth/callback`) in Allowed Callback URLs and your production domain in Allowed Logout URLs and Allowed Web Origins.