Skip to content

Commit 765a71e

Browse files
authored
Merge branch 'master' into feat/breadcrumbs
2 parents b56d6e4 + c253875 commit 765a71e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+5523
-25
lines changed

.env.sample

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,10 @@ AWS_S3_SECRET_ACCESS_KEY=
8686
AWS_S3_BUCKET_NAME=
8787
AWS_S3_BUCKET_BASE_URL=
8888
AWS_S3_BUCKET_ENDPOINT=
89+
90+
# SSO Service Provider Entity ID
91+
# Unique identifier for Hawk in SAML IdP configuration
92+
SSO_SP_ENTITY_ID=urn:hawk:tracker:saml
93+
94+
## SAML state store type (memory or redis, default: redis)
95+
SAML_STORE_TYPE=redis

.env.test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,6 @@ AWS_S3_SECRET_ACCESS_KEY=
9797
AWS_S3_BUCKET_NAME=
9898
AWS_S3_BUCKET_BASE_URL=
9999
AWS_S3_BUCKET_ENDPOINT=
100+
101+
## SAML state store type (memory or redis, default: redis)
102+
SAML_STORE_TYPE=memory

.github/workflows/build-and-push-docker-image.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,19 @@ jobs:
4848
type=semver,pattern={{version}}
4949
type=semver,pattern={{major}}.{{minor}}
5050
51+
- name: Read Node.js version from .nvmrc
52+
id: node_version
53+
run: |
54+
NODE_VERSION=$(cat .nvmrc | tr -d 'v')
55+
echo "version=${NODE_VERSION}" >> $GITHUB_OUTPUT
56+
5157
- name: Build and push image
5258
uses: docker/build-push-action@v3
5359
with:
5460
context: .
5561
file: docker/Dockerfile.prod
62+
build-args: |
63+
NODE_VERSION=${{ steps.node_version.outputs.version }}
5664
tags: ${{ steps.meta.outputs.tags }}
5765
labels: ${{ steps.meta.outputs.labels }}
5866
push: ${{ github.ref == 'refs/heads/stage' || github.ref == 'refs/heads/prod' || startsWith(github.ref, 'refs/tags/v') }}

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v22.12.0
1+
v24.11.1

docker-compose.test.yml

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ services:
1111
- ./:/usr/src/app
1212
- /usr/src/app/node_modules
1313
- ./test/integration/api.env:/usr/src/app/.env
14+
- ./test/integration/keycloak:/keycloak:ro
1415
depends_on:
1516
- mongodb
1617
- rabbitmq
18+
- keycloak
1719
# - accounting
1820
stdin_open: true
1921
tty: true
@@ -32,10 +34,20 @@ services:
3234
condition: service_healthy
3335
api:
3436
condition: service_started
35-
command: dockerize -wait http://api:4000/.well-known/apollo/server-health -timeout 30s yarn jest --config=./test/integration/jest.config.js --runInBand test/integration
37+
keycloak:
38+
condition: service_healthy
39+
environment:
40+
- KEYCLOAK_URL=http://keycloak:8180
41+
entrypoint: ["/bin/bash", "-c"]
42+
command:
43+
- |
44+
dockerize -wait http://api:4000/.well-known/apollo/server-health -timeout 30s -wait http://keycloak:8180/health/ready -timeout 60s &&
45+
/keycloak/setup.sh &&
46+
yarn jest --config=./test/integration/jest.config.js --runInBand test/integration
3647
volumes:
3748
- ./:/usr/src/app
3849
- /usr/src/app/node_modules
50+
- ./test/integration/keycloak:/keycloak:ro
3951

4052
rabbitmq:
4153
image: rabbitmq:3-management
@@ -52,6 +64,29 @@ services:
5264
timeout: 3s
5365
retries: 5
5466

67+
keycloak:
68+
image: quay.io/keycloak/keycloak:23.0
69+
environment:
70+
- KEYCLOAK_ADMIN=admin
71+
- KEYCLOAK_ADMIN_PASSWORD=admin
72+
- KC_HTTP_PORT=8180
73+
- KC_HOSTNAME_STRICT=false
74+
- KC_HOSTNAME_STRICT_HTTPS=false
75+
- KC_HTTP_ENABLED=true
76+
- KC_HEALTH_ENABLED=true
77+
ports:
78+
- 8180:8180
79+
command:
80+
- start-dev
81+
volumes:
82+
- keycloak-test-data:/opt/keycloak/data
83+
- ./test/integration/keycloak:/opt/keycloak/config
84+
healthcheck:
85+
test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/8180;echo -e 'GET /health/ready HTTP/1.1\r\nhost: http://localhost\r\nConnection: close\r\n\r\n' >&3;if [ $? -eq 0 ]; then echo 'Healthcheck Successful';exit 0;else echo 'Healthcheck Failed';exit 1;fi;"]
86+
interval: 10s
87+
timeout: 5s
88+
retries: 10
89+
5590
# accounting:
5691
# image: codexteamuser/codex-accounting:prod
5792
# env_file:
@@ -61,3 +96,4 @@ services:
6196

