feat(infisical-pg): auth injection completed and validation in password router

This commit is contained in:
Akhil Mohan
2024-01-27 12:38:22 +05:30
parent fcfd6b3fb2
commit f3b37de3f3
196 changed files with 1871 additions and 624 deletions
@@ -0,0 +1,75 @@
import { FastifyRequest } from "fastify";
import jwt, { JwtPayload } from "jsonwebtoken";
import { getConfig } from "@app/lib/config/env";
import { UnauthorizedError } from "@app/lib/errors";
import { AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
const extractAuth = async (req: FastifyRequest, jwtSecret: string) => {
const apiKey = req.headers?.["x-api-key"];
if (apiKey) {
return { authMode: AuthMode.API_KEY, token: apiKey };
}
const authHeader = req.headers?.authorization;
if (!authHeader) return { authMode: null, token: null };
const authTokenValue = authHeader.slice(7); // slice of after Bearer
if (authTokenValue.startsWith("st.")) {
return { authMode: AuthMode.SERVICE_TOKEN, token: authTokenValue } as const;
}
const decodedToken = jwt.verify(authTokenValue, jwtSecret) as JwtPayload;
switch (decodedToken.authTokenType) {
case AuthTokenType.ACCESS_TOKEN:
return { authMode: AuthMode.JWT, token: decodedToken as AuthModeJwtTokenPayload } as const;
case AuthTokenType.API_KEY:
return { authMode: AuthMode.API_KEY_V2, token: decodedToken } as const;
case AuthMode.SERVICE_ACCESS_TOKEN:
return { authMode: AuthMode.SERVICE_ACCESS_TOKEN, token: decodedToken } as const;
default:
throw new UnauthorizedError({ name: "Invalid token type" });
}
};
const getJwtIdentity = async (server: FastifyZodProvider, token: AuthModeJwtTokenPayload) => {
const session = await server.services.authToken.getUserTokenSessionById(
token.tokenVersionId,
token.userId
);
if (!session) throw new UnauthorizedError({ name: "Session not found" });
if (token.accessVersion !== session.accessVersion)
throw new UnauthorizedError({ name: "Stale session" });
const user = await server.store.user.getUserById(session.userId);
if (!user || !user.isAccepted) throw new UnauthorizedError({ name: "Token user not found" });
return user;
};
export const injectIdentity = (server: FastifyZodProvider) => {
server.decorateRequest("auth", null);
server.addHook("onRequest", async (req) => {
const appCfg = getConfig();
const { authMode, token } = await extractAuth(req, appCfg.JWT_AUTH_SECRET);
if (!authMode) return;
// TODO(akhilmhdh-pg): fill in rest of auth mode logic
switch (authMode) {
case AuthMode.JWT: {
const user = await getJwtIdentity(server, token as AuthModeJwtTokenPayload);
req.auth = { authMode: AuthMode.JWT, user, userId: user.id };
break;
}
case AuthMode.SERVICE_TOKEN:
break;
case AuthMode.SERVICE_ACCESS_TOKEN:
break;
case AuthMode.API_KEY:
break;
case AuthMode.API_KEY_V2:
break;
default:
throw new UnauthorizedError({ name: "Unknown token strategy" });
}
});
};
@@ -0,0 +1,17 @@
import { FastifyRequest } from "fastify";
import { UnauthorizedError } from "@app/lib/errors";
import { AuthMode } from "@app/services/auth/auth-type";
export const verifyAuth =
<T extends FastifyRequest>(authStrats: AuthMode[]) =>
(req: T) => {
if (!Array.isArray(authStrats)) throw new Error("Auth strategy must be array");
if (!req.auth)
throw new UnauthorizedError({ name: "Unauthorized access", message: "Token missing" });
const isAccessAllowed = authStrats.some((strat) => strat === req.auth.authMode);
if (!isAccessAllowed) {
throw new UnauthorizedError({ name: `${req.url} Unauthorized Access` });
}
};
+3
View File
@@ -11,6 +11,7 @@ import { tokenServiceFactory } from "@app/services/token/token-service";
import { registerV1Routes } from "./v1";
import { registerV2Routes } from "./v2";
import { registerV3Routes } from "./v3";
import { injectIdentity } from "../plugins/auth/inject-identity";
export const registerRoutes = async (
server: FastifyZodProvider,
@@ -37,6 +38,8 @@ export const registerRoutes = async (
user: authDal
} as FastifyZodProvider["store"]);
await server.register(injectIdentity);
// register routes for v1
await server.register(registerV1Routes, { prefix: "/v1" });
await server.register(registerV2Routes, { prefix: "/v2" });
@@ -2,6 +2,8 @@ import { z } from "zod";
import { BackupPrivateKeySchema } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerPasswordRouter = async (server: FastifyZodProvider) => {
server.route({
@@ -18,6 +20,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { salt, serverPublicKey } = await server.services.password.generateServerPubKey(
req.auth.userId,
@@ -48,6 +51,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req, res) => {
const appCfg = getConfig();
await server.services.password.changePassword({ ...req.body, userId: req.auth.userId });
@@ -65,6 +69,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/backup-private-key",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
body: z.object({
clientProof: z.string().trim(),
@@ -95,6 +100,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/backup-private-key",
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
response: {
200: z.object({
@@ -2,20 +2,24 @@ import jwt, { JwtPayload } from "jsonwebtoken";
import { z } from "zod";
import { getConfig } from "@app/lib/config/env";
import { AuthTokenType } from "@app/services/auth/auth-signup-type";
import { AuthTokenType } from "@app/services/auth/auth-type";
export const registerMfaRouter = async (server: FastifyZodProvider) => {
const cfg = getConfig();
server.decorateRequest("mfa", null);
server.addHook("preParsing", async (req, res) => {
const authorizationHeader = req.headers.authorization;
if (!authorizationHeader || !authorizationHeader.startsWith("Bearer ")) {
res.status(401).send({ error: "Missing bearer token" });
return;
return res;
}
const token = authorizationHeader.split(" ")[1];
if (!token) res.status(401).send({ error: "Missing bearer token" });
if (!token) {
res.status(401).send({ error: "Missing bearer token" });
return res;
}
const decodedToken = jwt.verify(token, cfg.JWT_AUTH_SECRET) as JwtPayload;
if (decodedToken.authTokenType !== AuthTokenType.MFA_TOKEN)
@@ -23,8 +27,7 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
const user = await server.store.user.getUserById(decodedToken.userId);
if (!user) throw new Error("User not found");
req.mfa.userId = user.id;
req.mfa.user = user;
req.mfa = { userId: user.id, user };
});
server.route({
@@ -52,7 +55,7 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
}),
response: {
200: z.object({
encryptionVersion: z.number().default(1).optional(),
encryptionVersion: z.number().default(1).nullable().optional(),
protectedKey: z.string(),
protectedKeyIV: z.string(),
protectedKeyTag: z.string(),
@@ -44,7 +44,7 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
z.object({ mfaEnabled: z.literal(true), token: z.string() }),
z.object({
mfaEnabled: z.literal(false),
encryptionVersion: z.number().default(1).optional(),
encryptionVersion: z.number().default(1).nullable().optional(),
protectedKey: z.string(),
protectedKeyIV: z.string(),
protectedKeyTag: z.string(),
@@ -1,6 +1,6 @@
import { z } from "zod";
import { UserSchema } from "@app/db/schemas";
import { UsersSchema } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
export const registerSignupRouter = async (server: FastifyZodProvider) => {
@@ -35,7 +35,7 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
200: z.object({
message: z.string(),
token: z.string(),
user: UserSchema
user: UsersSchema
})
}
},
@@ -72,7 +72,7 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
response: {
200: z.object({
message: z.string(),
user: UserSchema,
user: UsersSchema,
token: z.string()
})
}