ciscodeapps-auth
Version:
A login library with local login, Microsoft Entra ID authentication, password reset, and roles/permissions for multi-tenant applications.
157 lines (128 loc) • 6.24 kB
Markdown
Auth Service (Express · JWT · Multi‑Tenant · RBAC)
Internal package — private to the company.
This package is not published on npmjs. Install it only from the company’s Azure Artifacts feed using a project or user-level .npmrc.
Authentication & authorization service for Node/Express apps.
Provides local email/password auth with lockout, JWT access tokens + refresh, tenant scoping, RBAC, and optional Microsoft Entra (Azure AD) OAuth.
Features
Local auth (email/password) with account lockout policy.
JWT access tokens (Bearer) and refresh endpoint (cookie or body).
Multi-tenant scope on requests.
RBAC (roles → permission strings).
Microsoft Entra (Azure AD) OAuth (optional).
MongoDB/Mongoose models.
Routes are mounted under:
/api/auth (auth, password reset)
/api/users (user admin)
/api/auth/roles and /api/auth/permissions (RBAC)
Installation (Azure Artifacts only)
1) Configure .npmrc
Do not commit real tokens. Prefer ~/.npmrc or generate .npmrc in CI using secrets.
For developers (user-level ~/.npmrc):
registry=https://registry.npmjs.org/
# Route @ciscodeapps scope to the private feed
@ciscodeapps:registry=https://pkgs.dev.azure.com/CISCODEAPPS/Templates/_packaging/testfeed/npm/registry/
//pkgs.dev.azure.com/CISCODEAPPS/Templates/_packaging/testfeed/npm/registry/:_authToken=${AZURE_ARTIFACTS_PAT}
always-auth=true
Set the token as an environment variable before installing:
export AZURE_ARTIFACTS_PAT="<your PAT with Packaging:Read>"
You can also use the username/_password form:
//pkgs.dev.azure.com/...:username=AzureDevOps
//pkgs.dev.azure.com/...:_password=${BASE64_PAT}
//pkgs.dev.azure.com/...:email=not-used@localhost
where BASE64_PAT=$(printf %s "$AZURE_ARTIFACTS_PAT" | base64).
2) Install the package
npm i @ciscodeapps/auth-service
3) Required environment variables (host app)
Create a .env in the host project:
# Server
PORT=3000
NODE_ENV=development
BASE_URL=http://localhost:3000
# Database (the service connects to this on startup)
MONGO_URI_T=mongodb://127.0.0.1:27017/auth_service
# JWT
JWT_SECRET=change_me
JWT_ACCESS_TOKEN_EXPIRES_IN=15m
JWT_REFRESH_SECRET=change_me_too
JWT_REFRESH_TOKEN_EXPIRES_IN=7d
# Lockout policy
MAX_FAILED_LOGIN_ATTEMPTS=5
ACCOUNT_LOCK_TIME_MINUTES=15
# (Optional) Microsoft Entra ID (Azure AD)
MICROSOFT_TENANT=organizations
MICROSOFT_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
MICROSOFT_CLIENT_SECRET=your-secret
MICROSOFT_CALLBACK_URL=${BASE_URL}/api/auth/microsoft/callback
Use inside an existing Express app
Your package exports an Express app that already parses JSON, connects to Mongo, and mounts its routes. Just mount it:
// server.js (host app)
require('dotenv').config();
const express = require('express');
const authApp = require('@ciscodeapps/auth-service');
const app = express();
app.use(authApp); // exposes /api/auth, /api/users, /api/auth/roles, /api/auth/permissions
app.get('/health', (_, res) => res.json({ ok: true }));
app.listen(process.env.PORT || 3000, () =>
console.log('Host app on', process.env.PORT || 3000)
);
Prefer mounting the service. If you need to run it standalone, you can also start this package directly (it calls connectDB() on import).
What’s included (routes & behavior)
Auth
POST /api/auth/register – Create a user (email/password).
POST /api/auth/login – Local login. On success, returns accessToken and may set a refreshToken httpOnly cookie.
POST /api/auth/verify-otp – If OTP/step-up is enabled, verifies the code and completes login.
POST /api/auth/refresh-token – New access token from a valid refresh token (cookie or body).
POST /api/auth/request-password-reset – Sends a reset token (e.g., by email).
PATCH /api/auth/reset-password – Consumes the reset token and sets a new password.
GET /api/auth/microsoft → GET /api/auth/microsoft/callback – Optional Microsoft Entra OAuth; issues first-party tokens.
Users
GET /api/users – List users (tenant-scoped, paginated).
POST /api/users – Create a user (requires permission).
Additional CRUD endpoints as exposed by controllers.
Roles & Permissions
GET/POST /api/auth/roles – Manage roles (name, tenantId, permissions: string[]).
GET /api/auth/permissions – List permission strings and metadata.
Protecting your own routes (host app)
const authenticate = require('@ciscodeapps/auth-service/src/middleware/authenticate'); // JWT
const { hasPermission } = require('@ciscodeapps/auth-service/src/middleware/rbac.middleware'); // RBAC
app.get('/reports',
authenticate,
hasPermission('reports:read'),
(req, res) => res.json({ ok: true })
);
Tenant scope comes from the JWT payload (e.g., tenantId) and is used inside controllers/guards to filter queries.
Quick start (smoke tests)
Start your host app:
node server.js
Register & Login
curl -X POST http://localhost:3000/api/auth/register \
-H 'Content-Type: application/json' \
-d '{"email":"a@b.com","password":"Secret123!","tenantId":"t-001","name":"Alice"}'
curl -X POST http://localhost:3000/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"a@b.com","password":"Secret123!","tenantId":"t-001"}'
# => { "accessToken": "...", "refreshToken": "..." }
Call a protected route
ACCESS=<paste_access_token_here>
curl http://localhost:3000/api/users -H "Authorization: Bearer $ACCESS"
Refresh token
curl -X POST http://localhost:3000/api/auth/refresh-token \
-H 'Content-Type: application/json' \
-d '{"refreshToken":"<paste_refresh_token_here>"}'
# => { "accessToken": "..." }
Microsoft OAuth (optional) - Visit: http://localhost:3000/api/auth/microsoft → complete sign-in.
- Callback: ${BASE_URL}/api/auth/microsoft/callback returns tokens (and may set the refresh cookie).
CI/CD (Azure Pipelines)
# azure-pipelines.yml (snippet)
- task: npmAuthenticate@0
inputs:
workingFile: .npmrc # optional; the task wires npm auth for subsequent steps
- script: npm ci
displayName: Install deps
(For GitHub Actions, write a ~/.npmrc with the token from secrets.AZURE_ARTIFACTS_PAT before npm ci.)
Security notes
Never commit real PATs. Use env vars or CI secrets.
Run behind HTTPS. Rotate JWT & refresh secrets periodically.
Limit login attempts; log auth events for auditing.
License
Internal — Company proprietary.