feature/#22-API-Tokens #49
8 changed files with 115 additions and 5 deletions
|
@ -18,5 +18,5 @@ MAIL_HOST = mail_hoststring
|
|||
MAIL_PORT = mail_portnumber
|
||||
MAIL_SECURE (true|false) // true for port 465, fals for other ports
|
||||
|
||||
CLUB_NAME = clubname
|
||||
CLUB_NAME = clubname #default FF Admin
|
||||
CLUB_WEBSITE = https://my-club-website-url
|
|
@ -40,7 +40,7 @@ services:
|
|||
- MAIL_HOST=<url>
|
||||
- MAIL_PORT=<port> # default ist auf 578 gesetzt
|
||||
- MAIL_SECURE=<boolean> # default ist auf false gesetzt
|
||||
- CLUB_NAME=<tobemodified>
|
||||
- CLUB_NAME=<tobemodified> # default ist auf FF Admin gesetzt
|
||||
- CLUB_WEBSITE=<tobemodified>
|
||||
volumes:
|
||||
- <volume|local path>:/app/files
|
||||
|
|
|
@ -7,6 +7,9 @@ import { CreateApiCommand, DeleteApiCommand, UpdateApiCommand } from "../../../c
|
|||
import ApiCommandHandler from "../../../command/user/api/apiCommandHandler";
|
||||
import { UpdateApiPermissionsCommand } from "../../../command/user/api/apiPermissionCommand";
|
||||
import ApiPermissionCommandHandler from "../../../command/user/api/apiPermissionCommandHandler";
|
||||
import { JWTHelper } from "../../../helpers/jwtHelper";
|
||||
import { CLUB_NAME } from "../../../env.defaults";
|
||||
import { StringHelper } from "../../../helpers/stringHelper";
|
||||
|
||||
/**
|
||||
* @description get All apis
|
||||
|
@ -69,10 +72,15 @@ export async function createApi(req: Request, res: Response): Promise<any> {
|
|||
let title = req.body.title;
|
||||
let expiry = req.body.expiry;
|
||||
|
||||
// TODO: create jwt as token to prevent random string tests
|
||||
let token = await JWTHelper.create({
|
||||
iss: CLUB_NAME,
|
||||
sub: "api_token_retrieve",
|
||||
iat: new Date().toISOString(),
|
||||
aud: StringHelper.random(32),
|
||||
});
|
||||
|
||||
let createApi: CreateApiCommand = {
|
||||
token: "",
|
||||
token: token,
|
||||
title: title,
|
||||
expiry: expiry,
|
||||
};
|
||||
|
|
34
src/controller/apiController.ts
Normal file
34
src/controller/apiController.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { Request, Response } from "express";
|
||||
import { JWTHelper } from "../helpers/jwtHelper";
|
||||
import { JWTToken } from "../type/jwtTypes";
|
||||
import InternalException from "../exceptions/internalException";
|
||||
import RefreshCommandHandler from "../command/refreshCommandHandler";
|
||||
import { CreateRefreshCommand, DeleteRefreshCommand } from "../command/refreshCommand";
|
||||
import UserService from "../service/user/userService";
|
||||
import speakeasy from "speakeasy";
|
||||
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
||||
import RefreshService from "../service/refreshService";
|
||||
import ApiService from "../service/user/apiService";
|
||||
import ForbiddenRequestException from "../exceptions/forbiddenRequestException";
|
||||
|
||||
/**
|
||||
* @description Check authentication status by token
|
||||
* @param req {Request} Express req object
|
||||
* @param res {Response} Express res object
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
export async function getAccess(req: Request, res: Response): Promise<any> {
|
||||
const bearer = req.headers.authorization?.split(" ")?.[1] ?? undefined;
|
||||
|
||||
let { expiry } = await ApiService.getByToken(bearer);
|
||||
|
||||
if (new Date() > new Date(expiry)) {
|
||||
throw new ForbiddenRequestException("api token expired");
|
||||
}
|
||||
|
||||
let accessToken = await JWTHelper.buildApiToken(bearer);
|
||||
|
||||
res.json({
|
||||
accessToken,
|
||||
});
|
||||
}
|
|
@ -21,7 +21,7 @@ export const MAIL_HOST = process.env.MAIL_HOST ?? "";
|
|||
export const MAIL_PORT = Number(process.env.MAIL_PORT ?? "587");
|
||||
export const MAIL_SECURE = process.env.MAIL_SECURE ?? "false";
|
||||
|
||||
export const CLUB_NAME = process.env.CLUB_NAME ?? "";
|
||||
export const CLUB_NAME = process.env.CLUB_NAME ?? "FF Admin";
|
||||
export const CLUB_WEBSITE = process.env.CLUB_WEBSITE ?? "";
|
||||
|
||||
export function configCheck() {
|
||||
|
|
|
@ -6,6 +6,8 @@ import RolePermissionService from "../service/user/rolePermissionService";
|
|||
import UserPermissionService from "../service/user/userPermissionService";
|
||||
import UserService from "../service/user/userService";
|
||||
import PermissionHelper from "./permissionHelper";
|
||||
import ApiService from "../service/user/apiService";
|
||||
import ApiPermissionService from "../service/user/apiPermissionService";
|
||||
|
||||
export abstract class JWTHelper {
|
||||
static validate(token: string): Promise<string | jwt.JwtPayload> {
|
||||
|
@ -72,4 +74,29 @@ export abstract class JWTHelper {
|
|||
throw new InternalException("Failed accessToken creation", err);
|
||||
});
|
||||
}
|
||||
|
||||
static async buildApiToken(token: string): Promise<string> {
|
||||
let { id, title } = await ApiService.getByToken(token);
|
||||
let apiPermissions = await ApiPermissionService.getByApi(id);
|
||||
let apiPermissionStrings = apiPermissions.map((e) => e.permission);
|
||||
let permissionObject = PermissionHelper.convertToObject(apiPermissionStrings);
|
||||
|
||||
let jwtData: JWTToken = {
|
||||
userId: id,
|
||||
mail: "",
|
||||
username: title,
|
||||
firstname: "",
|
||||
lastname: "",
|
||||
isOwner: false,
|
||||
permissions: permissionObject,
|
||||
};
|
||||
|
||||
return await JWTHelper.create(jwtData)
|
||||
.then((result) => {
|
||||
return result;
|
||||
})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed accessToken creation", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ export default async function authenticate(req: Request, res: Response, next: Fu
|
|||
throw new InternalException("process failed");
|
||||
}
|
||||
|
||||
if (decoded?.sub == "api_token_retrieve") {
|
||||
throw new BadRequestException("This token is only authorized to get temporary access tokens via GET /api/webapi");
|
||||
}
|
||||
|
||||
req.userId = decoded.userId;
|
||||
req.username = decoded.username;
|
||||
req.isOwner = decoded.isOwner;
|
||||
|
|
37
src/middleware/authenticateAPI.ts
Normal file
37
src/middleware/authenticateAPI.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { Request, Response } from "express";
|
||||
import jwt from "jsonwebtoken";
|
||||
import BadRequestException from "../exceptions/badRequestException";
|
||||
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
||||
import InternalException from "../exceptions/internalException";
|
||||
import { JWTHelper } from "../helpers/jwtHelper";
|
||||
|
||||
export default async function authenticateAPI(req: Request, res: Response, next: Function) {
|
||||
const bearer = req.headers.authorization?.split(" ")?.[1] ?? undefined;
|
||||
|
||||
if (!bearer) {
|
||||
throw new BadRequestException("Provide valid Authorization Header");
|
||||
}
|
||||
|
||||
let decoded: string | jwt.JwtPayload;
|
||||
await JWTHelper.validate(bearer)
|
||||
.then((result) => {
|
||||
decoded = result;
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err == "jwt expired") {
|
||||
throw new UnauthorizedRequestException("Token expired", err);
|
||||
} else {
|
||||
throw new BadRequestException("Failed Authorization Header decoding", err);
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof decoded == "string" || !decoded) {
|
||||
throw new InternalException("process failed");
|
||||
}
|
||||
|
||||
if (decoded?.sub != "api_token_retrieve") {
|
||||
throw new BadRequestException("This route can only be accessed via a api token");
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
Loading…
Reference in a new issue