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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@quicore/credentials

v1.0.0

Published

Configurable, pluggable credential management module with support for multiple storage providers, encryption profiles, and authentication methods.

Readme

@quicore/credentials

A configurable, pluggable credential management module for Node.js. Supports multiple storage backends, encryption profiles, and authentication method types — all injectable and extensible.

Features

  • Modular architecture with full dependency injection
  • Multiple storage providers: File, MongoDB, S3, AWS KMS
  • Encryption profiles: abstract interface — bring your own implementation
  • Authentication methods: API Key, Bearer, OAuth2, Basic, AWS Signature, Custom
  • Separate body storage: credential metadata and secret bodies stored independently
  • Express HTTP controller with configurable routes
  • In-memory default store for development and testing
  • All layers independently extensible via subclassing

Installation

npm install @quicore/credentials

Express is an optional peer dependency, required only if using the HTTP controller:

npm install express

Storage provider SDKs are optional — install only what you use:

# MongoDB
npm install mongodb

# AWS S3
npm install @aws-sdk/client-s3

# AWS KMS
npm install @aws-sdk/client-kms

Quick Start

Minimal — In-Memory Store

import { CredentialModule } from '@quicore/credentials';

const module = new CredentialModule();
const service = module.getService();

await service.createCredential({
  credentialKey: 'my-api-key',
  type: 'ApiKey',
  body: {
    apiKey: 'secret-value'
  }
});

const credential = await service.getCredential('my-api-key');

With MongoDB Storage

import {
  CredentialModule,
  CredentialConfig,
  MongoCredentialBodyProvider
} from '@quicore/credentials';
import { MongoClient } from 'mongodb';

const mongo = new MongoClient(process.env.MONGO_URI);
await mongo.connect();

const provider = new MongoCredentialBodyProvider(mongo.db('app'), 'credential_bodies');

const config = new CredentialConfig({
  providers: [provider],
  defaultProvider: 'mongo'
});

const module = new CredentialModule({ config });
const service = module.getService();

With Express Routes

import express from 'express';
import { CredentialModule } from '@quicore/credentials';

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

const module = new CredentialModule();

for (const route of module.getRoutes()) {
  app[route.method](route.path, route.handler);
}

app.listen(3000);

Architecture

CredentialModule
    CredentialConfig          -- encryption profiles, providers, routes, pagination
    CredentialStore           -- metadata persistence (extend for production)
    CredentialBusinessService -- business logic, body routing
    CredentialController      -- Express HTTP layer (optional)

Body Storage Separation
    Credential collection     -- metadata only (body: null when provider is set)
    Provider                  -- actual credential secret

Providers
    FileCredentialBodyProvider    -- local filesystem
    MongoCredentialBodyProvider   -- MongoDB collection
    S3CredentialBodyProvider      -- AWS S3 bucket
    KMSCredentialBodyProvider     -- AWS KMS encrypted storage

Body Storage

When a provider is configured, credential bodies are stored only in the provider, not in the main credential collection. The credential record stores only metadata and a source reference pointing to the provider and location.

credential.source = {
  provider: 'mongo',
  location: 'credential_bodies',   // auto-populated by provider.getLocation()
  encryptionProfileRef: 'kms-prod',
  isEncrypted: true
}
credential.body = null              // not stored in collection

On fetch, the body is loaded from the provider and optionally decrypted before being returned.

If no provider is configured, the body is stored inline in the credential collection.

Configuration

import {
  CredentialConfig,
  EncryptionProfile,
  MongoCredentialBodyProvider,
  S3CredentialBodyProvider
} from '@quicore/credentials';

