Skip to content

Repository files navigation

AuthKit (NestJS Auth Package)

A production-ready, comprehensive authentication/authorization kit for NestJS with local auth, OAuth (Google/Microsoft/Facebook), JWT tokens, RBAC, admin user management, email verification, and password reset.

Features

  • Local Authentication: Email + password registration & login
  • OAuth Providers:
    • Google (ID Token validation + Authorization Code exchange)
    • Microsoft (Entra ID with JWKS verification)
    • Facebook (App token validation)
    • Web redirect flow (Passport)
    • Mobile token/code exchange
  • JWT Management:
    • Access tokens (stateless, short-lived)
    • Refresh tokens (long-lived JWTs with automatic invalidation on password change)
    • Email verification tokens (JWT-based links)
    • Password reset tokens (JWT-based links)
  • Email Verification: Required before login
  • Password Reset: JWT-secured reset link
  • Admin User Management: Create, list, ban/unban, delete, assign roles
  • RBAC (Role-Based Access Control):
    • Roles linked to users
    • Permissions linked to roles
    • Roles automatically included in JWT payload (Ids)
    • Fine-grained access control
  • Host App Control: Package uses host app's Mongoose connection (no DB lock-in)

Install

npm i @ciscode/authentication-kit

Host App Setup

1. Environment Variables

# DatabaseMONGO_URI=mongodb://127.0.0.1:27017/app_db# JWT ConfigurationJWT_SECRET=your_super_secret_key_change_thisJWT_ACCESS_TOKEN_EXPIRES_IN=15mJWT_REFRESH_SECRET=your_refresh_secret_change_thisJWT_REFRESH_TOKEN_EXPIRES_IN=7dJWT_EMAIL_SECRET=your_email_secret_change_thisJWT_EMAIL_TOKEN_EXPIRES_IN=1dJWT_RESET_SECRET=your_reset_secret_change_thisJWT_RESET_TOKEN_EXPIRES_IN=1h# Email (SMTP)SMTP_HOST=smtp.gmail.comSMTP_PORT=587SMTP_USER=your-email@gmail.comSMTP_PASS=your-app-passwordSMTP_SECURE=falseFROM_EMAIL=noreply@yourapp.com# Frontend URL (for email links)FRONTEND_URL=http://localhost:3000# Google OAuthGOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.comGOOGLE_CLIENT_SECRET=your-google-client-secretGOOGLE_CALLBACK_URL=http://localhost:3000/api/auth/google/callback# Microsoft/Entra ID OAuthMICROSOFT_CLIENT_ID=your-microsoft-client-idMICROSOFT_CLIENT_SECRET=your-microsoft-client-secretMICROSOFT_CALLBACK_URL=http://localhost:3000/api/auth/microsoft/callbackMICROSOFT_TENANT_ID=common# Optional, defaults to 'common'# Facebook OAuthFB_CLIENT_ID=your-facebook-app-idFB_CLIENT_SECRET=your-facebook-app-secretFB_CALLBACK_URL=http://localhost:3000/api/auth/facebook/callback# EnvironmentNODE_ENV=development

2. Host app example

import{Module,OnModuleInit}from"@nestjs/common";import{MongooseModule}from"@nestjs/mongoose";import{AuthKitModule,SeedService}from"@ciscode/authentication-kit";
@Module({imports: [MongooseModule.forRoot(process.env.MONGO_URI),AuthKitModule],})exportclassAppModuleimplementsOnModuleInit{constructor(privatereadonlyseed: SeedService){}asynconModuleInit(){awaitthis.seed.seedDefaults();}}

NOTES:

The AuthKit, by default seeds database with default roles and permissions once the host app is bootstraped (logs are generated for info) Default user role, on first register, is 'user' ... Mongoose needs Id to do this relation (the app MUST seed db from the package before anything)

API Routes

Local Auth Routes

