@scryan7371/sdr-security
v0.1.7
Published
Reusable auth/security capability for API and app clients.
Readme
sdr-security
Reusable auth/security capability for API and app clients.
Surfaces
api: shared auth types and input validation helpers.app: typed client for auth endpoints.
API Integration
Use shared helpers/types in your API controllers/services where useful:
sanitizeEmailisValidEmailisStrongPasswordAuthResponse,RegisterResponse,SafeUsernotifyAdminsOnEmailVerifiednotifyUserOnAdminApproval
Nest Integration
Import the Nest surface from @scryan7371/sdr-security/nest.
import { Module } from "@nestjs/common";
import {
SecurityWorkflowsModule,
SECURITY_WORKFLOW_NOTIFIER,
} from "@scryan7371/sdr-security/nest";
import { EmailService } from "./notifications/email.service";
@Module({
imports: [
SecurityWorkflowsModule.forRoot({
notifierProvider: {
provide: SECURITY_WORKFLOW_NOTIFIER,
useFactory: (emailService: EmailService) => ({
sendAdminsUserEmailVerified: ({ adminEmails, user }) =>
emailService.sendEmailVerifiedNotificationToAdmins(
adminEmails,
user,
),
sendUserAccountApproved: ({ email }) =>
emailService.sendAccountApproved(email),
}),
inject: [EmailService],
},
}),
],
})
export class AppModule {}User Table Ownership Model
Consuming apps keep ownership of their own app_user table. sdr-security
stores security/auth state in its own tables and links them by user id.
- App-owned table:
app_user(at minimum:id,email, plus any app-specific columns)
sdr-securitytables:security_user(password hash, verified/approved/active flags)security_identity(provider links such as Google subject)security_role,security_user_rolerefresh_tokensecurity_password_reset_token
Link key:
security_* .user_id->app_user.id
This lets each app evolve its user schema independently while reusing the same security workflows, guards, controllers, and migrations.
Typical app query pattern is a join when you need security state:
SELECT u.id, u.email, su.is_active, su.admin_approved_at, su.email_verified_at
FROM app_user u
LEFT JOIN security_user su ON su.user_id = u.id
WHERE u.id = $1;Nest/TypeORM equivalent:
const row = await usersRepo
.createQueryBuilder("user")
.leftJoin("security_user", "securityUser", "securityUser.user_id = user.id")
.select("user.id", "id")
.addSelect("user.email", "email")
.addSelect("securityUser.is_active", "isActive")
.addSelect("securityUser.admin_approved_at", "adminApprovedAt")
.addSelect("securityUser.email_verified_at", "emailVerifiedAt")
.where("user.id = :id", { id: userId })
.getRawOne();Optional Swagger setup in consuming app:
import { setupSecuritySwagger } from "@scryan7371/sdr-security/nest";
setupSecuritySwagger(app); // default path: /docs/securityRoutes exposed by the shared controller:
POST /security/auth/registerPOST /security/auth/loginPOST /security/auth/forgot-passwordPOST /security/auth/reset-passwordGET /security/auth/verify-email?token=...POST /security/auth/change-password(JWT required)POST /security/auth/logout(JWT required)POST /security/auth/refreshGET /security/auth/me/roles(JWT required)POST /security/workflows/users/:id/email-verified- marks
email_verified_atand notifies admins.
- marks
PATCH /security/workflows/users/:id/admin-approvalwith{ approved: boolean }- updates
admin_approved_atand notifies user when approved (admin JWT required).
- updates
PATCH /security/workflows/users/:id/activewith{ active: boolean }(admin JWT required)GET /security/workflows/roles(admin JWT required)POST /security/workflows/roles(admin JWT required)DELETE /security/workflows/roles/:role(admin JWT required)GET /security/workflows/users/:id/roles(admin JWT required)PUT /security/workflows/users/:id/roles(admin JWT required)POST /security/workflows/users/:id/roleswith{ role: string }(admin JWT required)DELETE /security/workflows/users/:id/roles/:role(admin JWT required)
Shared notification workflows
Use these helpers to standardize notification behavior across apps while still keeping app-specific email sending in your own services.
import { api as sdrSecurity } from "@scryan7371/sdr-security";
await sdrSecurity.notifyAdminsOnEmailVerified({
user: {
id: user.id,
email: user.email,
},
listAdminEmails: () => usersService.listAdminEmails(),
notifyAdmins: ({ adminEmails, user }) =>
emailService.sendEmailVerifiedNotificationToAdmins(adminEmails, user),
});
await sdrSecurity.notifyUserOnAdminApproval({
approved: body.approved,
user: {
email: user.email,
},
notifyUser: ({ email }) => emailService.sendAccountApproved(email),
});App Integration
Create one client per app session and reuse it across screens:
import { app as sdrSecurity } from "@scryan7371/sdr-security";
const securityClient = sdrSecurity.createSecurityClient({
baseUrl,
getAccessToken: () => accessToken,
});Methods:
registerloginloginWithGooglerefreshrevokelogoutrequestEmailVerificationverifyEmailrequestPhoneVerificationverifyPhone
Publish (npmjs)
- Configure project-local npm auth (
.npmrc):
registry=https://registry.npmjs.org/
@scryan7371:registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=${NPM_TOKEN}- Set token, bump version, and publish:
export NPM_TOKEN=xxxx
npm version patch
npm publish --access public --registry=https://registry.npmjs.org --userconfig .npmrc- Push commit and tags:
git push
git push --tagsCI Publish (GitHub Actions)
Tag pushes like sdr-security-v* trigger .github/workflows/publish.yml.
Required repo secret:
NPM_TOKEN(npm granular token with read/write + bypass 2FA for automation).
Install
Install a pinned version:
npm install @scryan7371/[email protected]Database Integration Test
A sample Postgres integration test is included at:
src/integration/database.integration.test.ts
Run it with:
npm run test:dbConfiguration resolution order:
.env.test(if present).env.dev(if present)- existing process env
Supported env vars:
SECURITY_TEST_DATABASE_URL(preferred)- or
DB_HOST,DB_PORT,DB_USER,DB_PASSWORD,DB_NAME - optional fallback:
DATABASE_URL - optional debug:
SECURITY_TEST_KEEP_SCHEMA=true(do not drop schema after test run)SECURITY_TEST_SCHEMA=your_schema_name(use fixed schema name)
See .env.test.example for a template.
Release Script
You can automate version bump + tag + push with:
npm run release:patch
npm run release:minor
npm run release:majorWhat it does:
- Verifies clean git working tree
- Runs
npm test - Runs
npm run build - Bumps
package.json+package-lock.json - Commits as
chore(release): vX.Y.Z - Tags as
sdr-security-vX.Y.Z - Pushes commit and tag
This tag format triggers .github/workflows/publish.yml.