const config = new CredentialConfig({
  // Encryption profiles (bring your own implementation)
  encryptionProfiles: [
    new EncryptionProfile({
      name: 'kms-prod',
      algorithm: 'AES-256-GCM',
      keyReference: 'arn:aws:kms:us-east-1:123456789:key/abc-123',
      provider: 'kms'
    })
  ],
  defaultEncryptionProfile: 'kms-prod',

  // Body storage providers
  providers: [
    new MongoCredentialBodyProvider(mongoDb, 'credential_bodies'),
    new S3CredentialBodyProvider(s3Client, 'my-bucket', { prefix: 'credentials/' })
  ],
  defaultProvider: 'mongo',

  // HTTP routing
  routePrefix: '/api/v1/credentials',
  pageSize: 50,

  // Caching strategy
  cachingStrategy: 'credential', // 'api-call' | 'integration-id' | 'credential'

  // Restrict credential types
  allowedAuthMethods: ['ApiKey', 'Bearer', 'OAuth2']
});

CredentialModule

The module wires all components together and supports full dependency injection:

import {
  CredentialModule,
  CredentialConfig,
  CredentialStore,
  CredentialBusinessService,
  CredentialController
} from '@quicore/credentials';

const module = new CredentialModule({
  store: customStore,           // optional: must extend CredentialStore
  service: customService,       // optional: must extend CredentialBusinessService
  controller: customController, // optional: must extend CredentialController
  config: credentialConfig      // optional: must be instance of CredentialConfig
});

module.getStore();
module.getService();
module.getController();
module.getConfig();
module.getRoutes();             // returns Express route definitions

Extending the Store

The default CredentialStore is in-memory and intended for development only. For production, extend it to back onto a real database:

import { CredentialStore } from '@quicore/credentials';

export class MongoCredentialStore extends CredentialStore {
  constructor(db) {
    super();
    this.collection = db.collection('credentials');
  }

  async save(credential) {
    await this.collection.replaceOne(
      { credentialKey: credential.credentialKey },
      credential.toJSON(),
      { upsert: true }
    );
    return credential;
  }

  async findByKey(credentialKey) {
    const doc = await this.collection.findOne({ credentialKey });
    return doc ? Credential.fromJSON(doc) : null;
  }

  async findAll() {
    const docs = await this.collection.find({}).toArray();
    return docs.map(doc => Credential.fromJSON(doc));
  }

  async delete(credentialKey) {
    const result = await this.collection.deleteOne({ credentialKey });
    return result.deletedCount > 0;
  }

  async exists(credentialKey) {
    const count = await this.collection.countDocuments({ credentialKey });
    return count > 0;
  }
}

Extending the Business Service

Override specific methods to add custom logic:

import { CredentialBusinessService } from '@quicore/credentials';

export class AuditedCredentialService extends CredentialBusinessService {
  async createCredential(data) {
    const result = await super.createCredential(data);
    await auditLog.record('credential.created', result.data.credentialKey);
    return result;
  }

  async deleteCredential(credentialKey) {
    const result = await super.deleteCredential(credentialKey);
    await auditLog.record('credential.deleted', credentialKey);
    return result;
  }
}

Extending a Provider

Implement the CredentialBodyProvider interface to add a custom storage backend:

import { CredentialBodyProvider } from '@quicore/credentials';

export class VaultCredentialBodyProvider extends CredentialBodyProvider {
  constructor(vaultClient, mountPath) {
    super();
    this.name = 'vault';
    this.vaultClient = vaultClient;
    this.mountPath = mountPath;
  }

  async save(credentialKey, body) {
    await this.vaultClient.write(`${this.mountPath}/${credentialKey}`, { data: body });
  }

  async fetch(credentialKey) {
    const result = await this.vaultClient.read(`${this.mountPath}/${credentialKey}`);
    return result.data;
  }

  async delete(credentialKey) {
    await this.vaultClient.delete(`${this.mountPath}/${credentialKey}`);
  }

  getLocation() {
    return `vault://${this.mountPath}`;
  }

  getProviderType() {
    return 'vault';
  }

  validate() {
    if (!this.vaultClient) throw new Error('vaultClient is required');
    if (!this.mountPath) throw new Error('mountPath is required');
  }
}

Encryption Profiles

EncryptionProfile is a black-box interface. You provide the implementation via setEncryptionImplementation():

import { EncryptionProfile } from '@quicore/credentials';