POST /api/auth/register | Register new user (public)
POST /api/auth/verify-email | Verify email with token (public)
POST /api/auth/resend-verification | Resend verification email (public)
POST /api/auth/login | Login with credentials (public)
POST /api/auth/refresh-token | Refresh access token (public)
POST /api/auth/forgot-password | Request password reset (public)
POST /api/auth/reset-password | Reset password with token (public)
GET /api/auth/me | Get current user profile (protected)
DELETE /api/auth/account | Delete own account (protected)

OAuth Routes - Mobile Exchange (Public)

Exchange OAuth provider tokens for app tokens:

POST /api/auth/oauth/google { idToken?: string, code?: string }
POST /api/auth/oauth/microsoft { idToken: string }
POST /api/auth/oauth/facebook { accessToken: string }

OAuth Routes - Web Redirect (Public)

Passport-based OAuth flow for web browsers:

GET /api/auth/google | Google OAuth
GET /api/auth/google/callback | Google Redirect (After login)
GET /api/auth/microsoft | Microsoft OAuth
GET /api/auth/microsoft/callback | Microsoft Redirect (After login)
GET /api/auth/facebook | Facebook OAuth
GET /api/auth/facebook/callback | Facebook Redirect (After login)

Admin Routes - Users (Protected with @Admin())

POST /api/admin/users |Create user
GET /api/admin/users?email=...&username=... |List users (with filters)
PATCH /api/admin/users/:id/ban |Ban user
PATCH /api/admin/users/:id/unban |Unban user
PATCH /api/admin/users/:id/roles |Update user roles
DELETE /api/admin/users/:id |Delete user

Admin Routes - Roles (Protected with @Admin())

POST /api/admin/roles Create role
GET /api/admin/roles List all roles
PUT /api/admin/roles/:id Update role name
PUT /api/admin/roles/:id/permissions Set role permissions
DELETE /api/admin/roles/:id Delete role

Admin Routes - Permissions (Protected with @Admin())

POST /api/admin/permissions Create permission
GET /api/admin/permissions List all permissions
PUT /api/admin/permissions/:id Update permission
DELETE /api/admin/permissions/:id Delete permission

Usage Examples

Register

Request:

POST /api/auth/registerContent-Type: application/json
{
"fullname": {
"fname": "Test",
"lname": "User"
},
"username": "custom-username",
"email": "user@example.com",
"password": "Pa$$word!",
"phoneNumber": "+1234567890",
"avatar": "https://example.com/avatar.jpg",
"jobTitle": "Software Engineer",
"company": "Ciscode"
}

Notes:

  • username is now optional. If not provided, it will be auto-generated as fname-lname (e.g., test-user)
  • jobTitle and company are optional profile fields
  • All other fields work as before

Response:

{
"id": "507f1f77bcf86cd799439011",
"email": "user@example.com"
}

Login

Request:

POST /api/auth/loginContent-Type: application/json
{
"email": "user@example.com",
"password": "Pa$$word!"
}

Response:

{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Note: refreshToken is also set in httpOnly cookie

Verify Email

Request:

POST /api/auth/verify-emailContent-Type: application/json
{
"token": "email-verification-token-from-email"
}

Response:

{
"ok": true
}

Refresh Token

Request (from body):

POST /api/auth/refresh-tokenContent-Type: application/json
{
"refreshToken": "refresh-token-value"
}

OR (from cookie - automatic):

POST /api/auth/refresh-tokenCookie: refreshToken=refresh-token-value

Response:

{
"accessToken": "new-access-token",
"refreshToken": "new-refresh-token"
}

OAuth Google (Mobile Exchange)

Request (with ID Token):

POST /api/auth/oauth/googleContent-Type: application/json
{
"idToken": "google-id-token-from-client"
}

OR (with Authorization Code):

POST /api/auth/oauth/googleContent-Type: application/json
{
"code": "authorization-code-from-google"
}

Response:

{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Get Current User Profile

Request:

GET /api/auth/meAuthorization: Bearer access-token

Response:

{
"ok": true,
"data": {
"_id": "507f1f77bcf86cd799439011",
"fullname": {
"fname": "Test",
"lname": "User"
},
"username": "test-user",
"email": "user@example.com",
"avatar": "https://example.com/avatar.jpg",
"phoneNumber": "+1234567890",
"jobTitle": "Software Engineer",
"company": "Ciscode",
"isVerified": true,
"isBanned": false,
"roles": [
{
"_id": "507f1f77bcf86cd799439012",
"name": "user",
"permissions": [
{
"_id": "507f1f77bcf86cd799439013",
"name": "read:profile"
}
]
}
],
"createdAt": "2026-01-28T10:00:00.000Z",
"updatedAt": "2026-01-28T10:00:00.000Z"
}
}

Note: Sensitive fields like password and passwordChangedAt are automatically excluded from the response.

Delete Account

Request:

DELETE /api/auth/accountAuthorization: Bearer access-token

Response:

{
"ok": true
}

Guards & Decorators

AuthenticateGuard: Protects routes that require authentication. (No Access if not authenticated) Admin Decorator: Restricts routes to admin users only. (No Access if not an admin)

JWT Token Structure

Access Token Payload

{
"sub": "user-id",
"roles": ["ids"],
"iat": 1672531200,
"exp": 1672531900
}

Refresh Token Payload

{
"sub": "user-id",
"purpose": "refresh",
"iat": 1672531200,
"exp": 1672617600
}

Security Note: Refresh tokens are automatically invalidated if user changes password. The passwordChangedAt timestamp is checked during token refresh.

Seeding

On app startup via onModuleInit(), the following are created:

Roles:

  • admin - Full permissions
  • user - No default permissions

Permissions:

  • users:manage - Create, list, ban, delete users
  • roles:manage - Create, list, update, delete roles
  • permissions:manage - Create, list, update, delete permissions

All permissions are assigned to the admin role.

User Model

{_id: ObjectId,fullname: {fname: string,lname: string},username: string(unique,3-30chars,auto-generatedasfname-lnameifnotprovided),email: string(unique,validated),phoneNumber?: string(unique,10-14digits),avatar?: string(default: 'default.jpg'),jobTitle?: string,company?: string,password: string(hashed,min6chars),roles: ObjectId[](referencesRole),isVerified: boolean(default: false),isBanned: boolean(default: false),passwordChangedAt: Date,createdAt: Date,updatedAt: Date}

Role Model

{_id: ObjectId,name: string(unique),permissions: ObjectId[](referencesPermission),createdAt: Date,updatedAt: Date}

Permission Model

{_id: ObjectId,name: string(unique),description?: string,createdAt: Date,updatedAt: Date}

Important Notes

  • Database: AuthKit does NOT manage MongoDB connection. Your host app must provide the connection via MongooseModule.forRoot().
  • Stateless: JWTs are stateless; refresh tokens are signed JWTs (not stored in DB).
  • Email Verification Required: Users cannot login until they verify their email.
  • Password Changes Invalidate Tokens: All refresh tokens become invalid immediately after password change.
  • OAuth Auto-Registration: Users logging in via OAuth are automatically created with verified status.
  • Cookie + Body Support: Refresh tokens can be passed via httpOnly cookies OR request body.
  • Admin Access: Routes under /api/admin/* require the admin role (enforced by @Admin() decorator).

Error Handling

The package throws errors with descriptive messages. Your host app should catch and format them appropriately:

try{awaitauthService.login(dto);}catch(error){// Possible errors:// "Invalid credentials."// "Account banned."// "Email not verified."// "User not found."// "JWT_SECRET is not set"// etc.}

Development

npm run build # Compile TypeScript + alias paths
npm run start # Run standalone (if applicable)
npm run test# Run tests (currently no tests defined)

License

MIT

Author

Ciscode


Version: 1.2.0

About

A unified AuthenticationPackage for NestJs apps, that handles the entire Authentication System including OAuth( facebook, google, microsoft, apple ...)

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages