Skip to content

Commit

Permalink
Dynamically inject any OAuth client secret from environment
Browse files Browse the repository at this point in the history
When deployment occurs, any env variable ending with _OAUTH_CLIENT_SECRET
is inserted into the Keycloak configuration task environment.
  • Loading branch information
alukach committed Oct 29, 2024
1 parent b9f1c3a commit bf7d356
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 19 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ jobs:
env:
AWS_ACCOUNT_ID: ${{ vars.AWS_ACCOUNT_ID }}
AWS_REGION: ${{ vars.AWS_REGION }}
GH_OAUTH_CLIENT_SECRET: ${{ vars.GH_OAUTH_CLIENT_SECRET }}
HOSTNAME: ${{ vars.HOSTNAME }}
KEYCLOAK_VERSION: ${{ vars.KEYCLOAK_VERSION }}
SSL_CERTIFICATE_ARN: ${{ vars.SSL_CERTIFICATE_ARN }}
STAGE: ${{ vars.STAGE }}
# OAuth2 client secret arns
GH_OAUTH_CLIENT_SECRET: ${{ vars.GH_OAUTH_CLIENT_SECRET }}
CILOGON_OAUTH_CLIENT_SECRET: ${{ vars.CILOGON_OAUTH_CLIENT_SECRET }}

- name: Get ConfigLambdaArn from CloudFormation
id: get-lambda-arn
Expand Down
25 changes: 22 additions & 3 deletions deploy/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,19 @@ const {
HOSTNAME,
STAGE = "dev",
KEYCLOAK_VERSION = "26.0.0",
GH_OAUTH_CLIENT_SECRET,
} = process.env;

assert(SSL_CERTIFICATE_ARN, "SSL_CERTIFICATE_ARN env var is required");
assert(HOSTNAME, "HOSTNAME env var is required");
assert(GH_OAUTH_CLIENT_SECRET, "GH_OAUTH_CLIENT_SECRET env var is required");

const oAuthClientSecrets = getOauthSecrets();
console.log(
`Found ${
Object.keys(oAuthClientSecrets).length
} OAuth client secrets for clients: ${Object.keys(oAuthClientSecrets).join(
", "
)}`
);

const app = new cdk.App();
new KeycloakStack(app, `VedaKeycloakStack-${STAGE}`, {
Expand All @@ -35,5 +42,17 @@ new KeycloakStack(app, `VedaKeycloakStack-${STAGE}`, {
hostname: HOSTNAME,
keycloakVersion: KEYCLOAK_VERSION,
configDir: join(__dirname, "..", "config"),
ghOauthClientSecret: GH_OAUTH_CLIENT_SECRET,
oAuthClientSecrets,
});

/**
* Helper function to extract OAuth client secrets from the runtime environment
* @returns Record<string, string> - A map of OAuth client IDs to the ARN of their secrets
*/
function getOauthSecrets(): Record<string, string> {
const oauthSecretSuffix = "_OAUTH_CLIENT_SECRET";
const clientSecrets = Object.entries(process.env)
.filter(([k, v]) => k.endsWith(oauthSecretSuffix))
.map(([k, v]) => [k.split(oauthSecretSuffix)[0], v]);
return Object.fromEntries(clientSecrets);
}
32 changes: 19 additions & 13 deletions deploy/lib/KeycloakConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface KeycloakConfigConstructProps {
adminSecret: secretsManager.ISecret;
hostname: string;
configDir: string;
githubOauthClientSecret: string;
oAuthClientSecrets: Record<string, string>;
}

export class KeycloakConfig extends Construct {
Expand All @@ -33,10 +33,22 @@ export class KeycloakConfig extends Construct {
platform: ecrAssets.Platform.LINUX_AMD64,
});

const githubOauthClientSecret = secretsManager.Secret.fromSecretCompleteArn(
this,
"GithubClientSecret",
props.githubOauthClientSecret
const clientSecrets = Object.fromEntries(
Object.entries(props.oAuthClientSecrets)
.map(([clientSlug, secretArn]): [string, secretsManager.ISecret] => [
clientSlug,
secretsManager.Secret.fromSecretCompleteArn(
this,
`${clientSlug}-client-secret`,
secretArn
),
])
.flatMap(([clientSlug, secret]) =>
["id", "secret"].map((key) => [
`${clientSlug}_client_${key}`.toUpperCase(),
ecs.Secret.fromSecretsManager(secret, key),
])
)
);

configTaskDef.addContainer("ConfigContainer", {
Expand All @@ -59,14 +71,8 @@ export class KeycloakConfig extends Construct {
props.adminSecret,
"password"
),
GH_CLIENT_ID: ecs.Secret.fromSecretsManager(
githubOauthClientSecret,
"id"
),
GH_CLIENT_SECRET: ecs.Secret.fromSecretsManager(
githubOauthClientSecret,
"secret"
),
// Inject the client ID and secret for any OAuth clients
...clientSecrets,
},
});

Expand Down
4 changes: 2 additions & 2 deletions deploy/lib/KeycloakStack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface StackInputProps {
sslCertificateArn: string;
keycloakVersion: string;
configDir: string;
ghOauthClientSecret: string;
oAuthClientSecrets: Record<string, string>;
}

interface StackProps extends cdk.StackProps, StackInputProps {
Expand Down Expand Up @@ -46,7 +46,7 @@ export class KeycloakStack extends cdk.Stack {
hostname: props.hostname,
subnetIds: vpc.publicSubnets.map((subnet) => subnet.subnetId),
adminSecret: adminSecret,
githubOauthClientSecret: props.ghOauthClientSecret,
oAuthClientSecrets: props.oAuthClientSecrets,
configDir: props.configDir,
});

Expand Down

0 comments on commit bf7d356

Please sign in to comment.