Skip to content
This repository has been archived by the owner on Oct 4, 2024. It is now read-only.

Use aws-jwt-verify library #190

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 36 additions & 97 deletions Cognito/decode-verify-jwt/decode-verify-jwt.ts
Original file line number Diff line number Diff line change
@@ -1,110 +1,49 @@
import {promisify} from 'util';
import * as Axios from 'axios';
import * as jsonwebtoken from 'jsonwebtoken';
const jwkToPem = require('jwk-to-pem');
import { CognitoJwtVerifier } from "aws-jwt-verify";

export interface ClaimVerifyRequest {
readonly token?: string;
}

export interface ClaimVerifyResult {
readonly userName: string;
readonly clientId: string;
readonly isValid: boolean;
readonly error?: any;
}

interface TokenHeader {
kid: string;
alg: string;
}
interface PublicKey {
alg: string;
e: string;
kid: string;
kty: string;
n: string;
use: string;
}
interface PublicKeyMeta {
instance: PublicKey;
pem: string;
}

interface PublicKeys {
keys: PublicKey[];
}

interface MapOfKidToPublicKey {
[key: string]: PublicKeyMeta;
const userPoolId = process.env.USER_POOL_ID as string;
if (!userPoolId) {
throw new Error("env var required: USER_POOL_ID");
}

interface Claim {
token_use: string;
auth_time: number;
iss: string;
exp: number;
username: string;
client_id: string;
}

const cognitoPoolId = process.env.COGNITO_POOL_ID || '';
if (!cognitoPoolId) {
throw new Error('env var required for cognito pool');
const clientId = process.env.CLIENT_ID as string;
if (!clientId) {
throw new Error("env var required: CLIENT_ID");
}
const cognitoIssuer = `https://cognito-idp.us-east-1.amazonaws.com/${cognitoPoolId}`;

let cacheKeys: MapOfKidToPublicKey | undefined;
const getPublicKeys = async (): Promise<MapOfKidToPublicKey> => {
if (!cacheKeys) {
const url = `${cognitoIssuer}/.well-known/jwks.json`;
const publicKeys = await Axios.default.get<PublicKeys>(url);
cacheKeys = publicKeys.data.keys.reduce((agg, current) => {
const pem = jwkToPem(current);
agg[current.kid] = {instance: current, pem};
return agg;
}, {} as MapOfKidToPublicKey);
return cacheKeys;
} else {
return cacheKeys;
}
};
const verifier = CognitoJwtVerifier.create({
userPoolId,
clientId,
tokenUse: "access", // or set to "id" if you want to verify ID tokens
});

const verifyPromised = promisify(jsonwebtoken.verify.bind(jsonwebtoken));

const handler = async (request: ClaimVerifyRequest): Promise<ClaimVerifyResult> => {
const handler = async (
request: ClaimVerifyRequest
): Promise<ClaimVerifyResult> => {
let result: ClaimVerifyResult;
try {
console.log(`user claim verify invoked for ${JSON.stringify(request)}`);
const token = request.token;
const tokenSections = (token || '').split('.');
if (tokenSections.length < 2) {
throw new Error('requested token is invalid');
}
const headerJSON = Buffer.from(tokenSections[0], 'base64').toString('utf8');
const header = JSON.parse(headerJSON) as TokenHeader;
const keys = await getPublicKeys();
const key = keys[header.kid];
if (key === undefined) {
throw new Error('claim made for unknown kid');
}
const claim = await verifyPromised(token, key.pem) as Claim;
const currentSeconds = Math.floor( (new Date()).valueOf() / 1000);
if (currentSeconds > claim.exp || currentSeconds < claim.auth_time) {
throw new Error('claim is expired or invalid');
}
if (claim.iss !== cognitoIssuer) {
throw new Error('claim issuer is invalid');
}
if (claim.token_use !== 'access') {
throw new Error('claim use is not access');
}
console.log(`claim confirmed for ${claim.username}`);
result = {userName: claim.username, clientId: claim.client_id, isValid: true};
const payload = await verifier.verify(request.token as string);
console.log(`payload confirmed for ${payload.username}`);
result = {
userName: payload.username,
clientId: payload.client_id,
isValid: true,
};
} catch (error) {
result = {userName: '', clientId: '', error, isValid: false};
result = { userName: "", clientId: "", error, isValid: false };
}
return result;
};

export {handler};
export { handler };

// Helper types:
export interface ClaimVerifyRequest {
readonly token?: string;
}

export interface ClaimVerifyResult {
readonly userName: string;
readonly clientId: string;
readonly isValid: boolean;
readonly error?: any;
}