Skip to content

Commit 562857b

Browse files
committed
refactor: Use the Docker Compose Keycloak instance by default
Signed-off-by: Johanna Lamppu <[email protected]>
1 parent a7f624a commit 562857b

File tree

15 files changed

+72
-92
lines changed

15 files changed

+72
-92
lines changed

.env.example

+8-8
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,6 @@
1010
# Prisma doesn't currently support giving a default value in case DATABASE_URL is empty, see https://github.com/prisma/prisma/issues/222
1111
DATABASE_URL=
1212

13-
# Keycloak variables for the API and UI
14-
KEYCLOAK_URL=
15-
KEYCLOAK_REALM=
16-
KEYCLOAK_CLIENT_ID_API=
17-
KEYCLOAK_CLIENT_SECRET_API=
18-
KEYCLOAK_CLIENT_ID_UI=
19-
KEYCLOAK_CLIENT_SECRET_UI=
20-
2113
# API URL
2214
NEXT_PUBLIC_API_URL=http://localhost:5000/api/
2315

@@ -32,6 +24,14 @@ E2E_USER_PASSWORD=
3224
# Compulsory in production (aka when NODE_ENV=production):
3325
##########################################################################
3426

27+
# Keycloak variables for the API and UI
28+
KEYCLOAK_URL=
29+
KEYCLOAK_REALM=
30+
KEYCLOAK_CLIENT_ID_API=
31+
KEYCLOAK_CLIENT_SECRET_API=
32+
KEYCLOAK_CLIENT_ID_UI=
33+
KEYCLOAK_CLIENT_SECRET_UI=
34+
3535
# NEXTAUTH specific settings
3636
NEXTAUTH_URL= # http://localhost:3000
3737
NEXTAUTH_SECRET=

README.md

-6
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,6 @@ To run this project you will need Node.js, npm and Docker installed.
4242

4343
```shell
4444
DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres
45-
KEYCLOAK_URL=
46-
KEYCLOAK_REALM=
47-
KEYCLOAK_CLIENT_ID_API=
48-
KEYCLOAK_CLIENT_SECRET_API=
49-
KEYCLOAK_CLIENT_ID_UI=
50-
KEYCLOAK_CLIENT_SECRET_UI=
5145
E2E_USER_USERNAME=
5246
E2E_USER_PASSWORD=
5347
```

apps/api/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"adm-zip": "0.5.16",
1717
"braces": "^3.0.3",
1818
"bull": "4.16.5",
19+
"common-helpers": "*",
1920
"compression": "1.8.0",
2021
"connect-pg-simple": "10.0.0",
2122
"cookie-parser": "1.4.7",

apps/api/src/@types/express.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ declare namespace Express {
3131
roles: string[];
3232
};
3333
resource_access: {
34-
[process.env.KEYCLOAK_CLIENT_ID_API]: {
34+
[process.env.KEYCLOAK_CLIENT_ID_API ||
35+
"dos-dev-api"]: {
3536
roles: string[];
3637
};
3738
account: {

apps/api/src/config/keycloak.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//
33
// SPDX-License-Identifier: MIT
44

5+
import { authConfig } from "common-helpers";
56
import genFunc from "connect-pg-simple";
67
import type { Request, Response } from "express";
78
import session from "express-session";
@@ -20,9 +21,9 @@ const memoryStore = new PostgresqlStore({
2021
});
2122

2223
const keycloakConfig: KeycloakConfig = {
23-
realm: process.env.KEYCLOAK_REALM!,
24-
resource: process.env.KEYCLOAK_CLIENT_ID_API!,
25-
"auth-server-url": process.env.KEYCLOAK_URL!,
24+
realm: authConfig.realm,
25+
resource: authConfig.clientIdAPI,
26+
"auth-server-url": authConfig.url,
2627
"bearer-only": true,
2728
"confidential-port": 0,
2829
"ssl-required": "external",

apps/api/src/helpers/keycloak_queries.ts

+12-14
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@
44

55
import { Zodios, ZodiosResponseByAlias } from "@zodios/core";
66
import { isAxiosError } from "axios";
7+
import { authConfig } from "common-helpers";
78
import NodeCache from "node-cache";
89
import { keycloakAPI, type ClientCredentialsToken } from "validation-helpers";
910
import { CustomError } from "./custom_error";
1011

11-
const kcClient = new Zodios(
12-
process.env.KEYCLOAK_URL || "https://auth.dev.doubleopen.io/",
13-
keycloakAPI,
14-
);
12+
const kcClient = new Zodios(authConfig.url, keycloakAPI);
1513

1614
const cache = new NodeCache({ stdTTL: 5 * 60, checkperiod: 60 });
1715

@@ -34,16 +32,16 @@ export const getAccessToken = async (): Promise<ClientCredentialsToken> => {
3432
try {
3533
const accessToken = (await kcClient.PostToken(
3634
{
37-
client_id: process.env.KEYCLOAK_CLIENT_ID_API!,
35+
client_id: authConfig.clientIdAPI,
3836
grant_type: "client_credentials",
39-
client_secret: process.env.KEYCLOAK_CLIENT_SECRET_API!,
37+
client_secret: authConfig.clientSecretAPI,
4038
},
4139
{
4240
headers: {
4341
"Content-Type": "application/x-www-form-urlencoded",
4442
},
4543
params: {
46-
realm: process.env.KEYCLOAK_REALM!,
44+
realm: authConfig.realm,
4745
},
4846
},
4947
)) as ClientCredentialsToken; // The endpoint provides a union type, but we know it's a ClientCredentialsToken with this type of request
@@ -100,7 +98,7 @@ export const logoutUser = async (
10098
const token = await getAccessToken();
10199
await kcClient.LogoutUser(undefined, {
102100
params: {
103-
realm: process.env.KEYCLOAK_REALM!,
101+
realm: authConfig.realm,
104102
id: userId,
105103
},
106104
headers: {
@@ -173,7 +171,7 @@ export const createUser = async (data: {
173171
const token = await getAccessToken();
174172
await kcClient.CreateUser(data, {
175173
params: {
176-
realm: process.env.KEYCLOAK_REALM!,
174+
realm: authConfig.realm,
177175
},
178176
headers: {
179177
Authorization: "Bearer " + token.access_token,
@@ -239,7 +237,7 @@ export const deleteUser = async (userId: string): Promise<boolean> => {
239237
const token = await getAccessToken();
240238
await kcClient.DeleteUser(undefined, {
241239
params: {
242-
realm: process.env.KEYCLOAK_REALM!,
240+
realm: authConfig.realm,
243241
id: userId,
244242
},
245243
headers: {
@@ -294,7 +292,7 @@ export const getRealmRoles = async (): Promise<RealmRole[]> => {
294292
const token = await getAccessToken();
295293
roles = await kcClient.GetRealmRoles({
296294
params: {
297-
realm: process.env.KEYCLOAK_REALM!,
295+
realm: authConfig.realm,
298296
},
299297
headers: {
300298
Authorization: "Bearer " + token.access_token,
@@ -360,7 +358,7 @@ export const addRealmRolesToUser = async (
360358

361359
await kcClient.AddRealmRoleToUser(roles, {
362360
params: {
363-
realm: process.env.KEYCLOAK_REALM!,
361+
realm: authConfig.realm,
364362
id: userId,
365363
},
366364
headers: {
@@ -420,7 +418,7 @@ export const getUsers = async (
420418
const token = await getAccessToken();
421419
users = await kcClient.GetUsers({
422420
params: {
423-
realm: process.env.KEYCLOAK_REALM!,
421+
realm: authConfig.realm,
424422
},
425423
queries: {
426424
username: username,
@@ -496,7 +494,7 @@ export const updateUser = async (
496494
const token = await getAccessToken();
497495
await kcClient.UpdateUser(data, {
498496
params: {
499-
realm: process.env.KEYCLOAK_REALM!,
497+
realm: authConfig.realm,
500498
id: userId,
501499
},
502500
headers: {

apps/api/src/middlewares/authz_permission.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//
33
// SPDX-License-Identifier: MIT
44

5+
import { authConfig } from "common-helpers";
56
import { NextFunction, Request, Response } from "express";
67
import { authorizationByPermission } from "keycloak-authorization-services";
78
import log from "loglevel";
@@ -16,13 +17,13 @@ export const authzPermission = (permission: {
1617
scopes: string[];
1718
}) => {
1819
const config = {
19-
baseUrl: process.env.KEYCLOAK_URL!,
20-
realm: process.env.KEYCLOAK_REALM!,
20+
baseUrl: authConfig.url,
21+
realm: authConfig.realm,
2122
};
2223

2324
const options = {
2425
permission,
25-
audience: process.env.KEYCLOAK_CLIENT_ID_API!,
26+
audience: authConfig.clientIdAPI,
2627
};
2728

2829
return async function customAuthorizationMiddleware(

apps/api/src/server.ts

+5-7
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,14 @@ import { onStartUp } from "./helpers/on_start_up";
1818
import { adminRouter, authRouter, scannerRouter, userRouter } from "./routes";
1919
import QueueService from "./services/queue";
2020

21-
const requiredEnvVars: string[] = [
22-
"DATABASE_URL",
23-
"KEYCLOAK_URL",
24-
"KEYCLOAK_REALM",
25-
"KEYCLOAK_CLIENT_ID_API",
26-
"KEYCLOAK_CLIENT_SECRET_API",
27-
];
21+
const requiredEnvVars: string[] = ["DATABASE_URL"];
2822

2923
if (process.env.NODE_ENV === "production") {
3024
requiredEnvVars.push(
25+
"KEYCLOAK_URL",
26+
"KEYCLOAK_REALM",
27+
"KEYCLOAK_CLIENT_ID_API",
28+
"KEYCLOAK_CLIENT_SECRET_API",
3129
"SESSION_SECRET",
3230
"COOKIE_SECRET",
3331
"SPACES_ENDPOINT",

apps/clearance_ui/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"class-variance-authority": "0.7.1",
4444
"clsx": "2.1.1",
4545
"cmdk": "1.1.1",
46+
"common-helpers": "*",
4647
"generate-password": "1.7.1",
4748
"js-yaml": "4.1.0",
4849
"lodash.debounce": "4.0.8",

apps/clearance_ui/src/pages/api/auth/[...nextauth].ts

+5-29
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,16 @@
1111

1212
import { Zodios } from "@zodios/core";
1313
import { isAxiosError } from "axios";
14+
import { authConfig } from "common-helpers";
1415
import NextAuth from "next-auth";
1516
import type { JWT } from "next-auth/jwt";
1617
import KeycloakProvider from "next-auth/providers/keycloak";
1718
import { keycloakAPI, type Token } from "validation-helpers";
1819

19-
const keycloakUrl = process.env.KEYCLOAK_URL;
20-
21-
if (!keycloakUrl) {
22-
throw new Error("KEYCLOAK_URL not set");
23-
}
24-
25-
const keycloakRealm = process.env.KEYCLOAK_REALM;
26-
27-
if (!keycloakRealm) {
28-
throw new Error("KEYCLOAK_REALM not set");
29-
}
30-
31-
const keycloakClientIdUi = process.env.KEYCLOAK_CLIENT_ID_UI;
32-
33-
if (!keycloakClientIdUi) {
34-
throw new Error("KEYCLOAK_CLIENT_ID_UI not set");
35-
}
36-
37-
const keycloakClientSecretUi = process.env.KEYCLOAK_CLIENT_SECRET_UI;
38-
39-
if (!keycloakClientSecretUi) {
40-
throw new Error("KEYCLOAK_CLIENT_SECRET_UI not set");
41-
}
20+
const keycloakUrl = authConfig.url;
21+
const keycloakRealm = authConfig.realm;
22+
const keycloakClientIdUi = authConfig.clientIdUI;
23+
const keycloakClientSecretUi = authConfig.clientSecretUI;
4224

4325
const kcClient = new Zodios(keycloakUrl, keycloakAPI);
4426

@@ -49,12 +31,6 @@ const kcClient = new Zodios(keycloakUrl, keycloakAPI);
4931
*/
5032
async function refreshAccessToken(token: JWT) {
5133
let retries = 3;
52-
if (!keycloakClientIdUi) {
53-
throw new Error("KEYCLOAK_CLIENT_ID_UI not set");
54-
}
55-
if (!keycloakRealm) {
56-
throw new Error("KEYCLOAK_REALM not set");
57-
}
5834

5935
while (retries > 0) {
6036
try {

apps/clearance_ui/src/pages/api/authz/permissions.ts

+4-20
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import { Zodios } from "@zodios/core";
66
import { isAxiosError } from "axios";
7+
import { authConfig } from "common-helpers";
78
import type { NextApiRequest, NextApiResponse } from "next";
89
import { NextResponse } from "next/server";
910
import NodeCache from "node-cache";
@@ -12,30 +13,13 @@ import { keycloakAPI, type Permissions } from "validation-helpers";
1213
// Use a cache to store the permissions for a short time
1314
const cache = new NodeCache({ stdTTL: 5 * 60, checkperiod: 60 });
1415

15-
const keycloakUrl = process.env.KEYCLOAK_URL;
16-
17-
if (!keycloakUrl) {
18-
throw new Error("KEYCLOAK_URL not set");
19-
}
20-
21-
const kcClient = new Zodios(keycloakUrl, keycloakAPI);
16+
const kcClient = new Zodios(authConfig.url, keycloakAPI);
2217

2318
export default async function handler(
2419
req: NextApiRequest,
2520
res: NextApiResponse<Permissions>,
2621
) {
2722
try {
28-
const keycloakClientIdApi = process.env.KEYCLOAK_CLIENT_ID_API;
29-
30-
if (!keycloakClientIdApi) {
31-
throw new Error("KEYCLOAK_CLIENT_ID_API not set");
32-
}
33-
34-
const keycloakRealm = process.env.KEYCLOAK_REALM;
35-
36-
if (!keycloakRealm) {
37-
throw new Error("KEYCLOAK_REALM not set");
38-
}
3923
// Check if the permissions are in the cache
4024
let permissions: Permissions | undefined = cache.get(
4125
req.headers.authorization as string,
@@ -45,12 +29,12 @@ export default async function handler(
4529
permissions = (await kcClient.PostToken(
4630
{
4731
grant_type: "urn:ietf:params:oauth:grant-type:uma-ticket",
48-
audience: keycloakClientIdApi,
32+
audience: authConfig.clientIdAPI,
4933
response_mode: "permissions",
5034
},
5135
{
5236
params: {
53-
realm: keycloakRealm,
37+
realm: authConfig.realm,
5438
},
5539
headers: {
5640
"Content-Type": "application/x-www-form-urlencoded",

package-lock.json

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/common-helpers/index.d.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,13 @@
44

55
declare const getCurrentDateTime: () => string;
66

7-
export { getCurrentDateTime };
7+
declare const authConfig: {
8+
url: string;
9+
realm: string;
10+
clientIdAPI: string;
11+
clientSecretAPI: string;
12+
clientIdUI: string;
13+
clientSecretUI: string;
14+
};
15+
16+
export { authConfig, getCurrentDateTime };
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// SPDX-FileCopyrightText: 2025 Double Open Oy
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
export const authConfig = {
6+
url: process.env.KEYCLOAK_URL || "http://localhost:8083",
7+
realm: process.env.KEYCLOAK_REALM || "dos-dev",
8+
clientIdAPI: process.env.KEYCLOAK_CLIENT_ID_API || "dos-dev-api",
9+
clientSecretAPI:
10+
process.env.KEYCLOAK_CLIENT_SECRET_API || "api-client-secret",
11+
clientIdUI: process.env.KEYCLOAK_CLIENT_ID_UI || "dos-dev-ui",
12+
clientSecretUI: process.env.KEYCLOAK_CLIENT_SECRET_UI || "ui-client-secret",
13+
};

packages/common-helpers/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
// SPDX-License-Identifier: MIT
44

55
export * from "./dateTimeHelper";
6+
export * from "./authConfig";

0 commit comments

Comments
 (0)