Authentication
How authentication works in LinkDen using Cloudflare Access or Clerk to protect the admin panel and API routes.
Authentication
LinkDen supports two authentication providers for protecting the admin panel and API routes:
- Cloudflare Access (recommended for Cloudflare deployments)
- Clerk (recommended for Vercel, Railway, Docker, or other platforms)
You can use one or both. The API server checks them in order: Cloudflare Access first, then Clerk.
Cloudflare Access
Cloudflare Access is a Zero Trust identity service built into your Cloudflare account. It places an authentication layer in front of your Workers, so unauthenticated requests never reach your application.
How It Works
- When a user visits a protected route (e.g.,
/admin), Cloudflare intercepts the request before it reaches the Worker. - The user is shown a Cloudflare Access login page where they authenticate via their configured identity provider (email OTP, Google, GitHub, etc.).
- After successful authentication, Cloudflare sets a
CF-Access-JWT-Assertioncookie/header containing a signed JWT. - The LinkDen API server verifies this JWT against Cloudflare's public keys at
https://<team-domain>.cloudflareaccess.com/cdn-cgi/access/certs. - The user's email from the JWT payload is used as their user ID.
Setting Up Cloudflare Access
Step 1: Enable Cloudflare Zero Trust
- Go to the Cloudflare dashboard.
- Click Zero Trust in the left sidebar (or visit one.dash.cloudflare.com).
- If this is your first time, you will be asked to choose a team name. This becomes your team domain (e.g.,
mycompanyformycompany.cloudflareaccess.com). - Select the Free plan (sufficient for personal use — up to 50 users).
Step 2: Configure an Identity Provider
- In Zero Trust, go to Settings > Authentication.
- Under Login methods, click Add new.
- Choose your identity provider:
- One-time PIN (simplest — sends a code to your email, no external provider needed)
- Google (OAuth)
- GitHub (OAuth)
- Or any other supported provider
- Follow the setup instructions for your chosen provider.
For single-user setups, One-time PIN is the easiest option — it just sends a login code to your email.
Step 3: Create an Access Application for the Admin Panel
- In Zero Trust, go to Access > Applications.
- Click Add an application.
- Choose Self-hosted.
- Configure the application:
| Field | Value |
|---|---|
| Application name | LinkDen Admin |
| Session duration | 24 hours (or your preference) |
| Application domain | linkden-web.<your-subdomain>.workers.dev |
| Path | /admin |
- Click Next to configure an access policy.
Step 4: Create an Access Policy
- Set the Policy name to "Allow me" (or any name).
- Set Action to Allow.
- Add a rule to the Include section:
- Selector: Emails
- Value: your email address (e.g.,
you@example.com)
- Click Next, then Add application.
This ensures only your email address can access the admin panel. All other visitors are blocked by Cloudflare before the request reaches your Worker.
Step 5: (Optional) Protect the API Worker
If you want to protect the entire API server (not just the web admin), create a second Access application:
| Field | Value |
|---|---|
| Application name | LinkDen API |
| Application domain | linkden-api.<your-subdomain>.workers.dev |
| Path | Leave empty (protects all routes) |
Use the same access policy. However, note that the API has its own public routes (the public tRPC queries) that do not require authentication, so you may prefer to leave the API Worker unprotected by Access and rely on the API-level auth middleware instead.
Step 6: Set the Environment Variable
Set CF_ACCESS_TEAM_DOMAIN in your API Worker's wrangler.toml:
[vars]
CF_ACCESS_TEAM_DOMAIN = "your-team-name"Replace your-team-name with the team name you chose in Step 1 (e.g., mycompany). Do not include .cloudflareaccess.com.
Then redeploy:
pnpm cf:deployVerifying Cloudflare Access
- Open your web app URL in an incognito window.
- Navigate to
/admin. - You should see the Cloudflare Access login page (not the LinkDen admin panel).
- Authenticate with your configured identity provider.
- After login, you should be redirected to the admin panel.
Clerk
Clerk is a hosted authentication service with React components for sign-in, session management, and user management. It is recommended when deploying to platforms other than Cloudflare, or when you want a polished sign-in UI within the app.
How It Works
- The Next.js app uses Clerk's React SDK to show sign-in components at
/sign-in. - After authentication, Clerk provides a session token (JWT) that the frontend includes in API requests via the
Authorization: Bearer <token>header. - The LinkDen API server verifies the JWT using the Clerk secret key.
- The user's Clerk
sub(subject ID) is used as their user ID.
Setting Up Clerk
- Create an account at clerk.com.
- Create a new Clerk application.
- Copy the Publishable Key (
pk_live_...) and Secret Key (sk_live_...). - Set the environment variables:
# In your .env file
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_your-key
CLERK_SECRET_KEY=sk_live_your-keyFor Cloudflare Workers, set CLERK_SECRET_KEY as a secret on the API Worker:
cd apps/server
echo "sk_live_your-key" | wrangler secret put CLERK_SECRET_KEYSingle-User Lockdown with Clerk
Since LinkDen is a single-user application, restrict sign-ups:
- Go to the Clerk Dashboard.
- Navigate to User & Authentication > Restrictions.
- Enable Allowlist.
- Add your email address to the allowlist.
Without the allowlist, anyone who discovers your sign-up page could create an account.
Using Both Providers
You can configure both Cloudflare Access and Clerk simultaneously. The API server checks them in order:
- If
CF_ACCESS_TEAM_DOMAINis set and the request has aCF-Access-JWT-Assertionheader, Cloudflare Access is verified first. - If that fails (or is not configured), and
CLERK_SECRET_KEYis set, theAuthorization: Bearerheader is checked against Clerk. - If neither succeeds, the request is denied for protected routes.
This is useful as a fallback mechanism, but most deployments should use just one provider.
Environment Variables Summary
| Variable | Provider | Where |
|---|---|---|
CF_ACCESS_TEAM_DOMAIN | Cloudflare Access | API Worker wrangler.toml [vars] |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | Clerk | Web app .env (baked at build time) |
CLERK_SECRET_KEY | Clerk | API Worker secret |
Troubleshooting
Cloudflare Access login page does not appear
- Verify the Access Application is configured with the correct domain and path (
/admin). - Check that the Access Application is active (not paused) in the Zero Trust dashboard.
- Clear your browser cookies and try again in an incognito window.
API returns 401 Unauthorized
- Cloudflare Access: Ensure
CF_ACCESS_TEAM_DOMAINis set correctly (just the team name, not the full URL). - Clerk: Ensure
CLERK_SECRET_KEYmatches the key in the Clerk dashboard. - Try signing out and back in to refresh the session token.
Redirect loop on admin pages
- If using Clerk, verify
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYis set correctly. - If using Cloudflare Access, ensure the Access Application path is
/admin(not/admin/*).
Cannot sign up with Clerk
- Check the Clerk dashboard allowlist. If enabled, only listed emails can sign up.