-
Notifications
You must be signed in to change notification settings - Fork 23
feat: improve auth0-fastify skill score (81% β 93%) #113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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> | ||||||||||||||||
|
|
@@ -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' | ||||||||||||||||
| > ``` | ||||||||||||||||
|
Comment on lines
+17
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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
Suggested change
π§° 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 |
||||||||||||||||
|
|
||||||||||||||||
| --- | ||||||||||||||||
|
|
||||||||||||||||
| ## Prerequisites | ||||||||||||||||
|
|
@@ -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 | ||||||||||||||||
|
|
||||||||||||||||
| --- | ||||||||||||||||
|
|
||||||||||||||||
|
|
@@ -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 | ||||||||||||||||
|
|
@@ -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`): | ||||||||||||||||
|
|
@@ -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, | ||||||||||||||||
|
|
@@ -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, | ||||||||||||||||
| }); | ||||||||||||||||
| }); | ||||||||||||||||
|
|
@@ -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 | | ||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||||||||||||
|
|
||||||||||||||||
| --- | ||||||||||||||||
|
|
||||||||||||||||
|
|
||||||||||||||||
| 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` | |
| 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 }); | ||
| }); | ||
| ``` | ||
|
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 }); | ||
| }); | ||
| ``` | ||
|
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' }); | ||
| }); | ||
| ``` | ||
| 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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment.
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?