Skip to content

n8n-io/n8n-dynamic-credentials-example

Repository files navigation

n8n Dynamic Credentials — Example Dashboard

A reference implementation showing how to build a web app that lets end-users connect their own OAuth credentials to n8n workflows, using the Dynamic Credentials feature.

Users log in via an identity provider (Okta in this example), see a list of workflows, connect the required credentials (e.g. Google Sheets), and run workflows — all from a single dashboard.

How it works

Browser                     Backend (Express)                n8n
  |                              |                             |
  |  1. GET /api/workflows       |                             |
  |<----- workflow list ---------|                             |
  |                              |                             |
  |  2. GET /api/workflows/:id/execution-status                |
  |  (user token)                |-- forward + API token ----->|
  |<----- credentials status ----|<-- readyToExecute, creds ---|
  |                              |   (URLs stripped,           |
  |                              |    canAuthorize/canRevoke)  |
  |                              |                             |
  |  3. POST /api/workflows/:wfId/credentials/:credId/authorize
  |  (user token)                |-- fetch execution-status -->|
  |                              |-- POST authorizationUrl --->|
  |<----- OAuth consent URL -----|<-- Google OAuth URL --------|
  |                              |                             |
  |  (user completes OAuth       |                             |
  |   in popup window)           |                             |
  |                              |                             |
  |  4. POST /api/workflows/:id/run                            |
  |  (user token)                |-- POST webhook endpoint --->|
  |<----- result ----------------|<-- execution result --------|

Key design decisions:

  • The n8n API token (N8N_DYNAMIC_CREDENTIALS_ENDPOINT_AUTH_TOKEN) never leaves the backend. The frontend only sends the user's identity token; the backend injects the API token when calling n8n.
  • n8n internal URLs are never exposed to the frontend. The backend strips authorizationUrl and revokeUrl from execution-status responses and replaces them with boolean flags (canAuthorize, canRevoke). Credential operations go through explicit backend endpoints.
  • Workflow webhook paths are server-side only. The frontend calls /api/workflows/:id/run; the backend resolves the actual webhook URL.

Project structure

src/
  backend/                  # Express server
    index.ts                # App setup, static file serving in production
    routes.ts               # API routes + workflow definitions
    tsconfig.json
  frontend/                 # React (Vite) SPA
    index.html
    vite.config.ts
    main.tsx                # Entry point + router
    App.tsx                 # Route definitions
    types.ts                # Shared TypeScript interfaces
    tsconfig.json
    auth/                   # Auth provider abstraction (Okta by default)
      AuthProvider.tsx      # Provider-specific context wrapper
      AuthCallback.tsx      # Provider-specific login callback
      useAuth.ts            # Provider-agnostic hook used by all components
    pages/
      Home.tsx              # Login page
      Workflows.tsx         # Main dashboard — data fetching + state
    components/
      Navbar.tsx            # Top nav with user info + logout
      WorkflowCard.tsx      # Single workflow card UI
      CredentialPill.tsx    # Credential status badge with actions
      ProtectedRoute.tsx    # Redirect to login if not authenticated
    styles/
      index.css             # Global styles + CSS variables
      App.css               # Workflow dashboard styles
      Home.css              # Login page styles

All Okta-specific code is isolated in src/frontend/auth/. The rest of the app only imports useAuth(), which exposes a provider-agnostic interface.

Prerequisites

  • Node.js >= 20
  • pnpm (or npm/yarn)
  • An n8n instance with dynamic credentials enabled
  • An Okta application (or adapt main.tsx to your identity provider)

Setup

  1. Clone the repo and install dependencies:
pnpm install
  1. Copy the example env file and fill in your values:
cp .env.example .env
Variable Description
N8N_URL URL of your n8n instance (e.g. http://localhost:5678)
N8N_DYNAMIC_CREDENTIALS_ENDPOINT_AUTH_TOKEN Token to authenticate with n8n's dynamic credentials endpoint (sent as X-Authorization header)
VITE_OKTA_CLIENT_ID Your Okta application's client ID
VITE_OKTA_DOMAIN Your Okta domain (e.g. dev-123456.okta.com)
VITE_OKTA_ISSUER Your Okta authorization server issuer URL
  1. Add your workflows in src/backend/routes.ts:
const workflows = [
  {
    id: "your-n8n-workflow-id",
    name: "Spreadsheet Generator",
    description: "Generates a new spreadsheet in your Google Drive",
    webhookPath: "your-webhook-uuid",
  },
];

The id is the n8n workflow ID. The webhookPath is the path portion of the workflow's webhook trigger URL.

Development

pnpm dev

This starts both:

  • Vite dev server on http://localhost:5173 (with HMR)
  • Express backend on http://localhost:3000 (with auto-reload via --watch)

Vite proxies /api requests to the Express backend automatically.

Production

pnpm build    # TypeScript check + Vite build
pnpm start    # Express serves the built frontend + API

The Express server serves the static frontend from dist/ and handles all /api routes. Single process, no separate web server needed.

Docker

docker build -t dynamic-credentials-example .
docker run -p 3000:3000 --env-file .env dynamic-credentials-example

Key files to look at

If you're building your own implementation, these are the files that matter most:

File Why it matters
src/backend/routes.ts Start here. Contains the workflow registry and all API route handlers. Shows how to call n8n's execution-status endpoint, how to strip internal URLs, and how to trigger the OAuth flow and revocation server-side.
src/backend/index.ts Minimal Express setup — how the n8n API token is kept server-side, and how the app is served in production.
src/frontend/auth/useAuth.ts The provider-agnostic auth hook. This is the interface your components should depend on — not the IdP SDK directly.
src/frontend/auth/AuthProvider.tsx The only file with Okta-specific imports. Replace this (and its siblings) to switch identity providers.
src/frontend/pages/Workflows.tsx The main dashboard. Shows the full data-fetching lifecycle: loading workflows, polling credential status, triggering authorization popups, and running workflows.
src/frontend/components/CredentialPill.tsx How to represent credential state (canAuthorize / canRevoke) in the UI and wire up the OAuth popup flow.
src/frontend/types.ts The TypeScript interfaces shared across frontend and backend responses. A good starting point to understand the data model.

Adapting to your identity provider

This example uses Okta, but the pattern works with any OpenID Connect provider. Auth is abstracted behind the useAuth() hook — you only need to change 3 files in src/frontend/auth/:

File What to change
AuthProvider.tsx Replace the Okta <Security> wrapper with your provider's context (e.g. Auth0's <Auth0Provider>)
AuthCallback.tsx Replace Okta's <LoginCallback> with your provider's callback handler
useAuth.ts Replace the useOktaAuth() calls with your provider's hook, keeping the same return type (isAuthenticated, isPending, userName, getAccessToken, signIn, signOut)

Then update the VITE_OKTA_* env vars in .env with your provider's config.

No changes needed in pages, components, or the backend — they all use the useAuth() hook and never reference Okta directly.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors