Skip to content

Commit

Permalink
Remove CAM token from JWT
Browse files Browse the repository at this point in the history
* Remove CAM token check from session endpoint
* Remove CAM user profile call from user endpoint, and just get user from the JWT
* Remove logout endpoint, function, and type
* Remove unused res.locals.username settings in auth middleware
  • Loading branch information
camargo committed Sep 12, 2023
1 parent ed97853 commit fa6b606
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 211 deletions.
166 changes: 12 additions & 154 deletions src/packages/auth/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,7 @@ import fetch from 'node-fetch';
import { getEnv } from '../../env.js';
import getLogger from '../../logger.js';
import { DbMerlin } from '../db/db.js';
import type {
AuthResponse,
JsonWebToken,
JwtDecode,
JwtPayload,
JwtSecret,
LogoutResponse,
SessionResponse,
UserResponse,
} from './types.js';
import type { AuthResponse, JsonWebToken, JwtDecode, JwtPayload, JwtSecret, SessionResponse } from './types.js';

const logger = getLogger('packages/auth/functions');

Expand Down Expand Up @@ -102,7 +93,6 @@ export function decodeJwt(authorizationHeader: string | undefined): JwtDecode {

export function generateJwt(
username: string,
camToken: string,
defaultRole: string,
allowedRoles: string[],
activeRole?: string,
Expand All @@ -113,7 +103,6 @@ export function generateJwt(
const options: jwt.SignOptions = { algorithm: type as Algorithm, expiresIn: JWT_EXPIRATION };
const payload: JwtPayload = {
activeRole: activeRole && allowedRoles.includes(activeRole) ? activeRole : defaultRole,
camToken,
'https://hasura.io/jwt/claims': {
'x-hasura-allowed-roles': allowedRoles,
'x-hasura-default-role': defaultRole,
Expand Down Expand Up @@ -151,12 +140,11 @@ export async function login(username: string, password: string): Promise<AuthRes
token: null,
};
} else {
const { ssoCookieValue: camToken } = json;
const { allowed_roles, default_role } = await getUserRoles(username, DEFAULT_ROLE, ALLOWED_ROLES);
return {
message: 'Login successful',
success: true,
token: generateJwt(username, camToken, default_role, allowed_roles),
token: generateJwt(username, default_role, allowed_roles),
};
}
} catch (error) {
Expand All @@ -174,192 +162,62 @@ export async function login(username: string, password: string): Promise<AuthRes
return {
message: 'Authentication is disabled',
success: true,
token: generateJwt(username, '', default_role, allowed_roles),
token: generateJwt(username, default_role, allowed_roles),
};
}
}

export async function logout(authorizationHeader: string | undefined): Promise<LogoutResponse> {
const { AUTH_TYPE, AUTH_URL } = getEnv();

if (AUTH_TYPE === 'cam') {
let response: Response | undefined;
let json: any;

try {
const { jwtErrorMessage, jwtPayload } = decodeJwt(authorizationHeader);

if (jwtPayload) {
const { camToken } = jwtPayload;
const body = JSON.stringify({ ssoToken: camToken });
const url = `${AUTH_URL}/ssoToken?action=invalidate`;
response = await fetch(url, { body, method: 'DELETE' });
json = await response.json();
const { errorCode = false } = json;

if (errorCode) {
const { errorMessage } = json;
return { message: errorMessage, success: false };
} else {
return { message: 'Logout successful', success: true };
}
} else {
return { message: jwtErrorMessage, success: false };
}
} catch (error) {
logger.error(error);
logger.error(response);
logger.error(json);
return { message: 'An unexpected error occurred', success: false };
}
} else {
return { message: 'Authentication is disabled', success: true };
}
}

export async function session(authorizationHeader: string | undefined): Promise<SessionResponse> {
const { AUTH_TYPE, AUTH_URL } = getEnv();
const { AUTH_TYPE } = getEnv();

if (AUTH_TYPE === 'cam') {
let response: Response | undefined;
let json: any;

try {
const { jwtErrorMessage, jwtPayload } = decodeJwt(authorizationHeader);
const { jwtErrorMessage, jwtPayload } = decodeJwt(authorizationHeader);

if (jwtPayload) {
const { camToken } = jwtPayload;

const body = JSON.stringify({ ssoToken: camToken });
const url = `${AUTH_URL}/ssoToken?action=validate`;
response = await fetch(url, { body, method: 'POST' });
json = await response.json();
const { errorCode = false } = json;

if (errorCode) {
const { errorMessage } = json;
return { message: errorMessage, success: false };
} else {
const { validated } = json;
const valid = validated ? 'valid' : 'invalid';
const message = `Token is ${valid}`;
return { message, success: validated };
}
} else {
return { message: jwtErrorMessage, success: false };
}
} catch (error) {
logger.error(error);
logger.error(response);
logger.error(json);
return { message: 'An unexpected error occurred', success: false };
if (jwtPayload) {
return { message: 'Token is valid', success: true };
} else {
return { message: jwtErrorMessage, success: false };
}
} else {
return { message: `Authentication is disabled`, success: true };
}
}

export async function user(authorizationHeader: string | undefined): Promise<UserResponse> {
const { AUTH_TYPE, AUTH_URL } = getEnv();

if (AUTH_TYPE === 'cam') {
let response: Response | undefined;
let json: any;

try {
const { jwtErrorMessage, jwtPayload } = decodeJwt(authorizationHeader);

if (jwtPayload) {
const { camToken } = jwtPayload;
const body = JSON.stringify({ ssoToken: camToken });
const url = `${AUTH_URL}/userProfile`;
response = await fetch(url, { body, method: 'POST' });
json = await response.json();
const { errorCode = false } = json;

if (errorCode) {
const { errorMessage } = json;
return { message: errorMessage, success: false, user: null };
} else {
const { filteredGroupList, fullName, groupList, userId } = json;
return {
message: 'User found',
success: true,
user: {
filteredGroupList,
fullName,
groupList,
userId,
},
};
}
} else {
return { message: jwtErrorMessage, success: false, user: null };
}
} catch (error) {
logger.error(error);
logger.error(response);
logger.error(json);
return {
message: 'An unexpected error occurred',
success: false,
user: null,
};
}
} else {
return {
message: `Authentication is disabled`,
success: true,
user: {
filteredGroupList: [],
fullName: 'unknown',
groupList: [],
userId: 'unknown',
},
};
}
}

export async function changeRole(
authorizationHeader: string | undefined,
role: string | undefined,
): Promise<AuthResponse> {
const { AUTH_TYPE } = getEnv();
const { jwtErrorMessage, jwtPayload } = decodeJwt(authorizationHeader);

let response: Response | undefined;
let json: any;

try {
if (jwtPayload) {
const {
camToken,
username,
'https://hasura.io/jwt/claims': {
'x-hasura-allowed-roles': allowedRoles,
'x-hasura-default-role': defaultRole,
},
} = jwtPayload;

if (AUTH_TYPE === 'cam') {
return {
message: 'Role change successful',
success: true,
token: generateJwt(username, camToken, defaultRole as string, allowedRoles as string[], role),
token: generateJwt(username, defaultRole as string, allowedRoles as string[], role),
};
} else {
return {
message: 'Authentication is disabled',
success: true,
token: generateJwt(username || 'unknown', '', defaultRole as string, allowedRoles as string[], role),
token: generateJwt(username, defaultRole as string, allowedRoles as string[], role),
};
}
} else {
return { message: jwtErrorMessage, success: false, token: null };
}
} catch (error) {
logger.error(error);
logger.error(response);
logger.error(json);
return {
message: 'An unexpected error occurred',
success: false,
Expand Down
6 changes: 2 additions & 4 deletions src/packages/auth/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import type { NextFunction, Request, Response } from 'express';
import { getEnv } from '../../env.js';
import { user } from './functions.js';
import { session } from './functions.js';

export const auth = async (req: Request, res: Response, next: NextFunction) => {
const { AUTH_TYPE } = getEnv();

if (AUTH_TYPE === 'none') {
res.locals.username = 'unknown';
next();
} else {
const authorizationHeader = req.get('authorization');
const response = await user(authorizationHeader);
const response = await session(authorizationHeader);

if (response.success) {
res.locals.username = response.user?.userId;
next();
} else {
res.status(401).send({ message: 'Unauthorized', success: false });
Expand Down
44 changes: 1 addition & 43 deletions src/packages/auth/routes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Express } from 'express';
import rateLimit from 'express-rate-limit';
import { getEnv } from '../../env.js';
import { changeRole, login, logout, session, user } from './functions.js';
import { changeRole, login, session } from './functions.js';

export default (app: Express) => {
const { RATE_LIMITER_LOGIN_MAX } = getEnv();
Expand Down Expand Up @@ -47,27 +47,6 @@ export default (app: Express) => {
res.json(response);
});

/**
* @swagger
* /auth/logout:
* delete:
* security:
* - bearerAuth: []
* produces:
* - application/json
* responses:
* 200:
* description: LogoutResponse
* summary: Logout to terminate a session
* tags:
* - Auth
*/
app.delete('/auth/logout', async (req, res) => {
const authorizationHeader = req.get('authorization');
const response = await logout(authorizationHeader);
res.json(response);
});

/**
* @swagger
* /auth/session:
Expand All @@ -89,27 +68,6 @@ export default (app: Express) => {
res.json(response);
});

/**
* @swagger
* /auth/user:
* get:
* security:
* - bearerAuth: []
* produces:
* - application/json
* responses:
* 200:
* description: UserResponse
* summary: Returns a session's user information
* tags:
* - Auth
*/
app.get('/auth/user', async (req, res) => {
const authorizationHeader = req.get('authorization');
const response = await user(authorizationHeader);
res.json(response);
});

/**
* @swagger
* /auth/changeRole:
Expand Down
11 changes: 1 addition & 10 deletions src/packages/auth/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export type JwtDecode = {

export type JwtPayload = {
activeRole: string;
camToken: string;
'https://hasura.io/jwt/claims': Record<string, string | string[]>;
username: string;
};
Expand All @@ -23,11 +22,6 @@ export type AuthResponse = {
token: JsonWebToken | null;
};

export type LogoutResponse = {
message: string;
success: boolean;
};

export type SessionResponse = {
message: string;
success: boolean;
Expand All @@ -40,8 +34,5 @@ export type UserResponse = {
};

export type User = {
filteredGroupList: string[];
fullName: string;
groupList: string[];
userId: string;
id: string;
};

0 comments on commit fa6b606

Please sign in to comment.