A comprehensive, highly reusable authentication library for React/Node/GraphQL applications.
All packages are published under the @appforgeapps scope on NPM:
# Install core authentication
npm install @appforgeapps/shieldforge-core @appforgeapps/shieldforge-types
# Install React integration
npm install @appforgeapps/shieldforge-react
# Install GraphQL support
npm install @appforgeapps/shieldforge-graphql
# Install Passkey/WebAuthn support (optional)
npm install @appforgeapps/shieldforge-passkey @appforgeapps/shieldforge-browserSee the complete example for a full implementation guide, or follow this quick start:
import { ShieldForge } from '@appforgeapps/shieldforge-core';
import { createResolvers, typeDefs } from '@appforgeapps/shieldforge-graphql';
// 1. Configure ShieldForge
const auth = new ShieldForge({
jwtSecret: process.env.JWT_SECRET!,
jwtExpiresIn: '7d',
saltRounds: 10,
});
// 2. Create GraphQL resolvers
const resolvers = createResolvers({
dataSource: {
getUserById: (id) => db.findUser(id),
getUserByEmail: (email) => db.findUserByEmail(email),
createUser: (data) => db.createUser(data),
// ... other database operations
},
auth: {
hashPassword: (pwd) => auth.hashPassword(pwd),
verifyPassword: (pwd, hash) => auth.verifyPassword(pwd, hash),
generateToken: (payload) => auth.generateToken(payload),
verifyToken: (token) => auth.verifyToken(token),
// ... other auth operations
},
});
// 3. Use with Apollo Server
const server = new ApolloServer({ typeDefs, resolvers });import { AuthProvider, useAuth, RequireAuth } from '@appforgeapps/shieldforge-react';
import { LOGIN_MUTATION } from '@appforgeapps/shieldforge-graphql';
// 1. Wrap your app with AuthProvider
function App() {
return (
<AuthProvider config={{ storageKey: 'auth.token', enableCrossTabSync: true }}>
<YourApp />
</AuthProvider>
);
}
// 2. Use the useAuth hook in components
function LoginForm() {
const { login } = useAuth();
const [loginMutation] = useMutation(LOGIN_MUTATION, {
onCompleted: (data) => login(data.login.token, data.login.user),
});
// ... form implementation
}
// 3. Protect routes with RequireAuth
function Dashboard() {
return (
<RequireAuth fallback={<LoginPage />}>
<ProtectedContent />
</RequireAuth>
);
}
// 4. Access auth state anywhere
function UserProfile() {
const { user, isAuthenticated, logout } = useAuth();
return <div>Welcome {user?.email}</div>;
}ShieldForge provides a complete authentication solution with modular packages that can be used together or independently. Built with TypeScript for type safety and designed for easy integration into any React/Node/GraphQL project.
- π Complete Authentication: JWT-based auth with password hashing and strength validation
- βοΈ React Integration: Hooks and components for seamless React integration
- π GraphQL Ready: Schema definitions and resolvers for GraphQL APIs
- π Passkey Support: WebAuthn/FIDO2 for passwordless authentication
- π§ Email Support: Built-in password reset email functionality
- π― Type-Safe: Full TypeScript support with exported types
- π¦ Modular: Use only what you need
- βοΈ Configurable: Flexible configuration for all settings
- β Well-Tested: 131 unit tests with 100% pass rate
ShieldForge is organized as a monorepo with the following packages, all published under @appforgeapps on NPM:
Shared TypeScript interfaces and types used across all packages.
npm install @appforgeapps/shieldforge-typesBackend authentication utilities for Node.js applications.
npm install @appforgeapps/shieldforge-coreFeatures:
- Password hashing with bcrypt
- JWT token generation & verification
- Password strength calculation
- User sanitization
- Reset code generation
- Email sending (nodemailer)
React hooks and components for authentication.
npm install @appforgeapps/shieldforge-reactFeatures:
AuthProvidercontext provideruseAuthhook for accessing auth stateRequireAuthcomponent for protected routeswithAuthHOC wrapper
GraphQL schema definitions and resolvers.
npm install @appforgeapps/shieldforge-graphqlFeatures:
- Complete authentication type definitions (SDL)
- Resolver factories with dependency injection
- Query/Mutation document exports for clients
- Composable schema fragments
WebAuthn/FIDO2 server-side utilities.
npm install @appforgeapps/shieldforge-passkeyFeatures:
- Registration options generation
- Registration verification
- Authentication options generation
- Authentication verification
- Challenge management with TTL
Client-side WebAuthn utilities.
npm install @appforgeapps/shieldforge-browserFeatures:
- Base64URL encoding/decoding
- WebAuthn options normalization
- Browser compatibility checks
- Registration and authentication flows
import { ShieldForge } from '@shieldforge/core';
// Initialize ShieldForge
const auth = new ShieldForge({
jwtSecret: process.env.JWT_SECRET!,
jwtExpiresIn: '7d',
saltRounds: 10,
smtp: {
host: process.env.SMTP_HOST!,
port: 587,
user: process.env.SMTP_USER!,
pass: process.env.SMTP_PASS!,
from: 'noreply@myapp.com'
}
});
// Hash a password
const passwordHash = await auth.hashPassword('mypassword');
// Verify a password
const isValid = await auth.verifyPassword('mypassword', passwordHash);
// Generate a JWT token
const token = auth.generateToken({
userId: user.id,
email: user.email
});
// Verify a token
const payload = auth.verifyToken(token);import { AuthProvider, useAuth, RequireAuth } from '@shieldforge/react';
// Wrap your app with AuthProvider
function App() {
return (
<AuthProvider
config={{
storageKey: 'myapp.token',
enableCrossTabSync: true,
}}
>
<Routes />
</AuthProvider>
);
}
// Use the auth hook in components
function Dashboard() {
const { user, isAuthenticated, logout } = useAuth();
if (!isAuthenticated) {
return <div>Please log in</div>;
}
return (
<div>
<h1>Welcome, {user.email}!</h1>
<button onClick={logout}>Logout</button>
</div>
);
}
// Protect routes with RequireAuth
function ProtectedRoute() {
return (
<RequireAuth fallback={<LoginPage />}>
<Dashboard />
</RequireAuth>
);
}import { createResolvers, typeDefs } from '@shieldforge/graphql';
import { ShieldForge } from '@shieldforge/core';
const auth = new ShieldForge({
jwtSecret: process.env.JWT_SECRET!,
});
// Create resolvers with your data source
const resolvers = createResolvers({
dataSource: {
getUserById: async (id) => { /* your DB logic */ },
getUserByEmail: async (email) => { /* your DB logic */ },
createUser: async (input) => { /* your DB logic */ },
updateUser: async (id, input) => { /* your DB logic */ },
createPasswordReset: async (userId, code, expiresAt) => { /* your DB logic */ },
getPasswordReset: async (code) => { /* your DB logic */ },
deletePasswordReset: async (code) => { /* your DB logic */ },
},
auth: {
hashPassword: (password) => auth.hashPassword(password),
verifyPassword: (password, hash) => auth.verifyPassword(password, hash),
generateToken: (payload) => auth.generateToken(payload),
verifyToken: (token) => auth.verifyToken(token),
calculatePasswordStrength: (password) => auth.calculatePasswordStrength(password),
sanitizeUser: (user) => auth.sanitizeUser(user),
generateResetCode: () => auth.generateResetCode(),
sendPasswordResetEmail: (to, code) => auth.sendPasswordResetEmail(to, code),
},
});
// Use with Apollo Server or any GraphQL server
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (token) {
try {
const payload = auth.verifyToken(token);
return { userId: payload.userId, token };
} catch (error) {
// Invalid token
}
}
return {};
},
});import { LOGIN_MUTATION, REGISTER_MUTATION, ME_QUERY } from '@shieldforge/graphql';
import { useMutation, useQuery } from '@apollo/client';
function LoginForm() {
const [login, { loading }] = useMutation(LOGIN_MUTATION);
const { login: authLogin } = useAuth();
const handleSubmit = async (email: string, password: string) => {
const { data } = await login({
variables: { input: { email, password } }
});
authLogin(data.login.token, data.login.user);
};
return (/* your form JSX */);
}import { PasskeyService } from '@shieldforge/passkey';
const passkeys = new PasskeyService({
rpName: 'My App',
rpId: 'myapp.com',
origin: 'https://myapp.com',
challengeTTL: 5 * 60 * 1000 // 5 minutes
});
// Generate registration options
const registrationOptions = await passkeys.generateRegistrationOptions({
id: user.id,
email: user.email,
name: user.name,
});
// Verify registration
const verification = await passkeys.verifyRegistration(
clientResponse,
expectedChallenge
);import { startRegistration, isWebAuthnSupported } from '@appforgeapps/shieldforge-browser';
async function registerPasskey() {
if (!isWebAuthnSupported()) {
alert('WebAuthn is not supported in this browser');
return;
}
// Get registration options from your server
const options = await fetch('/api/passkey/register-options').then(r => r.json());
// Start registration
const credential = await startRegistration(options);
// Send credential to server for verification
await fetch('/api/passkey/register', {
method: 'POST',
body: JSON.stringify(credential),
});
}interface ShieldForgeConfig {
jwtSecret: string; // Required: Secret for JWT signing
jwtExpiresIn?: string; // Optional: Token expiration (default: '7d')
saltRounds?: number; // Optional: bcrypt salt rounds (default: 10)
smtp?: { // Optional: SMTP configuration for emails
host: string;
port: number;
user: string;
pass: string;
from: string;
secure?: boolean;
};
}interface AuthProviderConfig {
storageKey?: string; // Token storage key (default: 'shieldforge.token')
pollInterval?: number; // Session polling interval in ms
enableCrossTabSync?: boolean; // Enable cross-tab sync (default: true)
initialToken?: string | null; // Initial token for SSR/testing
initialUser?: AuthUser | null; // Initial user for SSR/testing
}interface PasskeyConfig {
rpName: string; // Required: Relying party name
rpId: string; // Required: Relying party ID (domain)
origin: string; // Required: Origin URL
challengeTTL?: number; // Optional: Challenge TTL in ms (default: 5 minutes)
}Check out the complete example app for a full implementation guide with:
- Backend: Express + GraphQL server with ShieldForge
- Frontend: React app with authentication
- Detailed comments: Every file extensively documented
- Production-ready code: Copy and adapt to your needs
The example includes:
- Login and registration
- Password reset flow
- Protected routes
- Profile management
- WebAuthn/Passkey support
- Apollo Client integration
Each file in the example is a standalone tutorial showing exactly how to use ShieldForge in your application.
# Install dependencies
npm install
# Build all packages
npm run build
# Clean build artifacts
npm run clean
# Run tests
npm run testShieldForge includes comprehensive unit tests for all packages:
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with UI
npm run test:ui
# Run tests with coverage
npm run test:coverageTest Coverage:
- 131 total tests across 6 packages
- 100% pass rate
- Covers all core functionality, React components, GraphQL resolvers, and WebAuthn flows
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
This library is published to NPM under the @appforgeapps scope. The repository uses npm workspaces with internal package dependencies configured to use the wildcard (*) version, which automatically resolves to local workspace packages during development and publishes correctly to NPM.
All internal package dependencies use the wildcard version specifier:
{
"dependencies": {
"@appforgeapps/shieldforge-types": "*"
}
}This ensures that:
- During development, npm automatically resolves to the local workspace package
- During publishing, npm automatically converts to the actual published version
- During NPM registry lookup, no 404 errors occur when resolving unpublished versions
Important: When adding new internal package dependencies, always use "*" for workspace packages instead of version ranges like "^1.0.0".
The repository includes a GitHub Actions workflow (.github/workflows/publish.yml) that automatically publishes packages when a release is created:
- Create a GitHub release with a version tag (e.g.,
v1.0.1) - The workflow will:
- Validate the NPM_TOKEN is configured
- Install dependencies
- Update version numbers in all packages
- Build all packages
- Run tests
- Publish packages in dependency order (types β browser β core/graphql/passkey/react)
Prerequisites:
- The
NPM_TOKENsecret must be configured in GitHub repository settings - The token must have publish rights for the
@appforgeappsscope - Ensure the token is not expired
The workflow publishes packages in the correct dependency order:
@appforgeapps/shieldforge-types(no dependencies)@appforgeapps/shieldforge-browser(no internal dependencies)- All other packages that depend on types
To manually publish packages:
# Build all packages
npm run build
# Run tests
npm test
# Publish in dependency order using workspace commands
npm publish --workspace=@appforgeapps/shieldforge-types --access public
npm publish --workspace=@appforgeapps/shieldforge-browser --access public
npm publish --workspace=@appforgeapps/shieldforge-core --access public
npm publish --workspace=@appforgeapps/shieldforge-graphql --access public
npm publish --workspace=@appforgeapps/shieldforge-passkey --access public
npm publish --workspace=@appforgeapps/shieldforge-react --access publicNote: You need an NPM account with access to the @appforgeapps scope.
MIT Β© chriscase
- π Documentation
- π¦ NPM Packages
- π Issue Tracker
- π¬ Discussions
- π‘ Examples