6297
volumes:
6398
mongodata-test:
99+
keycloak-test-data:

docker/Dockerfile.dev

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
FROM node:22-alpine as builder
1+
ARG NODE_VERSION=24.11.1
2+
FROM node:${NODE_VERSION}-alpine as builder
23

34
WORKDIR /usr/src/app
45
RUN apk add --no-cache git gcc g++ python3 make musl-dev
@@ -7,11 +8,11 @@ COPY package.json yarn.lock ./
78

89
RUN yarn install
910

10-
FROM node:22-alpine
11+
FROM node:${NODE_VERSION}-alpine
1112

1213
WORKDIR /usr/src/app
1314

14-
RUN apk add --no-cache openssl
15+
RUN apk add --no-cache openssl bash curl
1516

1617
ENV DOCKERIZE_VERSION v0.6.1
1718
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \

docker/Dockerfile.prod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
FROM node:22-alpine as builder
1+
ARG NODE_VERSION=24.11.1
2+
FROM node:${NODE_VERSION}-alpine as builder
23

34
WORKDIR /usr/src/app
45
RUN apk add --no-cache git gcc g++ python3 make musl-dev
@@ -11,7 +12,7 @@ COPY . .
1112

1213
RUN yarn build
1314

14-
FROM node:22-alpine
15+
FROM node:${NODE_VERSION}-alpine
1516

1617
WORKDIR /usr/src/app
1718

