08:File Uploads#7
Conversation
📝 WalkthroughWalkthroughThe upload page now submits video metadata and files to ChangesVideo upload flow
Authentication and routing
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant UploadVideo
participant axios
participant UploadVideoAPI
User->>UploadVideo: Select video and enter metadata
UploadVideo->>UploadVideo: Validate file size and build FormData
UploadVideo->>axios: POST FormData to /api/upload-video
axios->>UploadVideoAPI: Submit multipart upload request
UploadVideoAPI-->>axios: Return response status
axios-->>UploadVideo: Resolve or reject request
Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with 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.
Inline comments:
In `@app/`(app)/uploadvideo/page.tsx:
- Around line 60-100: Update the title, description, and video-file controls in
the upload form to include unique IDs, and set each corresponding label’s
htmlFor to the matching ID so labels correctly associate with their inputs.
- Around line 42-47: Update the upload handler in the page component around the
response status check and catch block to store a user-visible status or error
message: set a success message when the response status is 200, and set a
failure message in the catch path while retaining the existing error handling.
Render the stored message in the UI so users can see whether the upload
succeeded or failed.
- Around line 26-29: Update the upload handler in the route’s request-processing
function to validate the received file size against the existing MAX_FILE_SIZE
limit before invoking Cloudinary. Reject oversized files with the appropriate
client-error response and do not proceed to upload; keep the client-side check
as-is.
In `@middleware.ts`:
- Line 11: Remove "/uploadvideo" from the isPublicRoutes collection in
middleware.ts so the route is handled as protected and authenticated users can
access the upload flow without being redirected to /home.
- Around line 47-64: Update the unauthenticated request handling around isApiReq
so non-public API requests reach the existing JSON 401 response before any
sign-in redirect. Exclude API requests from the page redirect condition, while
preserving redirects for unauthenticated non-public page routes and allowing
public routes unchanged.
- Around line 18-22: Update the isAdminRoute matcher to use a leading slash for
the admin API pattern, ensuring requests under /api/admin/... are matched while
preserving the existing admin route patterns.
- Around line 24-39: Update the clerkMiddleware callback to await the provided
auth helper before reading authentication state, then use the returned auth
context for the unauthenticated check. Replace the unsupported userId.role
comparison in the isAdminRoute branch with Clerk’s authorization check, such as
auth.has({ role: "admin" }), while preserving the existing redirects.
In `@utils/helpers.ts`:
- Around line 7-8: Update callers of requireUser, especially the upload-image
route handler, to await the helper before starting protected work so
authentication completes and failures remain inside the handler’s existing
try/catch. Audit all other requireUser call sites and add await wherever the
returned promise is currently used without awaiting.
- Line 10: Remove the console.log call that logs userId from the request
handling flow, leaving the surrounding behavior unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 16d898d9-562b-4d9e-9f4a-72308fef5924
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
app/(app)/uploadvideo/page.tsxmiddleware.tspackage.jsontypes/interfaces.tsutils/helpers.ts
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 9
🤖 Prompt for all review comments with 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.
Inline comments:
In `@app/`(app)/uploadvideo/page.tsx:
- Around line 60-100: Update the title, description, and video-file controls in
the upload form to include unique IDs, and set each corresponding label’s
htmlFor to the matching ID so labels correctly associate with their inputs.
- Around line 42-47: Update the upload handler in the page component around the
response status check and catch block to store a user-visible status or error
message: set a success message when the response status is 200, and set a
failure message in the catch path while retaining the existing error handling.
Render the stored message in the UI so users can see whether the upload
succeeded or failed.
- Around line 26-29: Update the upload handler in the route’s request-processing
function to validate the received file size against the existing MAX_FILE_SIZE
limit before invoking Cloudinary. Reject oversized files with the appropriate
client-error response and do not proceed to upload; keep the client-side check
as-is.
In `@middleware.ts`:
- Line 11: Remove "/uploadvideo" from the isPublicRoutes collection in
middleware.ts so the route is handled as protected and authenticated users can
access the upload flow without being redirected to /home.
- Around line 47-64: Update the unauthenticated request handling around isApiReq
so non-public API requests reach the existing JSON 401 response before any
sign-in redirect. Exclude API requests from the page redirect condition, while
preserving redirects for unauthenticated non-public page routes and allowing
public routes unchanged.
- Around line 18-22: Update the isAdminRoute matcher to use a leading slash for
the admin API pattern, ensuring requests under /api/admin/... are matched while
preserving the existing admin route patterns.
- Around line 24-39: Update the clerkMiddleware callback to await the provided
auth helper before reading authentication state, then use the returned auth
context for the unauthenticated check. Replace the unsupported userId.role
comparison in the isAdminRoute branch with Clerk’s authorization check, such as
auth.has({ role: "admin" }), while preserving the existing redirects.
In `@utils/helpers.ts`:
- Around line 7-8: Update callers of requireUser, especially the upload-image
route handler, to await the helper before starting protected work so
authentication completes and failures remain inside the handler’s existing
try/catch. Audit all other requireUser call sites and add await wherever the
returned promise is currently used without awaiting.
- Line 10: Remove the console.log call that logs userId from the request
handling flow, leaving the surrounding behavior unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 16d898d9-562b-4d9e-9f4a-72308fef5924
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
app/(app)/uploadvideo/page.tsxmiddleware.tspackage.jsontypes/interfaces.tsutils/helpers.ts
🛑 Comments failed to post (9)
app/(app)/uploadvideo/page.tsx (3)
26-29: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Enforce the file-size limit on the server.
This browser-only check is bypassable. The supplied
app/api/upload-video/route.tshandler sends the received file to Cloudinary without checking its size, allowing oversized authenticated uploads and their associated storage/processing cost. Reject files over the limit in the route handler before uploading.🤖 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 `@app/`(app)/uploadvideo/page.tsx around lines 26 - 29, Update the upload handler in the route’s request-processing function to validate the received file size against the existing MAX_FILE_SIZE limit before invoking Cloudinary. Reject oversized files with the appropriate client-error response and do not proceed to upload; keep the client-side check as-is.
42-47: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Show upload success and failure in the UI.
Failures are only logged to the console, so users cannot tell whether the upload completed and may retry unnecessarily. Store and render a status/error message.
🤖 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 `@app/`(app)/uploadvideo/page.tsx around lines 42 - 47, Update the upload handler in the page component around the response status check and catch block to store a user-visible status or error message: set a success message when the response status is 200, and set a failure message in the catch path while retaining the existing error handling. Render the stored message in the UI so users can see whether the upload succeeded or failed.
60-100: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Associate each visible label with its input.
These
<label>elements have nohtmlFor, while their controls have no IDs. Screen readers cannot reliably identify the fields, and clicking a label does not focus its control. Add matching IDs andhtmlForvalues for title, description, and video file.🤖 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 `@app/`(app)/uploadvideo/page.tsx around lines 60 - 100, Update the title, description, and video-file controls in the upload form to include unique IDs, and set each corresponding label’s htmlFor to the matching ID so labels correctly associate with their inputs.middleware.ts (4)
11-11: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Do not classify the upload page as public.
A signed-in user visiting
/uploadvideois redirected to/home, making the authenticated upload flow inaccessible. Remove/uploadvideofromisPublicRoutesso it is protected instead.Also applies to: 42-45
🤖 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 `@middleware.ts` at line 11, Remove "/uploadvideo" from the isPublicRoutes collection in middleware.ts so the route is handled as protected and authenticated users can access the upload flow without being redirected to /home.
18-22: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== files ==" git ls-files | rg '(^|/)middleware\.ts$|package\.json$|node_modules/next/dist/docs/' || true echo "== package versions ==" if [ -f package.json ]; then node -e "const p=require('./package.json'); console.log(JSON.stringify({dependencies:p.dependencies, devDependencies:p.devDependencies}, null, 2));" | rg -n 'next|clerk|typescript|node' -C 1 || true fi echo "== middleware ==" if [ -f middleware.ts ]; then nl -ba middleware.ts | sed -n '1,120p' fi echo "== clerk createRouteMatcher usages/docs ==" rg -n "createRouteMatcher|/api/admin|admin\\/|admin\\(" . || trueRepository: CodingWithTushar/learningsaas
Length of output: 773
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== middleware.ts lines ==" awk '{printf "%5d\t%s\n", NR, $0}' middleware.ts | sed -n '1,160p' echo "== package.json relevant deps ==" node - <<'JS' const p = require('./package.json'); console.log({ dependencies: p.dependencies && Object.fromEntries(Object.entries(p.dependencies).filter(([k]) => k === '`@clerk/nextjs`' || k === 'next')), devDependencies: p.devDependencies && Object.fromEntries(Object.entries(p.devDependencies).filter(([k]) => k === '`@types/react`' || k === 'typescript')) }); JS echo "== next docs available under node_modules ==" if [ -d node_modules/next ]; then find node_modules/next/dist/docs -maxdepth 3 -type f | head -100 || true rg -n "middleware|createRouteMatcher|route|pattern|matcher|api/admin|admin" node_modules/next/dist/docs || true else echo "node_modules/next not present" fi echo "== clerk createRouteMatcher package docs/readme snippets ==" if [ -d node_modules/@clerk/nextjs ]; then find node_modules/@clerk/nextjs -maxdepth 4 \( -name '*.md' -o -path '*dist*' -o -path '*README*' \) | head -100 || true rg -n "createRouteMatcher|api/admin|/api/admin|matcher|route" node_modules/@clerk/nextjs -g '*.js' -g '*.ts' -g '*.md' || true else echo "node_modules/@clerk/nextjs not present" fi echo "== search for admin routes/usages ==" rg -n "createRouteMatcher|routeMatcher|/api/admin|api/admin|/admin|admin/" . || trueRepository: CodingWithTushar/learningsaas
Length of output: 3662
🌐 Web query:
@clerk/nextjs createRouteMatcher route pattern forward slash api/admin💡 Result:
As of July 22, 2026, the
createRouteMatcher()helper in@clerk/nextjsis deprecated [1][2][3]. Clerk now recommends protecting resources directly (e.g., within Pages, Layouts, or API Route handlers) rather than relying on Middleware-based path matching [2][3]. If you are currently usingcreateRouteMatcherand need to match the/api/adminroute and its sub-paths, the following patterns are supported [4]: 1. To match/api/adminand everything under it:'/api/admin(.*)'[2] 2. Using the path segment syntax:'/api/admin/:path*'[1] Example usage (for legacy maintenance): import { createRouteMatcher } from '@clerk/nextjs/server' const isAdminRoute = createRouteMatcher(['/api/admin(.*)']) For new implementations or migrations, Clerk advises using resource-based authorization checks [2][3]. This involves invokingauth.protect()directly within your protected server-side resources instead of checking paths inmiddleware.ts[2][3]. For more details on migrating away fromcreateRouteMatcher, refer to the official Clerk migration guide [2].Citations:
- 1: https://clerk.com/docs/reference/nextjs/clerk-middleware
- 2: https://clerk.com/docs/guides/development/upgrading/upgrade-guides/migrate-from-create-route-matcher
- 3: clerk/javascript#8994
- 4: https://cdn.jsdelivr.net/npm/@clerk/nextjs@6.18.3/dist/types/server/routeMatcher.d.ts
🌐 Web query:
Clerk Next.js createRouteMatcher docs route patterns /api admin💡 Result:
As of July 2026, the
createRouteMatcherfunction is officially deprecated in the Clerk Next.js SDK [1][2]. It currently triggers a runtime deprecation warning, and Clerk recommends migrating toward resource-based authorization checks (such as usingauth().protect()directly within your API routes or Server Components) instead of relying on centralized middleware path matching [1][2]. If you are currently usingcreateRouteMatcheror maintaining existing implementations, the following patterns apply: Route Patterns and Syntax Clerk's route matchers support path patterns and regular expressions [3][4]. When defining paths, you can use the following conventions: - Use the:path*suffix for matching path-segment subtrees (e.g.,/admin/:path*matches/admin,/admin/users, and/admin/settings/123) [1][5]. - The legacy(.*)suffix (e.g.,/admin(.*)) is also supported but is generally discouraged in favor of the more precise:path*syntax [5]. - For API routes specifically, you would typically target them by prefixing the pattern, such as/api/admin/:path*[6]. Implementation Context In yourmiddleware.ts(orproxy.ts),createRouteMatcheris typically used to determine which routes are public or protected [7][8]. Example of protecting an admin API route: import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'; const isAdminRoute = createRouteMatcher(['/api/admin/:path*']); export default clerkMiddleware(async (auth, req) => { if (isAdminRoute(req)) { await auth.protect; // Enforces authentication for matched routes } }); export const config = { matcher: [ // Ensure the middleware runs for API routes '/(api|trpc)(.)', // Standard Next.js internal/static file exclusion '/((?!_next|[^?]\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', ], }; Critical Considerations - Middleware Limitations: Centralized middleware-based matching can diverge from how Next.js routes requests, potentially leaving some resources unintentionally exposed [2]. - Webhooks: Always ensure public API endpoints (like Stripe webhooks) are explicitly excluded from protection if they do not require user authentication [6]. - Migration: You are encouraged to review the official Clerk migration guide to transition your authentication logic to resource-based checks, which provide more granular security and better alignment with Next.js routing [1][2].Citations:
- 1: https://clerk.com/docs/reference/nextjs/clerk-middleware
- 2: clerk/javascript#8994
- 3: https://cdn.jsdelivr.net/npm/@clerk/nextjs@6.18.3/dist/types/server/routeMatcher.d.ts
- 4: https://cdn.jsdelivr.net/npm/@clerk/nextjs@6.12.6/dist/types/server/routeMatcher.d.ts
- 5: clerk/javascript#9057
- 6: https://dev.to/whoffagents/clerk-nextjs-app-router-production-auth-patterns-for-saas-4753
- 7: https://clerk.com/articles/add-clerk-authentication-to-a-next-js-app-with-the-clerk-cli
- 8: https://oboe.com/learn/mastering-authentication-with-clerk-and-nextjs-1plm1uk
Match the admin API path with a leading slash.
"api/admin(.*)"does not cover Next.js requests such as/api/admin/...; use a leading slash like"/api/admin(.*)"or the supported pattern"/api/admin/:path*".🤖 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 `@middleware.ts` around lines 18 - 22, Update the isAdminRoute matcher to use a leading slash for the admin API pattern, ensuring requests under /api/admin/... are matched while preserving the existing admin route patterns.
24-39: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== package files ==" git ls-files | rg '(^|/)(package\.json|pnpm-lock\.yaml|yarn\.lock|package-lock\.json)$' || true echo "== middleware.ts locations ==" fd -a 'middleware\.(ts|js|mjs)$' . | sed 's#^\./##' | head -50 echo "== middleware.ts relevant file ==" if [ -f middleware.ts ]; then nl -ba middleware.ts | sed -n '1,120p' fi echo "== clerk nextjs dependency ==" if [ -f package.json ]; then node -e "const p=require('./package.json'); console.log(JSON.stringify({dependencies:p.dependencies?.['`@clerk/nextjs`'], devDependencies:p.devDependencies?.['`@clerk/nextjs`'], next:p.dependencies?.next, devDependenciesNext:p.devDependencies?.next}, null, 2))" fi echo "== search auth(), userId.role, clerk middleware docs local ==" rg -n "auth\\(|userId\\.|role|clerkMiddleware|next/dist/docs" -S . --glob '!node_modules/**' --glob '!dist/**' | head -200Repository: CodingWithTushar/learningsaas
Length of output: 356
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== middleware.ts ==" awk '{printf "%6d\t%s\n", NR, $0}' middleware.ts | sed -n '1,140p' echo "== package deps ==" node -e "const fs=require('fs'); const p=JSON.parse(fs.readFileSync('package.json','utf8')); console.log(JSON.stringify({next:p.dependencies?.next, clerk:p.dependencies?.['`@clerk/nextjs`'], devNext:p.devDependencies?.next, devClerk:p.devDependencies?.['`@clerk/nextjs`']}, null, 2))" echo "== package-lock relevant package entries ==" node - <<'JS' const fs=require('fs'); const p=JSON.parse(fs.readFileSync('package-lock.json','utf8')); for (const [name,v] of Object.entries(p.packages || {}).filter(([n]) => n === '' || n === 'node_modules/@clerk/nextjs' || n === 'node_modules/next')) { console.log(v.version || name, v.dependencies && v.dependencies['node'], v.engines); } JS echo "== target code checks ==" python3 - <<'PY' from pathlib import Path s=Path('middleware.ts').read_text() print("auth destructure:", bool(("const { userId } = auth" in s) or "const { userId } = await auth" in s or "const { userId } = (await auth())" in s)) print("auth invoked:", bool("await auth({ " in s or "await auth()" in s or "auth({ " in s or "auth()" in s)) print("userId accesses:", [i+1 for i,l in enumerate(s.splitlines()) if 'userId' in l]) print("role access:", bool("userId.role" in s)) PY echo "== local Next docs availability ==" find node_modules/next -type f \( -path '*docs*' -o -name '*.js' -o -name '*.d.ts' \) -maxdepth 4 2>/dev/null | head -50 || trueRepository: CodingWithTushar/learningsaas
Length of output: 3254
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== all middleware.ts ==" find . -name 'middleware.ts' -type f -print echo "== TypeScript availability and Clerk auth typings from node_modules ==" if [ -d node_modules/@clerk/nextjs ]; then find node_modules/@clerk/nextjs -maxdepth 3 \( -name '*.d.ts' -o -name '*.d.mts' \) | xargs rg -n "declare function clerkMiddleware|clerkMiddleware\\(|auth\\??:|userId" | head -200 || true find node_modules/@clerk/nextjs -maxdepth 4 \( -name '*.d.ts' -o -name '*.d.mts' \) | xargs rg -n "createRouteMatcher|server" | head -100 || true else echo "node_modules/@clerk/nextjs not present" fi if command -v tsc >/dev/null 2>&1; then echo "== TypeScript compiler availability ==" tsc --version echo "== TypeScript check with minimal Clerk middleware type probe (no execution) ==" mkdir -p /tmp/clerk-auth-probe cat >/tmp/clerk-auth-probe/probe.ts <<'TS' import { clerkMiddleware, createRouteMatcher } from "@"__clerk_/nextjs/server"; export default clerkMiddleware((auth, req: Request) => { const { userId } = auth; if (userId && ("role" in userId && typeof userId.role === "string")) { void userId.role; } }); TS tsc --noEmit --skipLibCheck /tmp/clerk-auth-probe/probe.ts 2>&1 | sed -n '1,160p' else echo "tsc not available" fiRepository: CodingWithTushar/learningsaas
Length of output: 2478
🌐 Web query:
@clerk/nextjs middleware auth await auth() userId role clerkMiddleware user metadata role source💡 Result:
To retrieve user metadata and roles within your Next.js middleware using Clerk, you must first ensure that the custom metadata is included in the session token [1][2][3]. ### Step 1: Customize Session Token By default, Clerk does not include
publicMetadatain the session claims to keep the token size manageable [4]. To access this data in middleware without making additional API calls, you must customize the session token in the Clerk Dashboard: 1. Navigate to Sessions → Customize session token [1][5]. 2. Add a new claim (e.g.,metadata) that maps to your user's public metadata [1]:json { "metadata": "{{user.public_metadata}}" }### Step 2: Access in Middleware Once customized, you can access this data viasessionClaimsinside theclerkMiddlewarecallback [1][6]. You mustawaittheauth()function to retrieve the current authentication context [1][7][6].typescript // middleware.ts import { clerkMiddleware, createRouteMatcher } from '`@clerk/nextjs/server`' import { NextResponse } from 'next/server' const isAdminRoute = createRouteMatcher(['/admin(.*)']) export default clerkMiddleware(async (auth, req) => { // Await the auth() helper to get context const { sessionClaims } = await auth() // Safely access your custom metadata claim const role = sessionClaims?.metadata?.role // Example: Protect routes based on role if (isAdminRoute(req) && role !== 'admin') { return NextResponse.redirect(new URL('/', req.url)) } }) export const config = { matcher: [ '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', '/(api|trpc)(.*)', ], }### Key Considerations * Awaitauth(): Alwaysawaittheauth()function when calling it inside middleware or server-side components to resolve the promise [7]. * Metadata vs. Session Claims: UsingsessionClaimsis highly performant as it avoids extra network requests [1][2][3]. If you do not wish to add the claim to the token, you would have to fetch the user object via the Clerk API (clerkClient.users.getUser), which is significantly slower and subject to rate limits [4][1]. * Security:publicMetadatacan be read on the client but only modified on the server [2][6]. Always perform critical authorization checks on the server side [8][1].Citations:
- 1: https://stacknotice.com/blog/clerk-nextjs-authentication-guide-2026
- 2: https://clerk.com/docs/guides/secure/basic-rbac
- 3: https://clerk.com/blog/nextjs-role-based-access-control
- 4: https://clerk.com/blog/read-user-data-guide
- 5: https://stackoverflow.com/questions/79435996/cant-get-role-data-in-clerk-middleware
- 6: https://clerk.com/docs/guides/secure/basic-rbac.md
- 7: https://github.com/clerk/skills/blob/main/skills/frameworks/clerk-nextjs-patterns/SKILL.md
- 8: https://clerk.com/articles/complete-authentication-guide-for-nextjs-app-router
🌐 Web query:
Clerk docs role-based access control middleware has() auth() userId💡 Result:
In Clerk, role-based access control (RBAC) and general authorization are primarily handled using the auth helper, which returns an Auth object containing the current user's authentication and authorization state [1][2]. Key Components: 1. auth: This is the core server-side helper used to access user information and authorization tools [2][3]. When awaited, it returns an object containing the current userId, isAuthenticated status, and the has helper [4][1]. 2. userId: This property on the returned Auth object provides the ID of the currently signed-in user, which should be used to scope data or perform lookups [1][3]. 3. has: This is the recommended helper for performing authorization checks [4][5]. It accepts an object specifying the access control type—such as { role: '...' }, { permission: '...' }, { feature: '...' }, or { plan: '...' }—and returns a boolean value [4][1][5]. It is flexible and allows you to implement custom logic (e.g., returning a 403 error) when a check fails [4][5]. 4. auth.protect: For more concise route protection, you can use the protect method directly on the auth object [2][6]. It can enforce authorization by accepting the same parameters as has (e.g., auth.protect({ role: 'admin' })) [2][6]. If the user does not meet the requirements, it can automatically trigger a redirect or error [2]. Best Practices: - Authorization vs. Authentication: Use auth.userId to confirm a user is signed in (authenticated) and the has helper or auth.protect to verify their specific access rights (authorized) [4][2]. - Granularity: Clerk documentation recommends permission-based authorization over role-based authorization for greater flexibility and security [5]. - Location: While you can use auth.protect in middleware for fast-path enforcement, it is recommended to perform detailed authorization checks as close to the data source or business logic as possible (e.g., in Route Handlers or Server Actions) to ensure defense in depth [7][6]. - Limitations: When using has on the server side, note that it works with custom permissions; system permissions are not included in session token claims, so you should check for specific roles instead when dealing with system-level access [5].
Citations:
- 1: https://clerk.com/docs/reference/backend/types/auth-object
- 2: https://clerk.com/docs/reference/nextjs/app-router/auth
- 3: https://clerk.com/docs/reference/nextjs/app-router/route-handlers
- 4: https://clerk.com/docs/guides/secure/authorization-checks
- 5: https://github.com/clerk/clerk-docs/blob/126650ba/docs/guides/authorization-checks.mdx
- 6: https://github.com/clerk/skills/blob/main/skills/features/clerk-orgs/references/nextjs-patterns.md
- 7: https://clerk.com/docs/reference/nextjs/clerk-middleware
Invoke Clerk middleware auth and use Clerk authorization checks.
clerkMiddlewarereceives an asyncauthhelper;const { userId } = auth;leaves authentication unread. Awaitauth()before checking login state, and replaceuserId.rolewith Clerk’s supported authorization flow (auth.has({ role: "admin" })or claims/token-based role metadata). As written, protected admin routes can bypass auth checks or redirect users based on an invalid string property.🤖 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 `@middleware.ts` around lines 24 - 39, Update the clerkMiddleware callback to await the provided auth helper before reading authentication state, then use the returned auth context for the unauthenticated check. Replace the unsupported userId.role comparison in the isAdminRoute branch with Clerk’s authorization check, such as auth.has({ role: "admin" }), while preserving the existing redirects.
47-64: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Return 401 for API requests before redirecting pages.
For an unauthenticated non-public API request, Line 50 redirects first, so Lines 56-63 never run.
/api/upload-videotherefore returns a sign-in redirect instead of the declared JSON 401 response. Exclude API requests from the redirect condition or handleisApiReqfirst.Proposed fix
- if (!isPublicRoutes(req) && !isPublicApiRoutes(req)) { + if (!isApiReq && !isPublicRoutes(req)) {📝 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.//If the User is not LoggedIn if (!userId) { //ProtectedRoutes user can not access them without LoggedIn if (!isApiReq && !isPublicRoutes(req)) { const signinUrl = new URL("/sign-in", req.url); signinUrl.searchParams.set("redirect_url", currentUrl.pathname); return NextResponse.redirect(signinUrl); } if (isApiReq && !isPublicApiRoutes(req)) { return NextResponse.json( { error: "UnAuthorized", message: "You must be logged in", }, { status: 401 }, ); }🤖 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 `@middleware.ts` around lines 47 - 64, Update the unauthenticated request handling around isApiReq so non-public API requests reach the existing JSON 401 response before any sign-in redirect. Exclude API requests from the page redirect condition, while preserving redirects for unauthenticated non-public page routes and allowing public routes unchanged.utils/helpers.ts (2)
7-8: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Fix the unawaited
requireUser()caller.
app/api/upload-image/route.ts:9calls this helper withoutawait. Since authentication now suspends at Line 8, that handler starts the Cloudinary upload before authorization succeeds or rejects; the rejection also bypasses itstry/catch. Change that call toawait requireUser()and audit other callers.🤖 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 `@utils/helpers.ts` around lines 7 - 8, Update callers of requireUser, especially the upload-image route handler, to await the helper before starting protected work so authentication completes and failures remain inside the handler’s existing try/catch. Audit all other requireUser call sites and add await wherever the returned promise is currently used without awaiting.
10-10: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Remove user-ID logging.
This writes a stable user identifier to server logs on every request without an operational need.
🤖 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 `@utils/helpers.ts` at line 10, Remove the console.log call that logs userId from the request handling flow, leaving the surrounding behavior unchanged.
Add upload UI/components that let users choose and submit files.
Add server endpoint(s) to receive file uploads and perform validation (size, type).
Add storage handling (local storage / storage adapter) and safe filename handling.
Add tests exercising upload success and common failure cases.
Add documentation and an example showing how to use the uploads feature.