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

@davidwells/cogneato

v0.6.0

Published

low level implementation lib for use with aws cognito srp login

Readme

@davidwells/cogneato

npm version License: MIT TypeScript

Low-level TypeScript helpers for AWS Cognito SRP login, refresh tokens, remembered devices, MFA challenges, sign-up, account operations, and identity-pool credential exchange.

npm install @davidwells/cogneato

TL;DR

The Problem: Cognito's SRP auth flow is secure, but wiring it by hand means implementing SRP math, InitiateAuth, challenge responses, MFA loops, device confirmation, refresh tokens, and user account operations against low-level AWS JSON APIs.

The Solution: Cogneato wraps those Cognito calls in small TypeScript functions while keeping the flow explicit. srpLogin() is an async generator, so your UI or service decides how to handle NEW_PASSWORD_REQUIRED, SOFTWARE_MFA_REQUIRED, SMS_MFA_REQUIRED, successful tokens, and errors.

Why Use Cogneato?

| Feature | What It Does | |---------|--------------| | SRP login generator | Starts USER_SRP_AUTH, responds to password verifier challenges, yields MFA/new-password steps, and returns tokens. | | Remembered devices | Confirms new devices, stores reusable device credentials, and can reuse known devices on later logins. | | MFA helpers | Supports software-token MFA, SMS MFA, software-token association, token verification, and user MFA preference updates. | | User operations | Wraps sign-up, confirmation, password reset, password change, attribute verification, sign-out, and token revocation. | | Identity pools | Calls Cognito Identity GetId and GetCredentialsForIdentity for identity-pool credentials. | | Runtime-light design | Uses direct Cognito JSON requests instead of requiring the full AWS SDK at runtime. |

Quick Example

import { srpLogin, refresh, changePassword } from "@davidwells/cogneato";

const login = srpLogin({
  region: "us-west-2",
  userPoolId: "us-west-2_abc123",
  clientId: "4exampleclientid",
  username: "[email protected]",
  password: "CorrectHorseBatteryStaple1!",
  device: undefined,
  autoConfirmDevice: true,
  autoRememberDevice: "remembered",
});

let step = await login.next();

if (step.value.code === "SOFTWARE_MFA_REQUIRED") {
  step = await login.next("123456");
}

if (step.value.code === "NEW_PASSWORD_REQUIRED") {
  step = await login.next("NewPassword1!");
}

if (step.value.code === "ERROR") {
  throw step.value.error;
}

const auth = step.value.response!;

await refresh({
  region: "us-west-2",
  clientId: "4exampleclientid",
  refreshToken: auth.tokens.refreshToken,
  deviceKey: auth.newDevice?.key,
});

await changePassword({
  region: "us-west-2",
  accessToken: auth.tokens.accessToken,
  previousPassword: "CorrectHorseBatteryStaple1!",
  proposedPassword: "NewPassword1!",
});

Design Philosophy

Keep Cognito Explicit

Cogneato does not hide Cognito behind a large session manager. It exposes the flow as functions and return values you can store, inspect, and test:

const step = await login.next();

switch (step.value.code) {
  case "SOFTWARE_MFA_REQUIRED":
  case "SMS_MFA_REQUIRED":
  case "NEW_PASSWORD_REQUIRED":
  case "TOKENS":
  case "ERROR":
    break;
}

Make Interactive Auth Natural

SRP login is not always a single request. Users may need to enter a new password, a TOTP code, or an SMS code. The async generator shape lets the caller provide those values when Cognito asks for them.

Store Only What You Need

When Cognito returns NewDeviceMetadata, Cogneato can confirm the device and return the reusable device key, group key, and generated device password:

const device = auth.newDevice && {
  key: auth.newDevice.key,
  groupKey: auth.newDevice.groupKey,
  password: auth.newDevice.password!,
};

Use Small Operation Wrappers

Account operations map closely to Cognito API names, so tests and callers can reason about the exact request being made:

await updateUserAttributes({
  region,
  accessToken,
  userAttributes: [{ Name: "custom:tenantId", Value: "tenant-123" }],
});

How Cogneato Compares

| Capability | Cogneato | AWS Amplify Auth | AWS SDK CognitoIdentityProvider | Hand-rolled Cognito calls | |------------|----------|------------------|----------------------------------|---------------------------| | SRP math included | Yes | Yes | No | You write it | | Async challenge flow | Explicit generator | Managed internally | Manual | Manual | | Remembered device support | Built in | Framework-managed | Manual | Manual | | Direct Cognito operations | Yes | Partly abstracted | Yes | Yes | | Runtime size/control | Small focused package | Larger framework | Larger SDK client | Depends on your code | | Hosted UI/OAuth abstraction | No | Yes | No | No | | Best fit | Custom auth flows needing precise control | App teams using Amplify conventions | Backend services already using AWS SDK | Specialized experiments |

