Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4ef3df4
deps added, env updated
neSpecc Dec 20, 2025
3ecdd48
bootstrap module
neSpecc Dec 20, 2025
73fa3b9
models updated
neSpecc Dec 20, 2025
025fdc1
update branch
neSpecc Dec 24, 2025
223a946
tests for model and factory method added
neSpecc Dec 24, 2025
c7f9c29
rm redunant test cases
neSpecc Dec 24, 2025
5892ea0
Update .nvmrc
neSpecc Dec 24, 2025
1ca5fb8
Refactor SAML validation logic and add unit tests
neSpecc Dec 24, 2025
77d55e9
rm try-catch from tests
neSpecc Dec 24, 2025
61fea8e
Refactor SAML response validation to use node-saml
neSpecc Dec 24, 2025
1369b2c
Implement SAML AuthnRequest generation and tests
neSpecc Dec 24, 2025
0725454
SamlStateStore implemetation
neSpecc Dec 28, 2025
1963a59
Implement SAML SSO controller and tests
neSpecc Jan 7, 2026
76232df
Add SSO config support with admin-only GraphQL directive
neSpecc Jan 7, 2026
e9a6848
Add dynamic Node version and improve SAML SSO error handling
neSpecc Jan 7, 2026
0ea60b7
Update build-and-push-docker-image.yml
neSpecc Jan 7, 2026
1e9695b
Update build-and-push-docker-image.yml
neSpecc Jan 7, 2026
6b3d58c
Update mongodb.ts
neSpecc Jan 7, 2026
6cacb3a
Update controller.test.ts
neSpecc Jan 7, 2026
19d03f6
Add public SSO workspace info query
neSpecc Jan 7, 2026
65c22e3
Update workspace.js
neSpecc Jan 7, 2026
154f59e
Enforce SSO login and refactor SSO config update
neSpecc Jan 12, 2026
c384e71
add logs to the sso controller
neSpecc Jan 12, 2026
fbb0afc
Shorten refresh token expiry for enforced SSO users
neSpecc Jan 12, 2026
de004e3
Create sso.test.ts
neSpecc Jan 12, 2026
6e3d722
fixes for sso
neSpecc Jan 14, 2026
fc57f58
integration tests
neSpecc Jan 15, 2026
cf3c050
Bump version up to 1.2.33
github-actions[bot] Jan 15, 2026
7190108
Update package.json
neSpecc Jan 15, 2026
1080020
lint
neSpecc Jan 15, 2026
73a89e6
fix tests
neSpecc Jan 15, 2026
de08345
Add pluggable SAML state store with Redis and memory support
neSpecc Jan 15, 2026
26b9d2f
fix unti tests
neSpecc Jan 15, 2026
d95d526
fix integration tests
neSpecc Jan 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,10 @@ AWS_S3_SECRET_ACCESS_KEY=
AWS_S3_BUCKET_NAME=
AWS_S3_BUCKET_BASE_URL=
AWS_S3_BUCKET_ENDPOINT=

# SSO Service Provider Entity ID
# Unique identifier for Hawk in SAML IdP configuration
SSO_SP_ENTITY_ID=urn:hawk:tracker:saml

## SAML state store type (memory or redis, default: redis)
SAML_STORE_TYPE=redis
3 changes: 3 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,6 @@ AWS_S3_SECRET_ACCESS_KEY=
AWS_S3_BUCKET_NAME=
AWS_S3_BUCKET_BASE_URL=
AWS_S3_BUCKET_ENDPOINT=

## SAML state store type (memory or redis, default: redis)
SAML_STORE_TYPE=memory
8 changes: 8 additions & 0 deletions .github/workflows/build-and-push-docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,19 @@ jobs:
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}

- name: Read Node.js version from .nvmrc
id: node_version
run: |
NODE_VERSION=$(cat .nvmrc | tr -d 'v')
echo "version=${NODE_VERSION}" >> $GITHUB_OUTPUT

