Add authenticate hook to implement custom auth checks against current request (#11942)

* Add "authenticate" filter hook that allows custom auth check

* Start on test

* Update Jest, restructure API tests, start implementing authenticate test

* Move access token verify to util function

* Ensure jest can show inline warnings on correct lines

* Update is-directus-jwt to use jsonwebtoken decode + add tests

* Remove unused package

* Tweak and finish + test authenticate

* Tweak test

* Add authenticate filter to docs

* Don't scan tests for codeql

* No seriously, ignore tests
This commit is contained in:
Rijk van Zanten
2022-03-03 16:29:13 -05:00
committed by GitHub
parent b44f9dd8d0
commit eea9f45624
24 changed files with 1900 additions and 520 deletions

View File

@@ -1,34 +1,15 @@
import atob from 'atob';
import logger from '../logger';
import jwt from 'jsonwebtoken';
/**
* Check if a given string conforms to the structure of a JWT
* and whether it is issued by Directus.
*/
export default function isDirectusJWT(string: string): boolean {
const parts = string.split('.');
// JWTs have the structure header.payload.signature
if (parts.length !== 3) return false;
// Check if all segments are base64 encoded
try {
atob(parts[0]);
atob(parts[1]);
atob(parts[2]);
} catch (err: any) {
logger.error(err);
return false;
}
// Check if the header and payload are valid JSON
try {
JSON.parse(atob(parts[0]));
const payload = JSON.parse(atob(parts[1]));
if (payload.iss !== 'directus') return false;
const payload = jwt.decode(string, { json: true });
if (payload?.iss !== 'directus') return false;
return true;
} catch {
return false;
}
return true;
}

29
api/src/utils/jwt.ts Normal file
View File

@@ -0,0 +1,29 @@
import jwt, { JsonWebTokenError, TokenExpiredError } from 'jsonwebtoken';
import { DirectusTokenPayload } from '../types';
import { InvalidTokenException, ServiceUnavailableException } from '../exceptions';
export function verifyAccessJWT(token: string, secret: string): DirectusTokenPayload {
let payload;
try {
payload = jwt.verify(token, secret, {
issuer: 'directus',
}) as Record<string, any>;
} catch (err) {
if (err instanceof TokenExpiredError) {
throw new InvalidTokenException('Token expired.');
} else if (err instanceof JsonWebTokenError) {
throw new InvalidTokenException('Token invalid.');
} else {
throw new ServiceUnavailableException(`Couldn't verify token.`, { service: 'jwt' });
}
}
const { id, role, app_access, admin_access, share, share_scope } = payload;
if (role === undefined || app_access === undefined || admin_access === undefined) {
throw new InvalidTokenException('Invalid token payload.');
}
return { id, role, app_access, admin_access, share, share_scope };
}