Nuxt Auth

A fairly complete solution to handle authentication for your Nuxt 3 project
Features
- ✔️ Email/password authentication
- ✔️ Email verification & password reset flows
- ✔️ Oauth login (Google, Github ...)
- ✔️ Route middleware protection
- ✔️ Database agnostic (Prisma based)
- ✔️ Auth operations via
useAuth composable
- ✔️ Auto refresh of access token via
useAuthFetch composable
- ✔️ Add dynamic custom claims to access token
- ✔️ Customizable email templates
- ✔️ User session management via
useAuthSession composable
- ✔️ Admin management via
useAuthAdmin composable
- ✔️ Edge deployment on Vercel, Netlify, Cloudflare ...
Installation
Add @bg-dev/nuxt-auth dependency to your project
npm install --save-dev @bg-dev/nuxt-auth
yarn add --dev @bg-dev/nuxt-auth
Setup
Add @bg-dev/nuxt-auth to the modules section of nuxt.config.ts and set auth config option
export default defineNuxtConfig({
modules: ["@bg-dev/nuxt-auth"],
auth: {
accessToken: {
jwtSecret: "",
maxAge: 30 * 60,
customClaims: {},
},
refreshToken: {
cookieName: "auth_refresh_token",
jwtSecret: "",
maxAge: 7 * 24 * 60 * 60,
},
oauth: {
"<provider>": {
clientId: "",
clientSecret: "",
scopes: "",
authorizeUrl: "",
tokenUrl: "",
userUrl: ""
}
},
smtp: {
host: "",
port: "",
user: "",
pass: "",
from: ""
},
emailTemplates: {
passwordReset: "",
emailVerify: ""
},
prisma: {},
registration: {
enable: true,
requireEmailVerification: true,
passwordValidationRegex: "",
defaultRole: "user"
},
baseUrl: "",
enableGlobalAuthMiddleware: false,
webhookKey: "",
redirect: {
login: "",
logout: "",
home: "",
callback: "",
passwordReset: "",
emailVerify: "",
},
admin: {
enable: true,
}
},.
});
Setup Prisma if not already set
npx prisma init
Copy the default schema definition to your prisma/schema.prisma file & optionally add your custom fields
SQL
model User {
id Int @id @default(autoincrement())
name String
email String @unique
picture String
role Role @default(user)
provider Provider @default(default)
password String?
verified Boolean @default(false)
suspended Boolean @default(false)
refreshTokens RefreshToken[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model RefreshToken {
id Int @id @default(autoincrement())
uid String
userId Int
user User @relation(fields: [userId], references: [id])
userAgent String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
}
enum Role {
user
admin
}
enum Provider {
default
google
}
Mongo DB
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String
email String @unique
picture String
role Role @default(user)
provider Provider @default(default)
password String?
verified Boolean @default(false)
suspended Boolean @default(false)
refreshTokens RefreshToken[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model RefreshToken {
id String @id @default(auto()) @map("_id") @db.ObjectId
uid String
userId String @db.ObjectId
user User @relation(fields: [userId], references: [id])
userAgent String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
}
enum Role {
user
admin
}
enum Provider {
default
google
}
Run a migration to reflect schema changes to your database & generate prisma client
SQL
npx prisma migrate dev
Mongo DB
npx prisma db push
That's it! You can now use @bg-dev/nuxt-auth in your Nuxt app ✨
Usage
Route protection
For protecting page routes, 2 possible approachs can be used
- Globally enable and locally disable
enableGlobalAuthMiddleware: true;
definePageMeta({ auth: false });
definePageMeta({ middleware: "auth" });
definePageMeta({ middleware: "guest" });
Custom claims
For adding custom claims to the access token's payload, set the customClaims accessToken's option in the nuxt.config.ts. For User related dynamic values, use the mustache syntax.
customClaims: {
"https://hasura.io/jwt/claims": {
"x-hasura-allowed-roles": ["user", "admin"],
"x-hasura-default-role": "{{role}}",
"x-hasura-user-id": "{{id}}",
},
},
Email templates
For adding your own email templates, set the emailTemplates options in nuxt.config.ts. Exposed variables are User, link for redirection and validityInMinutes (equals to accessToken maxAge).
emailTemplates: {
passwordReset: `
<html lang="en">
<head>
<style>
body {
background-color: #f1f5f9;
color: #0f172a;
font-family: "Arial";
padding: 8px;
}
</style>
</head>
<body>
<h2>Hello {{name}},</h2>
<p>To reset your password, please follow this link</p>
<a href="{{link}}">Reset your password</a>
<p>Valid for {{validityInMinutes}} minutes</p>
</body>
</html>
`,
}
Authorization
For adding a server side auth protection, create server/middleware/auth.ts and copy the handler below. On protected server routes check event.context.auth property.
import { getAccessTokenFromHeader, verifyAccessToken } from "#auth";
export default defineEventHandler((event) => {
try {
const accessToken = getAccessTokenFromHeader(event);
if (accessToken) {
const payload = verifyAccessToken(accessToken);
event.context.auth = payload;
}
} catch (error) {}
});
Graphql authorization
For data fetching with Graphql, you can use nuxt-apollo module. Add a Nuxt plugin to get into apollo:auth hook, get an auto refreshed access token via getAccessToken and set the client's token.
export default defineNuxtPlugin((nuxtApp) => {
const { getAccessToken } = useAuthSession();
nuxtApp.hook("apollo:auth", async ({ client, token }) => {
const accessToken = await getAccessToken();
token.value = accessToken || null;
});
});
Hooks
The module provides auth:loggedIn hook for implementing custom login on user login and logout events:
export default defineNuxtPlugin({
enforce: "pre",
hooks: {
"auth:loggedIn": async (loggedIn) => {},
},
});
Notes
-
The module implements a JWT based authentication. The Session abstract used in the module refers to a Refresh token stored in DB
-
An admin is a user with role equals admin.
-
On production, include prisma generate step to the build command.
-
For security reasons, it's recommended to add rate limiting and CORS policy.
-
The redirect URI for oauth providers should be
{baseUrl}/api/auth/login/{provider}/callback
- The sessions are subject to expiration in case the user does not refresh his login. To flush this useless data, the module exposes the following REST API
curl -X DELETE -H "Webhook-Key: {webhookKey}" {baseUrl}/api/auth/session/revoke/expired
Development
npm install
npm run dev:prepare
npm run dev
npm run dev:build
npm run lint
npm run test
npm run test:watch
npm run release
Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contribution you make is greatly appreciated.
License
MIT License