npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@tekcify/auth-backend

v2.0.4

Published

Backend authentication helpers for Tekcify Auth. Provides middleware, guards, and utilities for validating JWT tokens and protecting API routes in NestJS and Express applications.

Readme

@tekcify/auth-backend

Backend authentication helpers for Tekcify Auth. Provides middleware, guards, and utilities for validating JWT tokens and protecting API routes in NestJS and Express applications.

Installation

npm install @tekcify/auth-backend
# or
pnpm add @tekcify/auth-backend
# or
yarn add @tekcify/auth-backend

Features

  • NestJS Support - Guards and decorators for NestJS applications
  • Express Support - Middleware for Express applications
  • Token Verification - JWT token validation with HMAC/RS256
  • User Info Fetching - Helper to get user information from auth server
  • TypeScript Support - Full type definitions included

Quick Start

Prerequisites

You need the JWT access secret from your Tekcify Auth server. This should match the JWT_ACCESS_SECRET environment variable used by the auth server.

JWT_ACCESS_SECRET=your-jwt-access-secret-here

Note: The auth server URL is centralized in @tekcify/auth-core-client package as AUTH_SERVER_URL (default: http://localhost:7001, override with AUTH_SERVER_URL env). Functions that communicate with the auth server use this constant automatically.

Auth Server Endpoints Used

  • Base prefix: /api
  • User profile: GET/PUT /api/user/profile, POST /api/user/profile/picture
  • Applications: GET/POST /api/applications, GET /api/applications/public/:clientId, PUT/DELETE /api/applications/:clientId, POST /api/applications/:clientId/logo

NestJS Integration

Step 1: Install Dependencies

pnpm add @tekcify/auth-backend @nestjs/common @nestjs/core

Step 2: Create and Configure the Guard

Create a guard provider in your module:

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { JwtAuthGuard } from '@tekcify/auth-backend/nestjs';

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useFactory: () => {
        return new JwtAuthGuard({
          secret: process.env.JWT_ACCESS_SECRET!,
          issuer: 'tekcify-auth',
          audience: 'tekcify-api',
          getUserInfo: async (userId: string) => {
            // Optional: Fetch user info from your database
            // This is called only if getUserInfo is provided
            const user = await userRepository.findById(userId);
            return user ? { email: user.email } : null;
          },
        });
      },
    },
  ],
})
export class AppModule {}

Step 3: Use the Guard in Controllers

import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard, CurrentUser } from '@tekcify/auth-backend/nestjs';
import type { UserPayload } from '@tekcify/auth-backend/nestjs';

@Controller('api')
@UseGuards(JwtAuthGuard) // Protect entire controller
export class ApiController {
  @Get('profile')
  getProfile(@CurrentUser() user: UserPayload) {
    return {
      userId: user.userId,
      email: user.email,
      scopes: user.scopes,
    };
  }

  @Get('public')
  // This route is still protected by the controller-level guard
  getPublic() {
    return { message: 'Public endpoint' };
  }
}

Step 4: Per-Route Guard Usage

You can also use the guard on individual routes:

import { Controller, Get } from '@nestjs/common';
import { JwtAuthGuard, CurrentUser } from '@tekcify/auth-backend/nestjs';

@Controller('api')
export class ApiController {
  @Get('public')
  getPublic() {
    return { message: 'Public endpoint' };
  }

  @Get('protected')
  @UseGuards(new JwtAuthGuard({
    secret: process.env.JWT_ACCESS_SECRET!,
    issuer: 'tekcify-auth',
    audience: 'tekcify-api',
  }))
  getProtected(@CurrentUser() user: UserPayload) {
    return {
      message: 'Protected endpoint',
      user: user.userId,
    };
  }
}

Step 5: Access User Information

The @CurrentUser() decorator provides access to the authenticated user:

@Get('me')
@UseGuards(JwtAuthGuard)
getCurrentUser(@CurrentUser() user: UserPayload) {
  return {
    userId: user.userId,
    email: user.email,
    scopes: user.scopes || [],
  };
}