Use Cogneato when:

  • You need Cognito SRP without adopting Amplify.
  • You want to own UI state for MFA, forced password changes, and device prompts.
  • You want small wrappers around Cognito user-pool and identity-pool operations.

Cogneato may not be ideal when:

  • You want Cognito Hosted UI, OAuth redirects, or social-provider flows.
  • Your app already standardizes on Amplify Auth.
  • You need admin-only Cognito APIs such as AdminCreateUser or AdminInitiateAuth.

Installation

npm

npm install @davidwells/cogneato

pnpm

pnpm add @davidwells/cogneato

yarn

yarn add @davidwells/cogneato

From Source

git clone https://github.com/DavidWells/saaslayer.git
cd saaslayer/packages/cogneato
npm install
npm run build

The package publishes CommonJS output and TypeScript declarations from dist/.

Quick Start

1. Configure your app client

Use a Cognito user-pool app client that supports USER_SRP_AUTH. Collect these values from your infrastructure outputs:

const cognito = {
  region: "us-west-2",
  userPoolId: "us-west-2_abc123",
  clientId: "4exampleclientid",
};

2. Run SRP login

import { srpLogin } from "@davidwells/cogneato";

const login = srpLogin({
  ...cognito,
  username: "[email protected]",
  password: "CorrectHorseBatteryStaple1!",
  device: undefined,
  autoConfirmDevice: true,
  autoRememberDevice: "remembered",
  clientMetadata: { source: "web" },
});

3. Handle challenge steps

let result = await login.next();

while (!result.done) {
  if (result.value.code === "SOFTWARE_MFA_REQUIRED") {
    result = await login.next(await promptForTotpCode());
    continue;
  }

  if (result.value.code === "SMS_MFA_REQUIRED") {
    console.log(`Code sent to ${result.value.hint}`);
    result = await login.next(await promptForSmsCode());
    continue;
  }

  if (result.value.code === "NEW_PASSWORD_REQUIRED") {
    result = await login.next(await promptForNewPassword());
    continue;
  }

  throw result.value.error;
}

if (result.value.code === "ERROR") {
  throw result.value.error;
}

const auth = result.value.response!;

4. Persist remembered-device metadata

if (auth.newDevice?.password) {
  await saveDeviceForUser(auth.username, {
    key: auth.newDevice.key,
    groupKey: auth.newDevice.groupKey,
    password: auth.newDevice.password,
  });
}

5. Reuse the device on the next login

const savedDevice = await loadDeviceForUser("[email protected]");

const login = srpLogin({
  ...cognito,
  username: "[email protected]",
  password: "CorrectHorseBatteryStaple1!",
  device: savedDevice,
  autoConfirmDevice: true,
  autoRememberDevice: "remembered",
});

API Reference

srpLogin(params)

Runs Cognito USER_SRP_AUTH as an async generator.

const login = srpLogin({
  region: "us-west-2",
  userPoolId: "us-west-2_abc123",
  clientId: "4exampleclientid",
  username: "[email protected]",
  password: "CorrectHorseBatteryStaple1!",
  device: undefined,
  autoConfirmDevice: true,
  autoRememberDevice: "remembered",
  clientMetadata: { source: "web" },
  debugTracing: false,
});

Generator step codes:

| Code | Meaning | Caller Response | |------|---------|-----------------| | TOKENS | Login completed. | Read step.value.response. | | SOFTWARE_MFA_REQUIRED | Cognito requires a six-digit TOTP code. | Call login.next("123456"). | | SMS_MFA_REQUIRED | Cognito requires a six-digit SMS code. | Call login.next("123456"). | | NEW_PASSWORD_REQUIRED | Cognito requires a password reset during login. | Call login.next("NewPassword1!"). | | ERROR | Login failed. | Read or throw step.value.error. |

Successful response shape:

type TAuthResponse = {
  username: string;
  tokens: {
    accessToken: string;
    refreshToken: string;
    idToken: string;
    tokenType: string;
    expiresIn: number;
  };
  newDevice?: {
    key: string;
    groupKey: string;
    password?: string;
    deviceAutoConfirmed: boolean;
    deviceAutoRemembered?: "remembered" | "not_remembered";
    userConfirmationNecessary?: boolean;
  };
};

refresh(params)

Refreshes access and ID tokens with a refresh token.

import { refresh } from "@davidwells/cogneato";

const tokens = await refresh({
  region: "us-west-2",
  clientId: "4exampleclientid",
  refreshToken: "refresh-token",
  deviceKey: "device-key",
});

Sign-Up and Confirmation