- name: Build and push image
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile.prod
build-args: |
NODE_VERSION=${{ steps.node_version.outputs.version }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
push: ${{ github.ref == 'refs/heads/stage' || github.ref == 'refs/heads/prod' || startsWith(github.ref, 'refs/tags/v') }}
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v22.12.0
v24.11.1
38 changes: 37 additions & 1 deletion docker-compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ services:
- ./:/usr/src/app
- /usr/src/app/node_modules
- ./test/integration/api.env:/usr/src/app/.env
- ./test/integration/keycloak:/keycloak:ro
depends_on:
- mongodb
- rabbitmq
- keycloak
# - accounting
stdin_open: true
tty: true
Expand All @@ -32,10 +34,20 @@ services:
condition: service_healthy
api:
condition: service_started
command: dockerize -wait http://api:4000/.well-known/apollo/server-health -timeout 30s yarn jest --config=./test/integration/jest.config.js --runInBand test/integration
keycloak:
condition: service_healthy
environment:
- KEYCLOAK_URL=http://keycloak:8180
entrypoint: ["/bin/bash", "-c"]
command:
- |
dockerize -wait http://api:4000/.well-known/apollo/server-health -timeout 30s -wait http://keycloak:8180/health/ready -timeout 60s &&
/keycloak/setup.sh &&
yarn jest --config=./test/integration/jest.config.js --runInBand test/integration
volumes:
- ./:/usr/src/app
- /usr/src/app/node_modules
- ./test/integration/keycloak:/keycloak:ro

rabbitmq:
image: rabbitmq:3-management
Expand All @@ -52,6 +64,29 @@ services:
timeout: 3s
retries: 5

keycloak:
image: quay.io/keycloak/keycloak:23.0
environment:
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=admin
- KC_HTTP_PORT=8180
- KC_HOSTNAME_STRICT=false
- KC_HOSTNAME_STRICT_HTTPS=false
- KC_HTTP_ENABLED=true
- KC_HEALTH_ENABLED=true
ports:
- 8180:8180
command:
- start-dev
volumes:
- keycloak-test-data:/opt/keycloak/data
- ./test/integration/keycloak:/opt/keycloak/config
healthcheck:
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;"]
interval: 10s
timeout: 5s
retries: 10

# accounting:
# image: codexteamuser/codex-accounting:prod
# env_file:
Expand All @@ -61,3 +96,4 @@ services:

volumes:
mongodata-test:
keycloak-test-data:
7 changes: 4 additions & 3 deletions docker/Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
FROM node:22-alpine as builder
ARG NODE_VERSION=24.11.1
FROM node:${NODE_VERSION}-alpine as builder

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

RUN yarn install

FROM node:22-alpine
FROM node:${NODE_VERSION}-alpine

WORKDIR /usr/src/app

RUN apk add --no-cache openssl
RUN apk add --no-cache openssl bash curl

ENV DOCKERIZE_VERSION v0.6.1
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
Expand Down
5 changes: 3 additions & 2 deletions docker/Dockerfile.prod
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
FROM node:22-alpine as builder
ARG NODE_VERSION=24.11.1
FROM node:${NODE_VERSION}-alpine as builder

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

RUN yarn build

FROM node:22-alpine
FROM node:${NODE_VERSION}-alpine

WORKDIR /usr/src/app

Expand Down
212 changes: 212 additions & 0 deletions docs/Keycloak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# Keycloak for Hawk SSO Development

This guide explains how to use Keycloak for testing Hawk's SSO implementation.

## Quick Start

### 1. Start Keycloak

From the project root:

```bash
docker-compose up keycloak
```

Keycloak will be available at: **http://localhost:8180**

### 2. Run Setup Script

The setup script will configure Keycloak with a test realm, SAML client, and test users.

**Option 1: Run from your host machine** (recommended):

```bash
cd api/test/integration/keycloak
KEYCLOAK_URL=http://localhost:8180 ./setup.sh
```

**Option 2: Run from API container** (if you don't have curl on host):

```bash
docker-compose exec -e KEYCLOAK_URL=http://keycloak:8180 api /keycloak/setup.sh
```

**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`).

### 3. Access Keycloak Admin Console

- URL: http://localhost:8180
- Username: `admin`
- Password: `admin`

## Configuration

### Realm

- **Name**: `hawk`
- **SAML Endpoint**: http://localhost:8180/realms/hawk/protocol/saml

### SAML Client

- **Client ID / Entity ID**: `urn:hawk:tracker:saml`
- This must match `SSO_SP_ENTITY_ID` environment variable in Hawk API
- **Protocol**: SAML 2.0
- **ACS URL**: http://localhost:4000/auth/sso/saml/{workspaceId}/acs
- **Name ID Format**: email

### Environment Variables

Hawk API requires the following environment variable:

- **SSO_SP_ENTITY_ID**: `urn:hawk:tracker:saml`
- Set in `docker-compose.yml` or `.env` file
- This is the Service Provider Entity ID used to identify Hawk in SAML requests

### Test Users

| Username | Email | Password | Department | Title |
|----------|-------|----------|------------|-------|
| testuser | testuser@hawk.local | password123 | Engineering | Software Engineer |
| alice | alice@hawk.local | password123 | Product | Product Manager |
| bob | bob@hawk.local | password123 | Engineering | Senior Developer |

## Hawk SSO Configuration

To configure SSO in Hawk workspace settings:

### Get Configuration Automatically

**Option 1: Use the helper script** (recommended):

```bash
cd api/test/integration/keycloak
./get-config.sh
```

This will output all required values that you can copy-paste into Hawk SSO settings.

**Option 2: Get values manually**:

### Required Fields

1. **IdP Entity ID**:
```
http://localhost:8180/realms/hawk
```

2. **SSO URL**:
```
http://localhost:8180/realms/hawk/protocol/saml
```

3. **X.509 Certificate**:

**Via command line**:
```bash
curl -s "http://localhost:8180/realms/hawk/protocol/saml/descriptor" | grep -oP '(?<=<ds:X509Certificate>)[^<]+' | head -1
```

**Via Keycloak Admin Console**:
- Go to Realm Settings → Keys
- Find RS256 algorithm row
- Click "Certificate" button
- Copy the certificate (without BEGIN/END lines)
- Paste into Hawk SSO settings

### Attribute Mapping

Configure these mappings in Hawk:

- **Email**: `email`
- **Name**: `name` (full name - combines firstName and lastName from Keycloak)
- **Department** (optional): `department`
- **Title** (optional): `title`

### Name ID Format

Select: **Email address (urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress)**

## Testing SSO Flow

### Manual Test

1. Configure SSO in Hawk workspace settings with the values above
2. Enable SSO for the workspace
3. Navigate to: http://localhost:4000/auth/sso/saml/{workspaceId}
4. You'll be redirected to Keycloak login page
5. Login with any test user (e.g., `testuser@hawk.local` / `password123`)
6. After successful authentication, you'll be redirected back to Hawk with tokens

### Automated Test

Run integration tests:

```bash
cd api
yarn test:integration
```

## Troubleshooting

### Keycloak not starting

Check Docker logs:
```bash
docker-compose logs keycloak
```

### Realm already exists

If you need to reset:
```bash
docker-compose down -v
docker-compose up keycloak
```

### Certificate issues

If SAML validation fails:
1. Verify the certificate is copied correctly (no extra spaces/newlines)
2. Ensure you copied the certificate content without BEGIN/END markers
3. Check Keycloak logs for signature errors

### Get SAML Metadata

You can view the full SAML metadata descriptor at:
```
http://localhost:8180/realms/hawk/protocol/saml/descriptor
```

This contains all technical details about the IdP configuration.

## Files

Files are located in `api/test/integration/keycloak/`:

- `import/hawk-realm.json` - Keycloak realm configuration
- `setup.sh` - Automated setup script

## Advanced Configuration

### Custom Workspace ID

To test with a different workspace ID, update the ACS URL in the Keycloak Admin Console:

1. Go to Clients → hawk-sp
2. Update `saml_assertion_consumer_url_post` attribute
3. Save changes

### Additional Users

You can add more users through:
- Keycloak Admin Console → Users → Add User
- Or update `api/test/integration/keycloak/import/hawk-realm.json` and re-import

### Different Port

If you need to run Keycloak on a different port:

1. Update `KC_HTTP_PORT` in `docker-compose.yml`
2. Update port mapping in `docker-compose.yml`
3. Update all URLs in this README
4. Update `api/test/integration/keycloak/import/hawk-realm.json` with new URLs
4 changes: 3 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ module.exports = {
* TypeScript support
*/
transform: {
'^.+\\.tsx?$': 'ts-jest',
'^.+\\.tsx?$': ['ts-jest', {
tsconfig: 'test/tsconfig.json',
}],
},

/**
Expand Down
Loading
Loading