atlassian-connect-auth
Version:
Helper for handling webhooks from Atlassian products
55 lines (48 loc) • 1.63 kB
text/typescript
import { AuthError, AuthErrorCode } from './AuthError';
import { ConnectJwt, ContextQsh, QueryStringHashType } from './types';
export interface VerifyQueryStringHashArgs {
queryStringHashType?: QueryStringHashType;
connectJwt: ConnectJwt;
computeQueryStringHashFunction: () => string;
}
/**
* Verifies the Query String Hash value provided in a Connect JWT against an incoming request to make sure it is not
* tainted.
*/
export function verifyQueryStringHash({
queryStringHashType = 'computed',
connectJwt,
computeQueryStringHashFunction,
}: VerifyQueryStringHashArgs): void {
if (queryStringHashType === 'skip') {
return;
}
if (!connectJwt.qsh) {
throw new AuthError('JWT did not contain the Query String Hash (QSH) claim', {
code: AuthErrorCode.MISSING_QSH,
connectJwt,
});
}
// Check context QSH
const isContext = queryStringHashType === 'context';
if ((isContext || queryStringHashType === 'any') && connectJwt.qsh === ContextQsh) {
return;
}
// Check computed hash
let requestComputedQsh = isContext ? 'context' : 'skipped';
if (queryStringHashType === 'computed' || queryStringHashType === 'any') {
requestComputedQsh = computeQueryStringHashFunction();
if (connectJwt.qsh === requestComputedQsh) {
return;
}
}
// Error if no match
throw new AuthError('Invalid QSH', {
code: AuthErrorCode.INVALID_QSH,
qshInfo: {
computed: requestComputedQsh || /* istanbul ignore next: ignore fallback */ 'empty',
received: connectJwt.qsh || /* istanbul ignore next: ignore fallback */ 'empty',
},
connectJwt,
});
}