Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 7 additions & 4 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ PLAYGROUND_ENABLE=false
# AMQP URL
AMQP_URL=amqp://guest:guest@rabbitmq

# Billing settings
BILLING_DEBUG=true
BILLING_COMPANY_EMAIL="team@hawk.so"

### Accounting module ###
# Accounting service URL
# CODEX_ACCOUNTING_URL=http://accounting:3999/graphql
Expand Down Expand Up @@ -90,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
7 changes: 3 additions & 4 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ SMTP_SENDER_ADDRESS=
# AMQP URL
AMQP_URL=amqp://guest:guest@rabbitmq:5672/

# Billing settings
BILLING_DEBUG=true
BILLING_COMPANY_EMAIL="team@hawk.so"

### Accounting module ###
# Accounting service URL
# CODEX_ACCOUNTING_URL=
Expand Down Expand Up @@ -101,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
Loading
Loading