Unofficial Amazon Cognito Identity SDK for Deno and TypeScript, published on JSR. Add user sign-up and sign-in to web and server apps with AWS Cognito (no Amplify required).
Based on amazon-cognito-identity-js. Ported from amazon-cognito-identity-dart-2.
Need ideas to get started?
- Check out use cases below.
- Authenticated access to:
- AppSync + GraphQL
- API Gateway + Lambda
- S3 (Presigned Post, GET with Authorization)
- Follow the tutorial on Serverless Stack for Cognito setup.
- Custom storage for persisting sessions.
- Client secret for app clients that use a secret.
- AWS credentials (Identity Pools) and federated logins.
import {
AttributeArg,
AuthenticationDetails,
CognitoUser,
CognitoUserMfaRequiredException,
CognitoUserNewPasswordRequiredException,
CognitoUserPool,
CognitoUserSession,
} from "jsr:@cbdstudios/aws-cognito-identity";
import {
AwsSigV4Client,
SigV4Request,
} from "jsr:@cbdstudios/aws-cognito-identity/sig_v4";Or add to your deno.json:
{
"imports": {
"@cbdstudios/aws-cognito-identity": "jsr:@cbdstudios/aws-cognito-identity@1",
"@cbdstudios/aws-cognito-identity/sig_v4": "jsr:@cbdstudios/aws-cognito-identity@1/sig_v4"
}
}Create a CognitoUserPool with User Pool ID and Client ID, then sign up with
username, password, and optional attributes.
import {
AttributeArg,
CognitoUserPool,
} from "jsr:@cbdstudios/aws-cognito-identity";
const userPool = new CognitoUserPool(
"ap-southeast-1_xxxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxx",
);
const userAttributes = [
new AttributeArg("first_name", "Jimmy"),
new AttributeArg("last_name", "Wong"),
];
try {
const data = await userPool.signUp(
"email@example.com",
"Password001",
{ userAttributes },
);
console.log(data.user, data.userConfirmed, data.userSub);
} catch (e) {
console.error(e);
}import {
CognitoUser,
CognitoUserPool,
} from "jsr:@cbdstudios/aws-cognito-identity";
const userPool = new CognitoUserPool(
"ap-southeast-1_xxxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxx",
);
const cognitoUser = new CognitoUser("email@example.com", userPool);
try {
const confirmed = await cognitoUser.confirmRegistration("123456");
console.log(confirmed);
} catch (e) {
console.error(e);
}const cognitoUser = new CognitoUser("email@example.com", userPool);
try {
const status = await cognitoUser.resendConfirmationCode();
console.log(status);
} catch (e) {
console.error(e);
}import {
AuthenticationDetails,
CognitoClientException,
CognitoUser,
CognitoUserConfirmationNecessaryException,
CognitoUserCustomChallengeException,
CognitoUserEmailOtpRequiredException,
CognitoUserMfaRequiredException,
CognitoUserMfaSetupException,
CognitoUserNewPasswordRequiredException,
CognitoUserPool,
CognitoUserSelectMfaTypeException,
CognitoUserTotpRequiredException,
} from "jsr:@cbdstudios/aws-cognito-identity";
const userPool = new CognitoUserPool(
"ap-southeast-1_xxxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxx",
);
const cognitoUser = new CognitoUser("email@example.com", userPool);
const authDetails = new AuthenticationDetails(
"email@example.com",
"Password001",
);
let session;
try {
session = await cognitoUser.authenticateUser(authDetails);
} catch (e) {
if (e instanceof CognitoUserNewPasswordRequiredException) {
// handle New Password challenge
} else if (e instanceof CognitoUserMfaRequiredException) {
// handle SMS_MFA challenge
} else if (e instanceof CognitoUserSelectMfaTypeException) {
// handle SELECT_MFA_TYPE challenge
} else if (e instanceof CognitoUserMfaSetupException) {
// handle MFA_SETUP challenge
} else if (e instanceof CognitoUserTotpRequiredException) {
// handle SOFTWARE_TOKEN_MFA challenge
} else if (e instanceof CognitoUserEmailOtpRequiredException) {
// handle EMAIL_OTP challenge
} else if (e instanceof CognitoUserCustomChallengeException) {
// handle CUSTOM_CHALLENGE challenge
} else if (e instanceof CognitoUserConfirmationNecessaryException) {
// handle User Confirmation Necessary
} else if (e instanceof CognitoClientException) {
// handle wrong username/password or other client errors
}
throw e;
}
console.log(session!.getAccessToken().getJwtToken());let attributes;
try {
attributes = await cognitoUser.getUserAttributes();
} catch (e) {
console.error(e);
}
attributes?.forEach((attr) => {
console.log("attribute", attr.getName(), "has value", attr.getValue());
});try {
const data = await cognitoUser.getAttributeVerificationCode("email");
console.log(data);
// ... obtain verification code from user ...
const verified = await cognitoUser.verifyAttribute("email", "123456");
console.log(verified);
} catch (e) {
console.error(e);
}try {
await cognitoUser.deleteAttributes(["nickname"]);
} catch (e) {
console.error(e);
}import { CognitoUserAttribute } from "jsr:@cbdstudios/aws-cognito-identity";
const attributes = [new CognitoUserAttribute("nickname", "joe")];
try {
await cognitoUser.updateAttributes(attributes);
} catch (e) {
console.error(e);
}try {
const mfaEnabled = await cognitoUser.enableMfa();
const mfaOptions = await cognitoUser.getMFAOptions();
console.log("mfaOptions:", mfaOptions);
} catch (e) {
console.error(e);
}try {
const mfaDisabled = await cognitoUser.disableMfa();
console.log(mfaDisabled);
} catch (e) {
console.error(e);
}try {
const changed = await cognitoUser.changePassword(
"oldPassword",
"newPassword",
);
console.log(changed);
} catch (e) {
console.error(e);
}const cognitoUser = new CognitoUser("email@example.com", userPool);
try {
const data = await cognitoUser.forgotPassword();
console.log("Code sent to", data);
// ... prompt user for code and new password ...
const confirmed = await cognitoUser.confirmPassword("123456", "newPassword");
console.log(confirmed);
} catch (e) {
console.error(e);
}try {
const deleted = await cognitoUser.deleteUser();
console.log(deleted);
} catch (e) {
console.error(e);
}await cognitoUser.signOut();await cognitoUser.globalSignOut();Pass key-value pairs as validation data (e.g. in AuthenticationDetails or
sign-up):
const validationData = {
myCustomKey1: "myCustomValue1",
myCustomKey2: "myCustomValue2",
};
const authDetails = new AuthenticationDetails(
"user@example.com",
"Password001",
validationData,
);Security: With USER_SRP_AUTH (the default), the password never leaves the
client as plain text—only SRP values are sent. Do not put the password (or
any sensitive key) in validationData or clientMetadata; those are sent as
ClientMetadata and the library strips known sensitive keys, but you should
never pass secrets there.
If you see a request body in the browser that is only
{"email":"...","password":"..."} (or similar), that request is from your
application (e.g. a form POST or a fetch to your own backend), not from this
SDK. This SDK’s requests to Cognito use the AWS API shape: AuthFlow,
ClientId, AuthParameters (e.g. USERNAME, SRP_A), and optional
ClientMetadata. To confirm SRP is used, look for a request to
cognito-idp.*.amazonaws.com with AuthFlow: "USER_SRP_AUTH" and
AuthParameters containing SRP_A (no plain password field).
When building a signed request, you can pass a custom header instead of AWS SigV4:
import {
AwsSigV4Client,
SigV4Request,
} from "jsr:@cbdstudios/aws-cognito-identity/sig_v4";
const req = new SigV4Request(awsSigV4Client, {
method: "POST",
path: "/graphql",
headers: {
"Content-Type": "application/graphql",
Accept: "application/json",
},
body: { query },
authorizationHeader: session.getIdToken().getJwtToken(),
});
await req.sign(session.getIdToken().getJwtToken());
// use req.url, req.headers, req.bodyWhen an admin creates the user and the account has status FORCE_CHANGE_PASSWORD, handle the challenge and optionally set required attributes:
try {
session = await cognitoUser.authenticateUser(authDetails);
} catch (e) {
if (e instanceof CognitoUserNewPasswordRequiredException) {
try {
if (!e.requiredAttributes?.length) {
session = await cognitoUser.sendNewPasswordRequiredAnswer(
"NewPassword002!",
);
} else {
const attributes = { name: "Adam Kaminski" };
session = await cognitoUser.sendNewPasswordRequiredAnswer(
"NewPassword002!",
attributes,
);
}
} catch (inner) {
// handle MFA or other nested challenges
throw inner;
}
} else {
throw e;
}
}
console.log(session!.getAccessToken().getJwtToken());Get an authenticated user’s AWS credentials (e.g. for Identity Pools). Use with Signature Version 4 or other AWS APIs.
import {
AuthenticationDetails,
CognitoCredentials,
CognitoUser,
CognitoUserPool,
} from "jsr:@cbdstudios/aws-cognito-identity";
const userPool = new CognitoUserPool(
"ap-southeast-1_xxxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxx",
);
const cognitoUser = new CognitoUser("email@example.com", userPool);
const authDetails = new AuthenticationDetails(
"email@example.com",
"Password001",
);
const session = await cognitoUser.authenticateUser(authDetails);
const credentials = new CognitoCredentials(
"ap-southeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
userPool,
);
await credentials.getAwsCredentials(session!.getIdToken().getJwtToken());
console.log(credentials.accessKeyId);
console.log(credentials.secretAccessKey);
console.log(credentials.sessionToken);Import the SigV4 helpers from the subpath:
import {
AwsSigV4Client,
SigV4Request,
} from "jsr:@cbdstudios/aws-cognito-identity/sig_v4";Note: In this Deno/TypeScript version, SigV4Request uses async crypto.
After constructing the request, call await req.sign() before using req.url,
req.headers, and req.body.
import { CognitoCredentials } from "jsr:@cbdstudios/aws-cognito-identity";
import {
AwsSigV4Client,
SigV4Request,
} from "jsr:@cbdstudios/aws-cognito-identity/sig_v4";
const credentials = new CognitoCredentials(identityPoolId, userPool);
await credentials.getAwsCredentials(session!.getIdToken().getJwtToken());
const endpoint = "https://xxxx.execute-api.ap-southeast-1.amazonaws.com/dev";
const client = new AwsSigV4Client(
credentials.accessKeyId!,
credentials.secretAccessKey!,
endpoint,
"execute-api",
"ap-southeast-1",
credentials.sessionToken,
);
const req = new SigV4Request(client, {
method: "POST",
path: "/projects",
headers: { "header-1": "one", "header-2": "two" },
queryParams: { tracking: "x123" },
body: { color: "blue" },
});
await req.sign();
const response = await fetch(req.url, {
method: req.method,
headers: req.headers as Record<string, string>,
body: req.body,
});
console.log(await response.text());const client = new AwsSigV4Client(
credentials.accessKeyId!,
credentials.secretAccessKey!,
"https://xxxx.appsync-api.ap-southeast-1.amazonaws.com",
"appsync",
"ap-southeast-1",
credentials.sessionToken,
);
const req = new SigV4Request(client, {
method: "POST",
path: "/graphql",
headers: { "Content-Type": "application/graphql; charset=utf-8" },
body: { operationName: "GetEvent", query: queryString },
});
await req.sign();
const response = await fetch(req.url, {
method: req.method,
headers: req.headers as Record<string, string>,
body: req.body,
});Use the access or ID token as the Authorization header (no SigV4):
const endpoint =
"https://xxxx.appsync-api.ap-southeast-1.amazonaws.com/graphql";
const response = await fetch(endpoint, {
method: "POST",
headers: {
Authorization: session!.getAccessToken().getJwtToken()!,
"Content-Type": "application/json",
},
body: JSON.stringify({ operationName: "CreateItem", query: mutation }),
});Implement the CognitoStorage interface to persist sessions (e.g. in
localStorage, a file, or a database).
import type { CognitoStorage } from "jsr:@cbdstudios/aws-cognito-identity";
const storage: CognitoStorage = {
async setItem(key, value) {
localStorage.setItem(key, JSON.stringify(value));
return value;
},
async getItem(key) {
const v = localStorage.getItem(key);
return v != null ? JSON.parse(v) : null;
},
async removeItem(key) {
localStorage.removeItem(key);
},
async clear() {
localStorage.clear();
},
};
const userPool = new CognitoUserPool(poolId, clientId, { storage });
const cognitoUser = new CognitoUser("email@example.com", userPool, { storage });
const authDetails = new AuthenticationDetails(
"email@example.com",
"Password001",
);
await cognitoUser.authenticateUser(authDetails);
// Later
const user = await userPool.getCurrentUser();
const session = await user!.getSession();
console.log(session?.isValid());Pass the provider name as the second argument to getAwsCredentials:
const credentials = new CognitoCredentials(identityPoolId, userPool);
await credentials.getAwsCredentials(accessToken, "graph.facebook.com");
console.log(credentials.sessionToken);If your app client has a client secret, set it when creating the pool:
const userPool = new CognitoUserPool(
"ap-southeast-1_xxxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxx",
{ clientSecret: "xxxxxxxxxxxxxxxxxxxxxxx" },
);Sign-up and getCurrentUser() will compute and pass the secret hash when
needed.
With USER_SRP_AUTH, Cognito expects InitiateAuth and
RespondToAuthChallenge to be called from the same context (the client:
browser or mobile app). If you proxy these calls from a server (e.g. your
backend calls Cognito on behalf of the user), Cognito may not return
Session in the InitiateAuth response, so you cannot complete
RespondToAuthChallenge.
Recommendation for web apps:
- Have the browser call Cognito directly for InitiateAuth and
RespondToAuthChallenge (same headers/body as this SDK; you can use
fetchtohttps://cognito-idp.<region>.amazonaws.com/withX-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuthandRespondToAuthChallenge). The password stays in the client; only SRP values are sent. - When the client receives
AuthenticationResult(idToken, accessToken, refreshToken), send those tokens to your server. - Use this library on the server for everything after that: validate tokens, call GetUser, create your own session/cookie, SignUp, ForgotPassword, etc.
This library remains the right choice for server-side Cognito calls (GetUser, SignUp, ForgotPassword, token handling). For the SRP handshake itself, call Cognito from the client. See docs/srp-client-vs-server.md for more detail and a short checklist.
- Copy
.env.exampleto.envand set:COGNITO_USER_POOL_IDCOGNITO_CLIENT_IDCOGNITO_TEST_USERNAMECOGNITO_TEST_PASSWORD
- Run:
deno task test:live
(requires--allow-read --allow-net --allow-env)
To reach a 100% JSR score:
- Done in this repo: README, examples, module docs on all entrypoints, JSDoc on exported symbols,
descriptionindeno.json, no slow types. - On JSR: In the package’s Settings on jsr.io, set the description (if not taken from
deno.json) and mark at least one runtime as compatible (e.g. Deno, Node) so “at least one/two runtimes” are satisfied. - Provenance: Publish from CI with a verifiable workflow so the package gets a transparency log entry. For example, link the package to your GitHub repo in JSR settings, then add a workflow that runs
deno publish(ornpx jsr publish) withid-token: writeso JSR can verify the build.
MIT