docs/Keycloak.md

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# Keycloak for Hawk SSO Development
2+
3+
This guide explains how to use Keycloak for testing Hawk's SSO implementation.
4+
5+
## Quick Start
6+
7+
### 1. Start Keycloak
8+
9+
From the project root:
10+
11+
```bash
12+
docker-compose up keycloak
13+
```
14+
15+
Keycloak will be available at: **http://localhost:8180**
16+
17+
### 2. Run Setup Script
18+
19+
The setup script will configure Keycloak with a test realm, SAML client, and test users.
20+
21+
**Option 1: Run from your host machine** (recommended):
22+
23+
```bash
24+
cd api/test/integration/keycloak
25+
KEYCLOAK_URL=http://localhost:8180 ./setup.sh
26+
```
27+
28+
**Option 2: Run from API container** (if you don't have curl on host):
29+
30+
```bash
31+
docker-compose exec -e KEYCLOAK_URL=http://keycloak:8180 api /keycloak/setup.sh
32+
```
33+
34+
**Note:** The setup script requires `curl` and `bash` to interact with Keycloak API. The Keycloak container doesn't have these tools, so we either run from host or from another container (like `api`).
35+
36+
### 3. Access Keycloak Admin Console
37+
38+
- URL: http://localhost:8180
39+
- Username: `admin`
40+
- Password: `admin`
41+
42+
## Configuration
43+
44+
### Realm
45+
46+
- **Name**: `hawk`
47+
- **SAML Endpoint**: http://localhost:8180/realms/hawk/protocol/saml
48+
49+
### SAML Client
50+
51+
- **Client ID / Entity ID**: `urn:hawk:tracker:saml`
52+
- This must match `SSO_SP_ENTITY_ID` environment variable in Hawk API
53+
- **Protocol**: SAML 2.0
54+
- **ACS URL**: http://localhost:4000/auth/sso/saml/{workspaceId}/acs
55+
- **Name ID Format**: email
56+
57+
### Environment Variables
58+
59+
Hawk API requires the following environment variable:
60+
61+
- **SSO_SP_ENTITY_ID**: `urn:hawk:tracker:saml`
62+
- Set in `docker-compose.yml` or `.env` file
63+
- This is the Service Provider Entity ID used to identify Hawk in SAML requests
64+
65+
### Test Users
66+
67+
| Username | Email | Password | Department | Title |
68+
|----------|-------|----------|------------|-------|
69+
| testuser | [email protected] | password123 | Engineering | Software Engineer |
70+
| alice | [email protected] | password123 | Product | Product Manager |
71+
| bob | [email protected] | password123 | Engineering | Senior Developer |
72+
73+
## Hawk SSO Configuration
74+
75+
To configure SSO in Hawk workspace settings:
76+
77+
### Get Configuration Automatically
78+
79+
**Option 1: Use the helper script** (recommended):
80+
81+
```bash
82+
cd api/test/integration/keycloak
83+
./get-config.sh
84+
```
85+
86+
This will output all required values that you can copy-paste into Hawk SSO settings.
87+
88+
**Option 2: Get values manually**:
89+
90+
### Required Fields
91+
92+
1. **IdP Entity ID**:
93+
```
94+
http://localhost:8180/realms/hawk
95+
```
96+
97+
2. **SSO URL**:
98+
```
99+
http://localhost:8180/realms/hawk/protocol/saml
100+
```
101+
102+
3. **X.509 Certificate**:
103+
104+
**Via command line**:
105+
```bash
106+
curl -s "http://localhost:8180/realms/hawk/protocol/saml/descriptor" | grep -oP '(?<=<ds:X509Certificate>)[^<]+' | head -1
107+
```
108+
109+
**Via Keycloak Admin Console**:
110+
- Go to Realm Settings → Keys
111+
- Find RS256 algorithm row
112+
- Click "Certificate" button
113+
- Copy the certificate (without BEGIN/END lines)
114+
- Paste into Hawk SSO settings
115+
116+
### Attribute Mapping
117+
118+
Configure these mappings in Hawk:
119+
120+
- **Email**: `email`
121+
- **Name**: `name` (full name - combines firstName and lastName from Keycloak)
122+
- **Department** (optional): `department`
123+
- **Title** (optional): `title`
124+
125+
### Name ID Format
126+
127+
Select: **Email address (urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress)**
128+
129+
## Testing SSO Flow
130+
131+
### Manual Test
132+
133+
1. Configure SSO in Hawk workspace settings with the values above
134+
2. Enable SSO for the workspace
135+
3. Navigate to: http://localhost:4000/auth/sso/saml/{workspaceId}
136+
4. You'll be redirected to Keycloak login page
137+
5. Login with any test user (e.g., `[email protected]` / `password123`)
138+
6. After successful authentication, you'll be redirected back to Hawk with tokens
139+
140+
### Automated Test
141+
142+
Run integration tests:
143+
144+
```bash
145+
cd api
146+
yarn test:integration
147+
```
148+
149+
## Troubleshooting
150+
151+
### Keycloak not starting
152+
153+
Check Docker logs:
154+
```bash
155+
docker-compose logs keycloak
156+
```
157+
158+
### Realm already exists
159+
160+
If you need to reset:
161+
```bash
162+
docker-compose down -v
163+
docker-compose up keycloak
164+
```
165+
166+
### Certificate issues
167+
168+
If SAML validation fails:
169+
1. Verify the certificate is copied correctly (no extra spaces/newlines)
170+
2. Ensure you copied the certificate content without BEGIN/END markers
171+
3. Check Keycloak logs for signature errors
172+
173+
### Get SAML Metadata
174+
175+
You can view the full SAML metadata descriptor at:
176+
```
177+
http://localhost:8180/realms/hawk/protocol/saml/descriptor
178+
```
179+
180+
This contains all technical details about the IdP configuration.
181+
182+
## Files
183+
184+
Files are located in `api/test/integration/keycloak/`:
185+
186+
- `import/hawk-realm.json` - Keycloak realm configuration
187+
- `setup.sh` - Automated setup script
188+
189+
## Advanced Configuration
190+
191+
### Custom Workspace ID
192+
193+
To test with a different workspace ID, update the ACS URL in the Keycloak Admin Console:
194+
195+
1. Go to Clients → hawk-sp
196+
2. Update `saml_assertion_consumer_url_post` attribute
197+
3. Save changes
198+
199+
### Additional Users
200+
201+
You can add more users through:
202+
- Keycloak Admin Console → Users → Add User
203+
- Or update `api/test/integration/keycloak/import/hawk-realm.json` and re-import
204+
205+
### Different Port
206+
207+
If you need to run Keycloak on a different port:
208+
209+
1. Update `KC_HTTP_PORT` in `docker-compose.yml`
210+
2. Update port mapping in `docker-compose.yml`
211+
3. Update all URLs in this README
212+
4. Update `api/test/integration/keycloak/import/hawk-realm.json` with new URLs

jest.config.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ module.exports = {
2424
* TypeScript support
2525
*/
2626
transform: {
27-
'^.+\\.tsx?$': 'ts-jest',
27+
'^.+\\.tsx?$': ['ts-jest', {
28+
tsconfig: 'test/tsconfig.json',
29+
}],
2830
},
2931

3032
/**

0 commit comments

Comments
 (0)