import {
  signUp,
  confirmSignUp,
  resendConfirmationCode,
} from "@davidwells/cogneato";

await signUp({
  region,
  clientId,
  username: "[email protected]",
  password: "S3cret!pass",
  userAttributes: [
    { Name: "email", Value: "[email protected]" },
    { Name: "custom:orgId", Value: "org-123" },
  ],
  clientMetadata: { source: "web" },
});

await confirmSignUp({
  region,
  clientId,
  username: "[email protected]",
  confirmationCode: "123456",
});

await resendConfirmationCode({
  region,
  clientId,
  username: "[email protected]",
});

Password Reset and Password Change

import {
  forgotPassword,
  confirmForgotPassword,
  changePassword,
} from "@davidwells/cogneato";

await forgotPassword({
  region,
  clientId,
  username: "[email protected]",
});

await confirmForgotPassword({
  region,
  clientId,
  username: "[email protected]",
  confirmationCode: "123456",
  password: "NewPassword1!",
});

await changePassword({
  region,
  accessToken,
  previousPassword: "OldPassword1!",
  proposedPassword: "NewPassword1!",
});

User Profile and Account Operations

import {
  getUser,
  updateUserAttributes,
  getUserAttributeVerificationCode,
  verifyUserAttribute,
  globalSignOut,
  revokeToken,
} from "@davidwells/cogneato";

const user = await getUser({ region, accessToken });

await updateUserAttributes({
  region,
  accessToken,
  userAttributes: [{ Name: "email", Value: "[email protected]" }],
  clientMetadata: { source: "settings" },
});

await getUserAttributeVerificationCode({
  region,
  accessToken,
  attributeName: "email",
});

await verifyUserAttribute({
  region,
  accessToken,
  attributeName: "email",
  code: "123456",
});

await globalSignOut({ region, accessToken });

await revokeToken({
  region,
  clientId,
  token: refreshToken,
  clientSecret: "optional-client-secret",
});

Software Token MFA

import {
  associateSoftwareToken,
  verifySoftwareToken,
  setUserMfaPreference,
} from "@davidwells/cogneato";

const association = await associateSoftwareToken({
  region,
  accessToken,
});

await verifySoftwareToken({
  region,
  accessToken,
  userCode: "123456",
  friendlyDeviceName: "Work phone",
});

await setUserMfaPreference({
  region,
  accessToken,
  softwareTokenMfaSettings: { Enabled: true, PreferredMfa: true },
  smsMfaSettings: { Enabled: false, PreferredMfa: false },
  emailMfaSettings: { Enabled: false, PreferredMfa: false },
  webAuthnMfaSettings: { Enabled: false },
});

associateSoftwareToken() and verifySoftwareToken() also accept a challenge session instead of an accessToken when Cognito returns one during auth setup.

Identity Pool Credentials

import {
  getIdentityId,
  getCredentialsForIdentity,
} from "@davidwells/cogneato";

const { IdentityId } = await getIdentityId({
  region,
  identityPoolId: "us-west-2:identity-pool-id",
  logins: {
    "cognito-idp.us-west-2.amazonaws.com/us-west-2_abc123": idToken,
  },
});

const credentials = await getCredentialsForIdentity({
  region,
  identityId: IdentityId,
  logins: {
    "cognito-idp.us-west-2.amazonaws.com/us-west-2_abc123": idToken,
  },
});

Low-Level Cognito Operations

Cogneato also exports lower-level auth operation helpers used by srpLogin():

| Export | Cognito Operation | |--------|-------------------| | initiateUserSRPAuth | InitiateAuth with USER_SRP_AUTH | | respondPasswordVerifier | RespondToAuthChallenge with PASSWORD_VERIFIER | | respondSoftwareTokenMfa | RespondToAuthChallenge with SOFTWARE_TOKEN_MFA | | respondSmsMfa | RespondToAuthChallenge with SMS_MFA | | respondNewPasswordRequired | RespondToAuthChallenge with NEW_PASSWORD_REQUIRED | | respondDeviceSRPAuth | RespondToAuthChallenge with DEVICE_SRP_AUTH | | confirmDevice | ConfirmDevice plus optional device remembered status |

Prefer srpLogin() unless you need to assemble a custom Cognito challenge flow.

Configuration

Cogneato does not require a config file. Most apps keep Cognito deployment outputs in environment variables or generated service config.

export const authConfig = {
  region: process.env.COGNITO_REGION!,
  userPoolId: process.env.COGNITO_USER_POOL_ID!,
  clientId: process.env.COGNITO_CLIENT_ID!,
  identityPoolId: process.env.COGNITO_IDENTITY_POOL_ID,
};

Example .env values:

