mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-31 15:09:39 +02:00
add validate callback loading state and encryption
This commit is contained in:
parent
53be2739bb
commit
aa3b527f67
11 changed files with 155 additions and 22 deletions
|
@ -92,7 +92,18 @@ const configSchema = z.object({
|
|||
})
|
||||
.optional(),
|
||||
trust_proxy: z.boolean().optional().default(true),
|
||||
secret: z.string()
|
||||
secret: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform(getEnvOrYaml("SERVER_SECRET"))
|
||||
.pipe(
|
||||
z
|
||||
.string()
|
||||
.min(
|
||||
32,
|
||||
"SERVER_SECRET must be at least 32 characters long"
|
||||
)
|
||||
)
|
||||
}),
|
||||
traefik: z.object({
|
||||
http_entrypoint: z.string(),
|
||||
|
|
37
server/lib/crypto.ts
Normal file
37
server/lib/crypto.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import * as crypto from "crypto";
|
||||
|
||||
const ALGORITHM = "aes-256-gcm";
|
||||
|
||||
export function encrypt(value: string, key: string): string {
|
||||
const iv = crypto.randomBytes(12);
|
||||
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
|
||||
|
||||
const encrypted = Buffer.concat([
|
||||
cipher.update(value, "utf8"),
|
||||
cipher.final()
|
||||
]);
|
||||
const authTag = cipher.getAuthTag();
|
||||
|
||||
return [
|
||||
iv.toString("base64"),
|
||||
encrypted.toString("base64"),
|
||||
authTag.toString("base64")
|
||||
].join(":");
|
||||
}
|
||||
|
||||
export function decrypt(encryptedValue: string, key: string): string {
|
||||
const [ivB64, encryptedB64, authTagB64] = encryptedValue.split(":");
|
||||
|
||||
const iv = Buffer.from(ivB64, "base64");
|
||||
const encrypted = Buffer.from(encryptedB64, "base64");
|
||||
const authTag = Buffer.from(authTagB64, "base64");
|
||||
|
||||
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
|
||||
decipher.setAuthTag(authTag);
|
||||
|
||||
const decrypted = Buffer.concat([
|
||||
decipher.update(encrypted),
|
||||
decipher.final()
|
||||
]);
|
||||
return decrypted.toString("utf8");
|
||||
}
|
|
@ -9,6 +9,8 @@ import { fromError } from "zod-validation-error";
|
|||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { idp, idpOidcConfig, idpOrg, orgs } from "@server/db/schemas";
|
||||
import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl";
|
||||
import { encrypt } from "@server/lib/crypto";
|
||||
import config from "@server/lib/config";
|
||||
|
||||
const paramsSchema = z.object({}).strict();
|
||||
|
||||
|
@ -22,7 +24,8 @@ const bodySchema = z
|
|||
identifierPath: z.string().nonempty(),
|
||||
emailPath: z.string().optional(),
|
||||
namePath: z.string().optional(),
|
||||
scopes: z.array(z.string().nonempty())
|
||||
scopes: z.array(z.string().nonempty()),
|
||||
autoProvision: z.boolean().optional()
|
||||
})
|
||||
.strict();
|
||||
|
||||
|
@ -73,9 +76,15 @@ export async function createOidcIdp(
|
|||
identifierPath,
|
||||
emailPath,
|
||||
namePath,
|
||||
name
|
||||
name,
|
||||
autoProvision
|
||||
} = parsedBody.data;
|
||||
|
||||
const key = config.getRawConfig().server.secret;
|
||||
|
||||
const encryptedSecret = encrypt(clientSecret, key);
|
||||
const encryptedClientId = encrypt(clientId, key);
|
||||
|
||||
let idpId: number | undefined;
|
||||
await db.transaction(async (trx) => {
|
||||
const [idpRes] = await trx
|
||||
|
@ -90,11 +99,11 @@ export async function createOidcIdp(
|
|||
|
||||
await trx.insert(idpOidcConfig).values({
|
||||
idpId: idpRes.idpId,
|
||||
clientId,
|
||||
clientSecret,
|
||||
clientId: encryptedClientId,
|
||||
clientSecret: encryptedSecret,
|
||||
authUrl,
|
||||
tokenUrl,
|
||||
autoProvision: true,
|
||||
autoProvision,
|
||||
scopes: JSON.stringify(scopes),
|
||||
identifierPath,
|
||||
emailPath,
|
||||
|
|
|
@ -13,6 +13,7 @@ import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl";
|
|||
import cookie from "cookie";
|
||||
import jsonwebtoken from "jsonwebtoken";
|
||||
import config from "@server/lib/config";
|
||||
import { decrypt } from "@server/lib/crypto";
|
||||
|
||||
const paramsSchema = z
|
||||
.object({
|
||||
|
@ -77,10 +78,21 @@ export async function generateOidcUrl(
|
|||
|
||||
const parsedScopes = JSON.parse(existingIdp.idpOidcConfig.scopes);
|
||||
|
||||
const key = config.getRawConfig().server.secret;
|
||||
|
||||
const decryptedClientId = decrypt(
|
||||
existingIdp.idpOidcConfig.clientId,
|
||||
key
|
||||
);
|
||||
const decryptedClientSecret = decrypt(
|
||||
existingIdp.idpOidcConfig.clientSecret,
|
||||
key
|
||||
);
|
||||
|
||||
const redirectUrl = generateOidcRedirectUrl(idpId);
|
||||
const client = new arctic.OAuth2Client(
|
||||
existingIdp.idpOidcConfig.clientId,
|
||||
existingIdp.idpOidcConfig.clientSecret,
|
||||
decryptedClientId,
|
||||
decryptedClientSecret,
|
||||
redirectUrl
|
||||
);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import {
|
|||
generateSessionToken,
|
||||
serializeSessionCookie
|
||||
} from "@server/auth/sessions/app";
|
||||
import { decrypt } from "@server/lib/crypto";
|
||||
|
||||
const paramsSchema = z
|
||||
.object({
|
||||
|
@ -90,10 +91,21 @@ export async function validateOidcCallback(
|
|||
);
|
||||
}
|
||||
|
||||
const key = config.getRawConfig().server.secret;
|
||||
|
||||
const decryptedClientId = decrypt(
|
||||
existingIdp.idpOidcConfig.clientId,
|
||||
key
|
||||
);
|
||||
const decryptedClientSecret = decrypt(
|
||||
existingIdp.idpOidcConfig.clientSecret,
|
||||
key
|
||||
);
|
||||
|
||||
const redirectUrl = generateOidcRedirectUrl(existingIdp.idp.idpId);
|
||||
const client = new arctic.OAuth2Client(
|
||||
existingIdp.idpOidcConfig.clientId,
|
||||
existingIdp.idpOidcConfig.clientSecret,
|
||||
decryptedClientId,
|
||||
decryptedClientSecret,
|
||||
redirectUrl
|
||||
);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue