mirror of https://github.com/boxyhq/jackson.git
Support adding own certs (#715)
* Support adding own cert * Update the env and decode the keys before using it * Drop the JACKSON_ prefix * Tweaks to the getDefaultCertificate * Remove the console.log
This commit is contained in:
parent
8a0a9bf3c5
commit
cae8741307
|
@ -48,4 +48,11 @@ OPENID_JWS_ALG=
|
|||
OPENID_RSA_PRIVATE_KEY=
|
||||
# openssl rsa -in private_key.pem -pubout -out public_key.pem
|
||||
# cat public_key.pem | base64
|
||||
OPENID_RSA_PUBLIC_KEY=
|
||||
OPENID_RSA_PUBLIC_KEY=
|
||||
|
||||
# You can use `openssl req -x509 -newkey rsa:2048 -keyout key.pem -out public.crt -sha256 -days 365000 -nodes` to generate one
|
||||
# Base64 encoded value of public key `cat public.crt | base64`
|
||||
PUBLIC_KEY=
|
||||
|
||||
# Base64 encoded value of private key `cat key.pem | base64`
|
||||
PRIVATE_KEY=
|
|
@ -1,11 +1,11 @@
|
|||
import env from '@lib/env';
|
||||
import { apiKeys } from '@lib/env';
|
||||
|
||||
export const validateApiKey = (token: string | null) => {
|
||||
if (!token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return env.apiKeys.includes(token);
|
||||
return apiKeys.includes(token);
|
||||
};
|
||||
|
||||
export const extractAuthToken = (req): string | null => {
|
||||
|
|
14
lib/env.ts
14
lib/env.ts
|
@ -1,4 +1,4 @@
|
|||
import { DatabaseEngine, DatabaseType } from '@boxyhq/saml-jackson';
|
||||
import type { DatabaseEngine, DatabaseType, JacksonOption } from '@boxyhq/saml-jackson';
|
||||
|
||||
const hostUrl = process.env.HOST_URL || 'localhost';
|
||||
const hostPort = Number(process.env.PORT || '5225');
|
||||
|
@ -40,18 +40,22 @@ const jwtSigningKeys = {
|
|||
};
|
||||
const openid = { jwsAlg, jwtSigningKeys };
|
||||
|
||||
export default {
|
||||
hostUrl,
|
||||
hostPort,
|
||||
const jacksonOptions: JacksonOption = {
|
||||
externalUrl,
|
||||
samlPath,
|
||||
oidcPath,
|
||||
idpDiscoveryPath,
|
||||
samlAudience,
|
||||
preLoadedConnection,
|
||||
apiKeys,
|
||||
idpEnabled,
|
||||
db,
|
||||
clientSecretVerifier,
|
||||
openid,
|
||||
certs: {
|
||||
publicKey: process.env.PUBLIC_KEY || '',
|
||||
privateKey: process.env.PRIVATE_KEY || '',
|
||||
},
|
||||
};
|
||||
|
||||
export { apiKeys };
|
||||
export { jacksonOptions };
|
||||
|
|
|
@ -22,7 +22,7 @@ import type {
|
|||
} from '@boxyhq/saml-jackson';
|
||||
|
||||
import jackson from '@boxyhq/saml-jackson';
|
||||
import env from '@lib/env';
|
||||
import { jacksonOptions } from '@lib/env';
|
||||
import '@lib/metrics';
|
||||
|
||||
let connectionAPIController: IConnectionAPIController;
|
||||
|
@ -47,7 +47,7 @@ export default async function init() {
|
|||
!g.oidcDiscoveryController ||
|
||||
!g.spConfig
|
||||
) {
|
||||
const ret = await jackson(env);
|
||||
const ret = await jackson(jacksonOptions);
|
||||
connectionAPIController = ret.connectionAPIController;
|
||||
oauthController = ret.oauthController;
|
||||
adminController = ret.adminController;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Storable } from '@boxyhq/saml-jackson';
|
||||
import DB from 'npm/src/db/db';
|
||||
import opts from './env';
|
||||
import { jacksonOptions } from './env';
|
||||
import type { AdapterUser, VerificationToken } from 'next-auth/adapters';
|
||||
import { validateEmailWithACL } from './utils';
|
||||
import defaultDb from 'npm/src/db/defaultDb';
|
||||
|
@ -9,7 +9,7 @@ const g = global as any;
|
|||
|
||||
export async function initNextAuthDB(): Promise<Storable> {
|
||||
if (!g.adminAuthStore) {
|
||||
const _opts = defaultDb(opts);
|
||||
const _opts = defaultDb(jacksonOptions);
|
||||
const db = await DB.new(_opts.db);
|
||||
g.adminAuthStore = db.store('admin:auth');
|
||||
}
|
||||
|
|
|
@ -3,10 +3,11 @@ import { marked } from 'marked';
|
|||
|
||||
import saml20 from '@boxyhq/saml20';
|
||||
import xmlbuilder from 'xmlbuilder';
|
||||
import { getDefaultCertificate } from '../saml/x509';
|
||||
|
||||
// Service Provider SAML Configuration
|
||||
export class SPSAMLConfig {
|
||||
constructor(private opts: JacksonOption, private getDefaultCertificate: any) {}
|
||||
constructor(private opts: JacksonOption) {}
|
||||
|
||||
private get acsUrl(): string {
|
||||
return `${this.opts.externalUrl}${this.opts.samlPath}`;
|
||||
|
@ -37,7 +38,7 @@ export class SPSAMLConfig {
|
|||
publicKey: string;
|
||||
publicKeyString: string;
|
||||
}> {
|
||||
const cert = await this.getDefaultCertificate();
|
||||
const cert = await getDefaultCertificate();
|
||||
|
||||
return {
|
||||
acsUrl: this.acsUrl,
|
||||
|
|
|
@ -80,7 +80,7 @@ export const controllers = async (
|
|||
await healthCheckController.init();
|
||||
|
||||
// Create default certificate if it doesn't exist.
|
||||
await x509.init(certificateStore);
|
||||
await x509.init(certificateStore, opts);
|
||||
|
||||
const oauthController = new OAuthController({
|
||||
connectionStore,
|
||||
|
@ -100,7 +100,7 @@ export const controllers = async (
|
|||
|
||||
const oidcDiscoveryController = new OidcDiscoveryController({ opts });
|
||||
|
||||
const spConfig = new SPSAMLConfig(opts, x509.getDefaultCertificate);
|
||||
const spConfig = new SPSAMLConfig(opts);
|
||||
|
||||
// write pre-loaded connections if present
|
||||
const preLoadedConnection = opts.preLoadedConnection || opts.preLoadedConfig;
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import * as forge from 'node-forge';
|
||||
import crypto from 'crypto';
|
||||
|
||||
import type { Storable } from '../typings';
|
||||
import type { JacksonOption, Storable } from '../typings';
|
||||
|
||||
const pki = forge.pki;
|
||||
let certificateStore: Storable;
|
||||
let cachedCertificate: { publicKey: string; privateKey: string };
|
||||
let jacksonOption: JacksonOption;
|
||||
|
||||
export const init = async (store: Storable) => {
|
||||
export const init = async (store: Storable, opts: JacksonOption) => {
|
||||
certificateStore = store;
|
||||
jacksonOption = opts;
|
||||
|
||||
return await getDefaultCertificate();
|
||||
};
|
||||
|
@ -65,6 +67,22 @@ export const getDefaultCertificate = async (): Promise<{ publicKey: string; priv
|
|||
throw new Error('Certificate store not initialized');
|
||||
}
|
||||
|
||||
if (!jacksonOption) {
|
||||
throw new Error('Jackson option not initialized');
|
||||
}
|
||||
|
||||
// If the user has provided a certificate, use that instead of the default.
|
||||
// We expect the developer to provide base64 encoded keys, so we need to decode them.
|
||||
if (jacksonOption.certs?.privateKey && jacksonOption.certs?.publicKey) {
|
||||
cachedCertificate = {
|
||||
publicKey: Buffer.from(jacksonOption.certs.publicKey, 'base64').toString('utf-8'),
|
||||
privateKey: Buffer.from(jacksonOption.certs.privateKey, 'base64').toString('utf-8'),
|
||||
};
|
||||
|
||||
return cachedCertificate;
|
||||
}
|
||||
|
||||
// Otherwise, use the default certificate.
|
||||
cachedCertificate = await certificateStore.get('default');
|
||||
|
||||
// If certificate is expired let it drop through so it creates a new cert
|
||||
|
|
|
@ -323,6 +323,10 @@ export interface JacksonOption {
|
|||
public: string;
|
||||
};
|
||||
};
|
||||
certs?: {
|
||||
publicKey: string;
|
||||
privateKey: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SLORequestParams {
|
||||
|
|
Loading…
Reference in New Issue