COGNITO_REGION=us-west-2
COGNITO_USER_POOL_ID=us-west-2_abc123
COGNITO_CLIENT_ID=4exampleclientid
COGNITO_IDENTITY_POOL_ID=us-west-2:00000000-0000-0000-0000-000000000000

For SaaSLayer services, prefer generated deployer outputs or module manifests over hard-coded stack assumptions.

Architecture

Application UI / Service
        |
        | imports @davidwells/cogneato
        v
+-----------------------------+
| High-Level Auth Helpers     |
| - srpLogin generator        |
| - refresh                   |
| - account operations        |
| - MFA setup helpers         |
+-----------------------------+
        |
        v
+-----------------------------+
| Cognito Operation Wrappers  |
| - InitiateAuth              |
| - RespondToAuthChallenge    |
| - ConfirmDevice             |
| - SignUp / ConfirmSignUp    |
| - GetId / Credentials       |
+-----------------------------+
        |
        v
+-----------------------------+
| SRP and Platform Layer      |
| - SRP math                  |
| - device verifier           |
| - fetch / crypto wrappers   |
| - Cognito JSON request      |
+-----------------------------+
        |
        v
AWS Cognito User Pools and Identity Pools

Troubleshooting

Incorrect username or password.

Cognito returns this for bad credentials and, depending on app-client settings, may also return it when the user does not exist.

const result = await login.next();

if (result.value.code === "ERROR") {
  console.error(result.value.error?.message);
}

Expected 6 digit MFA code

srpLogin() validates MFA input before sending it to Cognito. Pass a string containing exactly six digits.

await login.next("123456");

NEW_PASSWORD_REQUIRED never completes

The generator expects the next value to be the new password string.

let step = await login.next();

if (step.value.code === "NEW_PASSWORD_REQUIRED") {
  step = await login.next("NewPassword1!");
}

Device reuse fails with Missing deviceParams

If you pass a remembered device, include all three persisted fields.

const device = {
  key: saved.key,
  groupKey: saved.groupKey,
  password: saved.password,
};

refresh() succeeds but does not return a refresh token

Cognito refresh-token auth usually returns a new access token and ID token, not a new refresh token. Keep the original refresh token until your app signs out or revokes it.

Browser build cannot find Node globals

This package targets ES2017/CommonJS and uses platform wrappers for crypto and request behavior. If your bundler complains about Node globals, check your bundler's CommonJS handling and runtime crypto support.

Limitations

| Capability | Current State | Workaround | |------------|---------------|------------| | Hosted UI and OAuth redirects | Not included | Use Cognito Hosted UI, Amplify Auth, or your OAuth client. | | Admin Cognito APIs | Not included | Use AWS SDK admin APIs from trusted backend services. | | Full session manager | Not included | Store tokens/device metadata in your application layer. | | WebAuthn authentication flow | Preference settings are typed, but auth flow helpers are not implemented here. | Use Cognito-supported WebAuthn flows separately. | | SecretHash app clients | Not modeled across all operations. | Prefer public app clients for SRP flows or add server-side signing. |

FAQ

Is this a replacement for AWS Amplify Auth?

No. It is a focused Cognito helper package for teams that want direct control over SRP login and Cognito operations without adopting Amplify's broader framework.

Does Cogneato store tokens?

No. It returns tokens and device metadata to the caller. Your app chooses where and how to store them.

Can I use it in a browser?

The package exposes browser-compatible entry metadata and platform wrappers, but you should verify your bundler and runtime crypto support in your target browsers.

Does it support MFA?

Yes. srpLogin() handles software-token MFA and SMS MFA challenges. The package also includes helpers for associating, verifying, and preferring software-token MFA.

Does it support remembered devices?

Yes. Set autoConfirmDevice: true and autoRememberDevice: "remembered" to confirm and remember new devices, then persist the returned newDevice metadata for the next login.

Why is login an async generator?

Cognito auth is interactive. A single login may require MFA, a forced password update, or a device challenge. An async generator lets your app pause for user input and resume the same auth flow.

Where does this package live now?

The canonical source is the SaaSLayer monorepo:

/packages/cogneato

Repository: https://github.com/DavidWells/saaslayer/tree/master/packages/cogneato

What is the project history?

Cogneato began as a fork of the franken-srp package, then grew into a broader set of Cognito SRP login, device, MFA, account-operation, and identity-pool helpers.

The old standalone repository at https://github.com/DavidWells/cogneato is retained as a historical pointer only.

Development

npm install
npm run build
npm test

Integration-test helpers under src/test/cdk can deploy Cognito resources for live Cognito testing:

npm run cdkdeploy
npm run createTestUsers
npm test
npm run cdkdestroy

License

MIT. See LICENSE.