const profile = new EncryptionProfile({
  name: 'aes-local',
  algorithm: 'AES-256-GCM',
  keyReference: './keys/local.key',
  provider: 'local'
});

profile.setEncryptionImplementation({
  async encrypt(plainText, keyRef, algorithm) {
    // your crypto logic here
    return encryptedBuffer;
  },
  async decrypt(cipherText, keyRef, algorithm) {
    // your crypto logic here
    return decryptedString;
  }
});

Authentication Methods

Each method validates and normalizes a credential body for its authentication type:

import { APIKeyMethod, BearerMethod, OAuth2Method } from '@quicore/credentials';

// API Key
const method = new APIKeyMethod({
  body: { apiKey: 'my-key' },
  headers: { 'X-Api-Key': ':{apiKey}' }
});
const validated = method.validate();

// OAuth2
const oauth = new OAuth2Method({
  body: {
    clientId: 'client-123',
    clientSecret: 'secret',
    tokenUrl: 'https://auth.example.com/token',
    scope: 'read write'
  }
});

Service API

const service = module.getService();

// Create
await service.createCredential({ credentialKey, type, body, ...opts });

// Read
await service.getCredential(credentialKey);

// Update
await service.updateCredential(credentialKey, { name, body, tags, ... });

// Delete
await service.deleteCredential(credentialKey);

// List with filters
await service.listCredentials({ tag, type, includeExpired });

// Search
await service.searchCredentials({ nameContains, types, tags, expiredOnly });

// Bulk
await service.getMultipleCredentials([key1, key2]);

// Status
await service.checkCredentialStatus(credentialKey);

// Export (sensitive fields redacted)
await service.exportCredentials([key1, key2]);

HTTP Routes (Express)

Default routes mounted by module.getRoutes():

| Method | Path | Description | |--------|------|-------------| | GET | /credentials | List all credentials | | POST | /credentials | Create a credential | | GET | /credentials/:credentialKey | Get a credential | | PATCH | /credentials/:credentialKey | Update a credential | | DELETE | /credentials/:credentialKey | Delete a credential | | GET | /credentials/search | Search credentials | | GET | /credentials/:credentialKey/status | Check expiry status | | POST | /credentials/export | Export (redacted) |

The route prefix and individual paths are configurable via CredentialConfig.

Credential Schema

{
  credentialKey: string,     // unique identifier, alphanumeric/hyphens/underscores
  type: string,              // 'ApiKey' | 'Bearer' | 'OAuth2' | 'Basic' | 'Custom'
  name: string,
  description: string,
  tags: string[],
  expiryAt: string | null,   // ISO 8601 date string
  cacheLevel: string,        // 'api-call' | 'integration-id' | 'credential'
  version: number,
  body: object | null,       // null when external provider is used
  headers: object | null,
  meta: object | null,
  source: {
    provider: string,        // provider name
    location: string,        // auto-populated from provider.getLocation()
    isEncrypted: boolean,
    encryptionProfileRef: string
  } | null
}

Token Set

TokenSet manages OAuth2 token state with built-in expiry checks and single-flight refresh:

import { TokenSet } from '@quicore/credentials';

const token = new TokenSet({
  access_token: 'abc123',
  expires_in: 3600,
  refresh_token: 'refresh-xyz'
});

token.expired();          // boolean
token.expiresIn();        // seconds remaining
token.isRefreshable();    // boolean

await token.refresh(async () => {
  // call your token refresh endpoint
  return new TokenSet(responseData);
});

Requirements

  • Node.js >= 18.0.0

Docs

Additional documentation is available in the docs/ directory:

  • docs/API_REFERENCE.md — full API surface
  • docs/QUICK_START.md — quick start guide
  • docs/CREDENTIAL_CONFIG_ARCHITECTURE.md — configuration architecture
  • docs/BODY_STORAGE_SEPARATION.md — body storage separation strategy
  • docs/PROVIDER_LOCATION_GUIDE.md — provider location reference
  • docs/MODULE_CONFIGURATION.md — module configuration options
  • docs/ARCHITECTURE_MIGRATION_GUIDE.md — migration from static implementations

License

MIT