Express Integration

Step 1: Install Dependencies

pnpm add @tekcify/auth-backend express

Step 2: Create Auth Middleware

import express from 'express';
import { createAuthMiddleware } from '@tekcify/auth-backend/express';

const app = express();

const authMiddleware = createAuthMiddleware({
  secret: process.env.JWT_ACCESS_SECRET!,
  issuer: 'tekcify-auth',
  audience: 'tekcify-api',
  getUserInfo: async (userId: string) => {
    // Optional: Fetch user info from your database
    const user = await userRepository.findById(userId);
    return user ? { email: user.email } : null;
  },
});

app.use(express.json());

// Apply middleware to all /api routes
app.use('/api', authMiddleware);

app.get('/api/profile', (req, res) => {
  // req.user is now available
  res.json({
    userId: req.user!.userId,
    email: req.user!.email,
    scopes: req.user!.scopes,
  });
});

Step 3: Route-Level Middleware

You can also apply middleware to specific routes:

app.get('/api/public', (req, res) => {
  res.json({ message: 'Public endpoint' });
});

app.get('/api/protected', authMiddleware, (req, res) => {
  res.json({
    message: 'Protected endpoint',
    user: req.user!.userId,
  });
});

Step 4: TypeScript Support

The middleware extends Express's Request type:

import type { Request, Response } from 'express';

app.get('/api/user', authMiddleware, (req: Request, res: Response) => {
  // TypeScript knows req.user exists
  const userId = req.user!.userId;
  const email = req.user!.email;
  
  res.json({ userId, email });
});

Token Verification

For cases where you need to verify tokens directly (e.g., in background jobs, WebSocket connections):

import { verifyAccessToken } from '@tekcify/auth-backend';

const token = req.headers.authorization?.replace('Bearer ', '');

if (!token) {
  throw new Error('No token provided');
}

const result = verifyAccessToken(token, {
  secret: process.env.JWT_ACCESS_SECRET!,
  issuer: 'tekcify-auth',
  audience: 'tekcify-api',
});

if (!result.valid) {
  throw new Error('Invalid token');
}

console.log('User ID:', result.payload.sub);
console.log('Scopes:', result.payload.scopes);

Token Introspection

For cases where you can't verify tokens directly (e.g., different signing keys, remote verification):

import { introspectToken } from '@tekcify/auth-core-client';

const token = req.headers.authorization?.replace('Bearer ', '');

const result = await introspectToken({
  token: token!,
  clientId: process.env.CLIENT_ID,
  clientSecret: process.env.CLIENT_SECRET,
});

if (result.active) {
  console.log('Token is valid');
  console.log('User ID:', result.sub);
  console.log('Scopes:', result.scope);
} else {
  throw new Error('Token is invalid or expired');
}

Getting User Information

Fetch user information from the auth server:

import { fetchUserInfo } from '@tekcify/auth-backend';

const userInfo = await fetchUserInfo(accessToken);

console.log('Email:', userInfo.email);
console.log('Name:', userInfo.name);
console.log('Verified:', userInfo.email_verified);

User Profile Management

Manage user profiles with simple function calls:

Get User Profile

import { getUserProfile } from '@tekcify/auth-backend';

const profile = await getUserProfile(accessToken);

console.log('User ID:', profile.userId);
console.log('Email:', profile.email);
console.log('Name:', profile.firstName, profile.lastName);
console.log('Avatar:', profile.avatarUrl);

Update User Profile

import { updateUserProfile } from '@tekcify/auth-backend';

const updatedProfile = await updateUserProfile(accessToken, {
  firstName: 'John',
  lastName: 'Doe',
});

console.log('Profile updated:', updatedProfile);

Upload Profile Picture

import { uploadProfilePicture } from '@tekcify/auth-backend';

// Browser (with File object)
const fileInput = document.querySelector<HTMLInputElement>('#profilePic');
const file = fileInput?.files?.[0];

if (file) {
  const result = await uploadProfilePicture(accessToken, file);
  console.log('New avatar URL:', result.avatarUrl);
}

// Node.js (with Buffer)
import fs from 'fs';

const fileBuffer = fs.readFileSync('./avatar.jpg');
const result = await uploadProfilePicture(accessToken, fileBuffer, 'avatar.jpg');
console.log('New avatar URL:', result.avatarUrl);

Application Management

Manage OAuth applications with simple function calls:

List Applications

import { listApplications } from '@tekcify/auth-backend';

const apps = await listApplications(accessToken);

apps.forEach(app => {
  console.log('App:', app.name);
  console.log('Client ID:', app.clientId);
  console.log('Logo:', app.logoUrl);
});

Get Application by Client ID

import { getApplicationByClientId } from '@tekcify/auth-backend';

// Public endpoint - no auth required
const app = await getApplicationByClientId('your-client-id');

console.log('App Name:', app.name);
console.log('Scopes:', app.scopes);

Create Application

import { createApplication } from '@tekcify/auth-backend';

const newApp = await createApplication(accessToken, {
  name: 'My Cool App',
  description: 'An awesome application',
  redirectUris: ['https://myapp.com/callback'],
  authorizedOrigins: ['https://myapp.com'],
  scopes: ['read:profile', 'write:profile'],
});

console.log('Client ID:', newApp.clientId);
console.log('Client Secret:', newApp.clientSecret); // Save this securely!

Update Application

import { updateApplication } from '@tekcify/auth-backend';

await updateApplication(accessToken, 'your-client-id', {
  name: 'Updated App Name',
  description: 'New description',
});

console.log('Application updated');

Upload Application Logo

import { uploadApplicationLogo } from '@tekcify/auth-backend';

// Browser
const logoFile = document.querySelector<HTMLInputElement>('#logo')?.files?.[0];
if (logoFile) {
  const result = await uploadApplicationLogo(accessToken, 'your-client-id', logoFile);
  console.log('New logo URL:', result.logoUrl);
}

// Node.js
import fs from 'fs';

const logoBuffer = fs.readFileSync('./logo.png');
const result = await uploadApplicationLogo(
  accessToken,
  'your-client-id',
  logoBuffer,
  'logo.png'
);
console.log('New logo URL:', result.logoUrl);

Delete Application

import { deleteApplication } from '@tekcify/auth-backend';

await deleteApplication(accessToken, 'your-client-id');
console.log('Application deleted');

Complete NestJS Example

import { Module, Controller, Get, UseGuards } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { JwtAuthGuard, CurrentUser } from '@tekcify/auth-backend/nestjs';
import type { UserPayload } from '@tekcify/auth-backend/nestjs';

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useFactory: () => {
        return new JwtAuthGuard({
          secret: process.env.JWT_ACCESS_SECRET!,
          issuer: 'tekcify-auth',
          audience: 'tekcify-api',
        });
      },
    },
  ],
})
export class AppModule {}

@Controller('api')
@UseGuards(JwtAuthGuard)
export class ApiController {
  @Get('profile')
  getProfile(@CurrentUser() user: UserPayload) {
    return {
      userId: user.userId,
      email: user.email,
      scopes: user.scopes || [],
    };
  }

  @Get('posts')
  async getPosts(@CurrentUser() user: UserPayload) {
    // Only return posts for the authenticated user
    return await postRepository.findByUserId(user.userId);
  }
}

Complete Express Example

import express from 'express';
import { createAuthMiddleware, getUserProfile, updateUserProfile } from '@tekcify/auth-backend';

const app = express();
app.use(express.json());

const authMiddleware = createAuthMiddleware({
  secret: process.env.JWT_ACCESS_SECRET!,
  issuer: 'tekcify-auth',
  audience: 'tekcify-api',
});

// Public routes
app.get('/health', (req, res) => {
  res.json({ status: 'ok' });
});

// Protected routes
app.use('/api', authMiddleware);

