āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā š shadcn/directory/clerk/clerk-docs/reference/nuxt/clerk-middleware ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
The clerkMiddleware() helper allows you to protect your Nuxt application on the server-side. It can be used to validate a user's authentication status or authorization status.
[!WARNING]
clerkMiddleware()should be used to protect API routes only. It's not recommended to useclerkMiddleware()to protect pages as it will only work on initial page reload. On subsequent navigations, it won't be triggered because client-side navigation will bypass the middleware. To protect pages, see the dedicated guide.
clerkMiddleware()By default, the Nuxt SDK automatically adds the clerkMiddleware() helper to your Nuxt application.
To manually configure the middleware:
In your nuxt.config.ts file, under the clerk property, set skipServerMiddleware: true.
export default defineNuxtConfig({
modules: ['@clerk/nuxt'],
clerk: {
skipServerMiddleware: true,
},
})
In your server/middleware/ directory, create a file named clerk.ts with the following code:
import { clerkMiddleware } from '@clerk/nuxt/server'
export default clerkMiddleware()
You can protect API routes using either or both of the following:
You can also protect multiple routes using the createRouteMatcher() helper.
To protect routes based on user authentication status, you can check if the user is signed in by checking the isAuthenticated property on the auth object.
In the following example, the clerkMiddleware() helper checks if the user is signed in and accessing a protected route. If they aren't signed in, an error is thrown using Nuxt's createError() utility.
import { clerkMiddleware } from '@clerk/nuxt/server'
export default clerkMiddleware((event) => {
const { isAuthenticated } = event.context.auth()
const isAdminRoute = event.path.startsWith('/api/admin')
if (!isAuthenticated && isAdminRoute) {
throw createError({
statusCode: 401,
statusMessage: 'Unauthorized: User not signed in',
})
}
})
To protect routes based on user authorization status, you can use the has() helper to check if the user has the required privileges, such as a role, permission, feature, or plan. The has() helper is available on the auth object.
In the following example, the clerkMiddleware() helper checks if the user is accessing a protected route. If so, it checks if the user has the required custom permission. If they don't, an error is thrown using Nuxt's createError() utility.
import { clerkMiddleware } from '@clerk/nuxt/server'
export default clerkMiddleware((event) => {
const { has } = event.context.auth()
const isInvoicesRoute = event.path.startsWith('/api/invoices')
const canCreateInvoices = has({
permission: 'org:invoices:create',
})
// Check if the user is accessing a protected route
if (isInvoicesRoute) {
// Check if the user has the required permission
if (!canCreateInvoices) {
throw createError({
statusCode: 403,
statusMessage: 'Unauthorized: Missing permission to create invoices',
})
}
}
})
[!WARNING] It's best practice to use permission-based authorization over role-based authorization, as it reduces complexity and increases security. Usually, complex role checks can be refactored with a single permission check.
In the following example, the clerkMiddleware() helper checks if the user is accessing a protected route. If so, it checks if the user has the required admin role. If they don't, an error is thrown using Nuxt's createError() utility.
import { clerkMiddleware } from '@clerk/nuxt/server'
export default clerkMiddleware((event) => {
const { has } = event.context.auth()
const isAdminRoute = event.path.startsWith('/api/admin')
const isAdmin = has({
role: 'org:admin',
})
// Check if the user is accessing a protected route
if (isAdminRoute) {
// Check if the user has the required role
if (!isAdmin) {
throw createError({
statusCode: 403,
statusMessage: 'Unauthorized: Admin access required',
})
}
}
})
You can protect multiple routes at once by using Clerk's createRouteMatcher() helper function. The createRouteMatcher() helper accepts an array of route patterns and checks if the route the user is trying to visit matches one of the patterns passed to it.
Let's take the first example from this guide and add the createRouteMatcher() helper. Instead of only checking /api/admin/** routes, the following example checks both /api/invoices/** and /api/admin/** routes.
+ import { clerkMiddleware, createRouteMatcher } from '@clerk/nuxt/server'
export default clerkMiddleware((event) => {
const { isAuthenticated } = event.context.auth()
- const isAdminRoute = event.path.startsWith('/api/admin')
+ const isProtectedRoute = createRouteMatcher(['/api/invoices(.*)', '/api/admin(.*)'])
// Check if the user is not signed in
// and is trying to access a protected route. If so, throw a 401 error.
if (!isAuthenticated && isProtectedRoute(event)) {
throw createError({
statusCode: 401,
statusMessage: 'Unauthorized: User not signed in',
})
}
})
Now, let's add authorization-based protection to the example so that you can see how to combine everything you've learned so far.
import { clerkMiddleware, createRouteMatcher } from '@clerk/nuxt/server'
export default clerkMiddleware((event) => {
const { isAuthenticated } = event.context.auth()
const isProtectedRoute = createRouteMatcher(['/api/invoices(.*)', '/api/admin(.*)'])
+ const canCreateInvoices = has({
+ permission: 'org:invoices:create',
+ })
// Check if the user is not signed in
// and is trying to access a protected route. If so, throw a 401 error.
if (!isAuthenticated && isProtectedRoute(event)) {
throw createError({
statusCode: 401,
statusMessage: 'Unauthorized: User not signed in',
})
}
+ // Check if the user doesn't have the required permission
+ // and is accessing a protected route. If so, throw a 403 error.
+ if (!canCreateInvoices && isProtectedRoute(event)) {
+ throw createError({
+ statusCode: 403,
+ statusMessage: 'Unauthorized: Missing permission to create invoices',
+ })
+ }
})
clerkMiddleware() optionsOrganizationSyncOptionsā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā