Merge pull request 'API Key based auth feature/26-api_auth' (#27) from feature/26-api_auth into dev
continuous-integration/drone/push Build is failing Details

Reviewed-on: #27
This commit is contained in:
Nicolai Ort 2021-02-13 20:09:36 +00:00
commit 2d031dae03
6 changed files with 51 additions and 2 deletions

View File

@ -35,6 +35,7 @@ The basic generation mechanism makes the templates and routes interchangeable (i
| CURRENCY_SYMBOL | String | "€" | The your currency's symbol - used to generate pdf text.
| SPONSORING_RECEIPT_MINIMUM_AMOUNT | String | "10" | The mimimum total donation amount a sponsor has to donate to be able to receive a donation receipt - used to generate pdf text.
| SPONOR_LOGOS | Array<String> | Empty png | The sponsor images you want to loop through. You can provide them via http url, local file or base64-encoded image.
| API_KEY | String(min length: 64) | Random generated string | The api key you want to use for auth (query-param `key`), has to be at least 64 chars long.
## Templates
> The document server uses html templates to generate various pdf documents.

View File

@ -14,6 +14,14 @@ export function generateSpec(storage: MetadataArgsStorage, schemas) {
{
components: {
schemas,
"securitySchemes": {
"AuthToken": {
"type": "apiKey",
"in": "query",
"name": "key",
description: "A simple api key. See the README's env section for more details."
}
}
},
info: {
description: "The the API for the LfK! document server.",

View File

@ -3,11 +3,13 @@ import "reflect-metadata";
import { createExpressServer } from "routing-controllers";
import { config, e as errors } from './config';
import loaders from "./loaders/index";
import AuthChecker from './middlewares/AuthChecker';
import { ErrorHandler } from './middlewares/ErrorHandler';
const CONTROLLERS_FILE_EXTENSION = process.env.NODE_ENV === 'production' ? 'js' : 'ts';
const app = createExpressServer({
middlewares: [ErrorHandler],
authorizationChecker: AuthChecker,
development: config.development,
cors: true,
controllers: [`${__dirname}/controllers/*.${CONTROLLERS_FILE_EXTENSION}`],

View File

@ -1,3 +1,4 @@
import consola from "consola";
import { config as configDotenv } from 'dotenv';
configDotenv();
@ -9,7 +10,8 @@ export const config = {
currency_symbol: process.env.CURRENCY_SYMBOL || "€",
sponsoring_receipt_minimum_amount: process.env.SPONSORING_RECEIPT_MINIMUM_AMOUNT || "10",
codeformat: process.env.CODEFORMAT || "qrcode",
sponor_logos: getSponsorLogos()
sponor_logos: getSponsorLogos(),
api_key: getApiKey(),
}
let errors = 0
if (typeof config.internal_port !== "number") {
@ -27,4 +29,24 @@ function getSponsorLogos(): string[] {
return ["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg=="];
}
}
function getApiKey(): string {
const key = process.env.API_KEY;
if (!key) {
consola.info("No API key set - generating a random one...");
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (var i = 0; i < 64; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
consola.info(`API KEY: ${result}`)
return result;
}
if (key.length < 64) {
consola.error(`API key is too short - minimum: 64, current: ${key.length}`)
throw new Error("API_KEY too short.")
}
return key
}
export let e = errors

View File

@ -1,4 +1,4 @@
import { Body, JsonController, Post, QueryParam, Res } from 'routing-controllers';
import { Authorized, Body, JsonController, Post, QueryParam, Res } from 'routing-controllers';
import { OpenAPI } from 'routing-controllers-openapi';
import { Runner } from '../models/Runner';
import { RunnerCard } from '../models/RunnerCard';
@ -10,6 +10,8 @@ import { PdfCreator } from '../PdfCreator';
* All endpoints have to accept a locale query-param to support i18n.
*/
@JsonController()
@Authorized()
@OpenAPI({ security: [{ "AuthToken": [] }] })
export class PdfController {
private pdf: PdfCreator = new PdfCreator();
private initialized: boolean = false;

View File

@ -0,0 +1,14 @@
import { Action } from "routing-controllers";
import { config } from '../config';
/**
* Handles authentication via jwt's (Bearer authorization header) for all api endpoints using the @Authorized decorator.
* @param action Routing-Controllers action object that provides request and response objects among other stuff.
* @param permissions The permissions that the endpoint using @Authorized requires.
*/
const AuthChecker = async (action: Action) => {
const provided_token = action.request.query.key;
return provided_token == config.api_key;
}
export default AuthChecker