🔀 Merge pull request #68 from ccakes:jwt-auth

This commit is contained in:
Jan Oberhauser 2019-10-14 22:37:51 +02:00
parent 6908afa677
commit 92669bb77a
4 changed files with 187 additions and 117 deletions

View file

@ -141,8 +141,27 @@ const config = convict({
env: 'N8N_BASIC_AUTH_PASSWORD',
doc: 'The password of the basic auth user'
},
},
jwtAuth: {
active: {
format: 'Boolean',
default: false,
env: 'N8N_JWT_AUTH_ACTIVE',
doc: 'If JWT auth should be activated for editor and REST-API'
},
jwtHeader: {
format: String,
default: '',
env: 'N8N_JWT_AUTH_HEADER',
doc: 'The request header containing a signed JWT'
},
jwksUri: {
format: String,
default: '',
env: 'N8N_JWKS_URI',
doc: 'The URI to fetch JWK Set for JWT auh'
},
}
},
endpoints: {

View file

@ -71,6 +71,7 @@
"typescript": "~3.5.2"
},
"dependencies": {
"@types/jsonwebtoken": "^8.3.4",
"@oclif/command": "^1.5.18",
"@oclif/errors": "^1.2.2",
"basic-auth": "^2.0.1",
@ -84,6 +85,8 @@
"glob-promise": "^3.4.0",
"google-timezones-json": "^1.0.2",
"inquirer": "^6.5.1",
"jsonwebtoken": "^8.5.1",
"jwks-rsa": "^1.6.0",
"localtunnel": "^1.9.1",
"mongodb": "^3.2.3",
"n8n-core": "~0.11.0",

View file

@ -52,6 +52,11 @@ export function basicAuthAuthorizationError(resp: Response, realm: string, messa
resp.end(message);
}
export function jwtAuthAuthorizationError(resp: Response, message?: string) {
resp.statusCode = 403;
resp.end(message);
}
export function sendSuccessResponse(res: Response, data: any, raw?: boolean, responseCode?: number) { // tslint:disable-line:no-any
res.setHeader('Content-Type', 'application/json');

View file

@ -74,6 +74,8 @@ import {
import * as basicAuth from 'basic-auth';
import * as compression from 'compression';
import * as config from '../config';
import * as jwt from 'jsonwebtoken';
import * as jwks from 'jwks-rsa';
// @ts-ignore
import * as timezones from 'google-timezones-json';
import * as parseUrl from 'parseurl';
@ -126,6 +128,7 @@ class App {
async config(): Promise<void> {
this.versions = await GenericHelpers.getVersions();
const authIgnoreRegex = new RegExp(`^\/(rest|healthz|${this.endpointWebhook}|${this.endpointWebhookTest})\/?.*$`);
// Check for basic auth credentials if activated
const basicAuthActive = config.get('security.basicAuth.active') as boolean;
@ -140,7 +143,6 @@ class App {
throw new Error('Basic auth is activated but no password got defined. Please set one!');
}
const authIgnoreRegex = new RegExp(`^\/(rest|healthz|${this.endpointWebhook}|${this.endpointWebhookTest})\/?.*$`);
this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
if (req.url.match(authIgnoreRegex)) {
return next();
@ -162,6 +164,47 @@ class App {
});
}
// Check for and validate JWT if configured
const jwtAuthActive = config.get('security.jwtAuth.active') as boolean;
if (jwtAuthActive === true) {
const jwtAuthHeader = await GenericHelpers.getConfigValue('security.jwtAuth.jwtHeader') as string;
if (jwtAuthHeader === '') {
throw new Error('JWT auth is activated but no request header was defined. Please set one!');
}
const jwksUri = await GenericHelpers.getConfigValue('security.jwtAuth.jwksUri') as string;
if (jwksUri === '') {
throw new Error('JWT auth is activated but no JWK Set URI was defined. Please set one!');
}
this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
if (req.url.match(authIgnoreRegex)) {
return next();
}
const token = req.header(jwtAuthHeader) as string;
if (token === '') {
return ResponseHelper.jwtAuthAuthorizationError(res, "Missing token");
}
const jwkClient = jwks({ cache: true, jwksUri });
function getKey(header: any, callback: Function) {
jwkClient.getSigningKey(header.kid, (err: Error, key: any) => {
if (err) throw ResponseHelper.jwtAuthAuthorizationError(res, err.message);
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
jwt.verify(token, getKey, {}, (err: Error, decoded: string) => {
if (err) return ResponseHelper.jwtAuthAuthorizationError(res, "Invalid token");
next();
});
});
}
// Get push connections
this.app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
if (req.url.indexOf('/rest/push') === 0) {