Skip to content
This repository has been archived by the owner on Jul 12, 2024. It is now read-only.

Commit

Permalink
Merge pull request #243 from esune/master
Browse files Browse the repository at this point in the history
Clean up hooks code
  • Loading branch information
esune authored Nov 4, 2020
2 parents 32e74d4 + 01c8e0e commit 0c8a5cd
Showing 1 changed file with 60 additions and 48 deletions.
108 changes: 60 additions & 48 deletions api/src/utils/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {
Forbidden,
MethodNotAllowed,
NotAuthenticated,
} from "@feathersjs/errors";
import { Forbidden, MethodNotAllowed } from "@feathersjs/errors";
import { HookContext } from "@feathersjs/feathers";
import { decode, verify } from "jsonwebtoken";
import jwks, { ClientOptions, JwksClient, SigningKey } from "jwks-rsa";
Expand Down Expand Up @@ -47,38 +43,25 @@ export async function canDeleteInvite(context: HookContext) {
}

export async function verifyJWT(context: HookContext) {
const authHeader = context.params.headers?.authorization as string;
if (!authHeader) {
return Promise.reject(new Forbidden("The authorization header is missing"));
}
const token = authHeader.split(" ")[1];

// fetch public key from JWKS url and verify token
const jwksOptions = {
jwksUri: context.app.get("authentication").jwksUri,
} as ClientOptions;
const client = jwks(jwksOptions) as JwksClient;
const keys = (await client.getSigningKeysAsync()) as SigningKey[];

let decoded;
try {
decoded = verify(token, keys[0].getPublicKey(), {
algorithms: context.app.get("authentication").algorithms,
});
} catch (error) {
throw new NotAuthenticated(`Authentication failed: ${error.message}`);
const token = extractIdToken(
context.params.headers?.authorization as string
);
if (!token) {
throw new Forbidden("The authorization header is missing");
}
const keys = await getAuthSigningKeys(context);
verifyIdToken(token, keys, context);
return context;
}

export function verifyJWTRoles(roles: string[]) {
return async (context: HookContext) => {
const authHeader = context.params.headers?.authorization as string;
if (!authHeader) {
const token = extractIdToken(
context.params.headers?.authorization as string
);
if (!token) {
throw new Forbidden("The authorization header is missing");
}
const token = authHeader.split(" ")[1];

const decoded = decode(token) as {
[key: string]: any;
};
Expand All @@ -95,12 +78,12 @@ export function verifyJWTRoles(roles: string[]) {

export function setRequestUser(field: string) {
return async (context: HookContext) => {
const authHeader = context.params.headers?.authorization as string;
if (!authHeader) {
const token = extractIdToken(
context.params.headers?.authorization as string
);
if (!token) {
throw new Forbidden("The authorization header is missing");
}
const token = authHeader.split(" ")[1];

const decoded = decode(token) as {
[key: string]: any;
};
Expand All @@ -112,36 +95,65 @@ export function setRequestUser(field: string) {

export async function validateCredentialRequest(context: HookContext) {
const dbClient = (await context.app.get("mongoClient")) as Db;
const authHeader = context.params.headers?.authorization as string;
const idToken = authHeader.split(" ")[1];
const idToken = extractIdToken(
context.params.headers?.authorization as string
);
const keys = await getAuthSigningKeys(context);

const decoded = verifyIdToken(idToken, keys, context);
const inviteToken = await dbClient
.collection("issuer-invite")
.findOne({ token: context.data.token });
const requestedSchema = context.app
.get("public-schemas")
.get(context.data.schema_id || "default");

if (!decoded && !inviteToken && !requestedSchema) {
throw new Forbidden(
"The requested action could not be completed. Please check your settings and ensure you are either authenticated or requesting a public schema."
);
}
return context;
}

async function getAuthSigningKeys(context: HookContext): Promise<SigningKey[]> {
if (!context.app.get("authentication")) {
logger.debug(
"The [authentication] section is missing in the configuration"
);
return [] as SigningKey[];
}
// fetch public key from JWKS url and verify token
const jwksOptions = {
jwksUri: context.app.get("authentication").jwksUri,
} as ClientOptions;
const oidcClient = jwks(jwksOptions) as JwksClient;
const keys = (await oidcClient.getSigningKeysAsync()) as SigningKey[];
return (await oidcClient.getSigningKeysAsync()) as SigningKey[];
}

function verifyIdToken(
idToken: string | undefined,
keys: SigningKey[],
context: HookContext
): string | undefined | object {
if (!idToken || !context.app.get("authentication") || keys.length === 0) {
return undefined;
}
let decoded;
try {
decoded = verify(idToken, keys[0].getPublicKey(), {
algorithms: context.app.get("authentication").algorithms,
});
} catch (error) {
logger.error(error);
decoded = undefined;
}
return decoded;
}

const inviteToken = await dbClient
.collection("issuer-invite")
.findOne({ token: context.data.token });
const requestedSchema = context.app
.get("public-schemas")
.get(context.data.schema_id || "default");

if (!decoded && !inviteToken && !requestedSchema) {
throw new Forbidden(
"The requested action could not be completed. Please check your settings and ensure you are either authenticated or requesting a public schema."
);
function extractIdToken(authHeader: string | undefined): string | undefined {
if (!authHeader || authHeader.split(" ").length === 1) {
return undefined;
}
return context;
return authHeader.split(" ")[1];
}

0 comments on commit 0c8a5cd

Please sign in to comment.