app.get('/api/profile', async (req, res) => {
  try {
    const accessToken = req.headers.authorization?.replace('Bearer ', '');
    const profile = await getUserProfile(accessToken!);
    res.json(profile);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.put('/api/profile', async (req, res) => {
  try {
    const accessToken = req.headers.authorization?.replace('Bearer ', '');
    const updated = await updateUserProfile(accessToken!, req.body);
    res.json(updated);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Complete Integration Example

Here's a complete example showing authentication, profile management, and application management:

import {
  OAuthClient,
  generateCodeVerifier,
  generateCodeChallenge,
} from '@tekcify/auth-core-client';

import {
  getUserProfile,
  updateUserProfile,
  uploadProfilePicture,
  listApplications,
  createApplication,
  uploadApplicationLogo,
} from '@tekcify/auth-backend';

// Step 1: Authenticate user (using OAuth flow)
const oauthClient = new OAuthClient({
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  redirectUri: 'https://yourapp.com/callback',
  scopes: ['read:profile', 'write:profile'],
});

// Generate PKCE parameters
const verifier = generateCodeVerifier();
const challenge = await generateCodeChallenge(verifier, 'S256');

// Build auth URL and redirect user
const authUrl = await oauthClient.buildAuthorizeUrl({
  state: crypto.randomUUID(),
  codeChallenge: challenge,
  codeChallengeMethod: 'S256',
});

// After callback, exchange code for tokens
const tokens = await oauthClient.exchangeCode(code, verifier);
const accessToken = tokens.accessToken;

// Step 2: Manage user profile
const profile = await getUserProfile(accessToken);
console.log('Current profile:', profile);

// Update profile
const updated = await updateUserProfile(accessToken, {
  firstName: 'John',
  lastName: 'Doe',
});
console.log('Updated profile:', updated);

// Upload profile picture (browser)
const fileInput = document.querySelector<HTMLInputElement>('#profilePic');
if (fileInput?.files?.[0]) {
  const result = await uploadProfilePicture(accessToken, fileInput.files[0]);
  console.log('New avatar URL:', result.avatarUrl);
}

// Step 3: Manage applications
const apps = await listApplications(accessToken);
console.log('My applications:', apps);

// Create new application
const newApp = await createApplication(accessToken, {
  name: 'My New App',
  description: 'A cool application',
  redirectUris: ['https://myapp.com/callback'],
  scopes: ['read:profile'],
});
console.log('Created app:', newApp.clientId);
console.log('Client secret (save this!):', newApp.clientSecret);

// Upload application logo
const logoInput = document.querySelector<HTMLInputElement>('#logo');
if (logoInput?.files?.[0]) {
  const logoResult = await uploadApplicationLogo(
    accessToken,
    newApp.clientId,
    logoInput.files[0]
  );
  console.log('Logo uploaded:', logoResult.logoUrl);
}

API Reference

NestJS

JwtAuthGuard

Guard class for protecting routes.

new JwtAuthGuard({
  secret: string;                    // JWT secret
  issuer?: string;                  // Token issuer (default: 'tekcify-auth')
  audience?: string;                // Token audience (default: 'tekcify-api')
  getUserInfo?: (userId: string) => Promise<{ email: string } | null>;
})

@CurrentUser()

Parameter decorator to inject the current user.

@CurrentUser() user: UserPayload

Express

createAuthMiddleware(options)

Creates Express middleware for authentication.

createAuthMiddleware({
  secret: string;
  issuer?: string;
  audience?: string;
  getUserInfo?: (userId: string) => Promise<{ email: string } | null>;
})

User Profile Functions

getUserProfile(accessToken)

Get the authenticated user's profile.

getUserProfile(accessToken: string): Promise<UserProfile>

updateUserProfile(accessToken, data)

Update the user's profile information.

updateUserProfile(
  accessToken: string,
  data: UpdateProfileDto
): Promise<UserProfile>

uploadProfilePicture(accessToken, file, fileName?)

Upload a profile picture. Works with both File (browser) and Buffer (Node.js).

uploadProfilePicture(
  accessToken: string,
  file: File | Buffer,
  fileName?: string
): Promise<UploadResponse>

Application Management Functions

listApplications(accessToken)

Get all applications owned by the authenticated user.

listApplications(accessToken: string): Promise<Application[]>

getApplicationByClientId(clientId)

Get public application information (no auth required).

getApplicationByClientId(clientId: string): Promise<Application>

createApplication(accessToken, data)

Create a new OAuth application.

createApplication(
  accessToken: string,
  data: CreateApplicationDto
): Promise<CreateApplicationResponse>

updateApplication(accessToken, clientId, data)

Update an existing application.

updateApplication(
  accessToken: string,
  clientId: string,
  data: UpdateApplicationDto
): Promise<{ message: string }>

uploadApplicationLogo(accessToken, clientId, file, fileName?)

Upload an application logo. Works with both File (browser) and Buffer (Node.js).

uploadApplicationLogo(
  accessToken: string,
  clientId: string,
  file: File | Buffer,
  fileName?: string
): Promise<LogoUploadResponse>

deleteApplication(accessToken, clientId)

Delete an application.

deleteApplication(
  accessToken: string,
  clientId: string
): Promise<{ message: string }>

Utilities

verifyAccessToken(token, options)

Verifies a JWT access token.

verifyAccessToken(token: string, {
  secret: string;
  issuer?: string;
  audience?: string;
}): VerifiedToken

fetchUserInfo(accessToken)

Fetches user information from the Tekcify auth server using the centralized AUTH_SERVER_URL.

fetchUserInfo(accessToken: string): Promise<UserInfo>

Types

interface UserPayload {
  userId: string;
  email: string;
  scopes?: string[];
}

interface VerifiedToken {
  payload: TokenPayload;
  valid: boolean;
}

interface UserProfile {
  userId: string;
  email: string;
  firstName: string | null;
  lastName: string | null;
  avatarUrl: string | null;
}

interface UpdateProfileDto {
  firstName?: string;
  lastName?: string;
}

interface UploadResponse {
  avatarUrl: string;
}

interface Application {
  clientId: string;
  name: string;
  description: string | null;
  logoUrl: string | null;
  redirectUris: string[];
  authorizedOrigins: string[] | null;
  scopes: string[];
  createdAt: string;
  updatedAt: string;
}

interface CreateApplicationDto {
  name: string;
  description?: string;
  redirectUris: string[];
  authorizedOrigins?: string[];
  scopes: string[];
}

interface CreateApplicationResponse extends Application {
  clientSecret: string; // Only returned once!
}

interface UpdateApplicationDto {
  name?: string;
  description?: string;
  redirectUris?: string[];
  authorizedOrigins?: string[];
  scopes?: string[];
}

interface LogoUploadResponse {
  logoUrl: string;
}

Error Handling

All middleware and guards throw UnauthorizedException (NestJS) or return 401 status (Express) when:

  • Token is missing
  • Token is invalid
  • Token is expired
  • User not found (if getUserInfo is provided)

Security Best Practices

  1. Never expose JWT secrets - Keep secrets in environment variables
  2. Use HTTPS in production - Always use secure connections
  3. Validate token issuer and audience - Prevents token reuse across services
  4. Implement rate limiting - Protect against brute force attacks
  5. Log authentication failures - Monitor for suspicious activity
  6. Use short-lived tokens - Refresh tokens regularly

Troubleshooting

"Invalid token" errors

  • Verify JWT_ACCESS_SECRET matches the auth server
  • Check token hasn't expired
  • Ensure issuer and audience match

"User not found" errors

  • Verify getUserInfo function returns correct format
  • Check database connection
  • Ensure user exists in your system

Token verification fails

  • Verify token format (should start with "Bearer ")
  • Check token hasn't been tampered with
  • Ensure token type is "access" (not "refresh")

License

MIT