mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
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:
@@ -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
29
api/src/utils/jwt.ts
Normal 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 };
|
||||
}
|
||||
Reference in New Issue
Block a user