Skip to content

Commit 9344e84

Browse files
committed
Add Hanko SSO authentication
1 parent e8978bc commit 9344e84

File tree

43 files changed

+1534
-87
lines changed

Some content is hidden

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

43 files changed

+1534
-87
lines changed

.env.example

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,20 @@ S3_BUCKET_NAME=${S3_BUCKET_NAME:-dtm-bucket}
4040
S3_ACCESS_KEY=${S3_ACCESS_KEY:-admin}
4141
S3_SECRET_KEY=${S3_SECRET_KEY:-somelongpassword}
4242

43-
# ---- Auth (frontend login) ----
43+
# ---- Authentication ----
44+
# Options: "legacy" (Google OAuth) or "hanko" (Hanko SSO)
45+
AUTH_PROVIDER=${AUTH_PROVIDER:-legacy}
46+
47+
# Legacy Google OAuth (when AUTH_PROVIDER="legacy")
4448
GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID:-}
4549
GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET:-}
4650
GOOGLE_LOGIN_REDIRECT_URI=${GOOGLE_LOGIN_REDIRECT_URI:-http://localhost:$FRONTEND_WEB_APP_PORT/auth}
4751

52+
# Hanko SSO (when AUTH_PROVIDER="hanko")
53+
HANKO_API_URL=${HANKO_API_URL:-https://dev.login.hotosm.org}
54+
COOKIE_SECRET=${COOKIE_SECRET:-your-secret-key-min-32-bytes-long}
55+
COOKIE_DOMAIN=${COOKIE_DOMAIN:-}
56+
4857
# ---- External APIs ----
4958
# ODM endpoint (NodeODM typically) for processing
5059
ODM_ENDPOINT=${ODM_ENDPOINT:-http://nodeodm:9900}
@@ -60,3 +69,7 @@ SMTP_USER=${SMTP_USER:-}
6069
SMTP_PASSWORD=${SMTP_PASSWORD:-}
6170
EMAILS_FROM_EMAIL=${EMAILS_FROM_EMAIL:-}
6271
EMAILS_FROM_NAME=${EMAILS_FROM_NAME:-"Drone Tasking Manager"}
72+
73+
# ---- CORS ----
74+
# Required for Hanko SSO cookies to work cross-origin
75+
EXTRA_CORS_ORIGINS=${EXTRA_CORS_ORIGINS:-["http://localhost:3040"]}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
name: Deploy login-hanko to testlogin.dronetm.hotosm.org
2+
3+
on:
4+
push:
5+
branches:
6+
- feature/hanko-auth-v2
7+
workflow_dispatch:
8+
9+
env:
10+
REGISTRY: ghcr.io
11+
IMAGE_PREFIX: ${{ github.repository }}
12+
13+
jobs:
14+
build:
15+
runs-on: ubuntu-latest
16+
permissions:
17+
contents: read
18+
packages: write
19+
steps:
20+
- uses: actions/checkout@v4
21+
- uses: docker/setup-buildx-action@v3
22+
- uses: docker/login-action@v3
23+
with:
24+
registry: ghcr.io
25+
username: ${{ github.actor }}
26+
password: ${{ secrets.GITHUB_TOKEN }}
27+
28+
- name: Build and push backend
29+
run: |
30+
docker build -t ghcr.io/${{ env.IMAGE_PREFIX }}/backend:login-hanko \
31+
--target service ./src/backend
32+
docker push ghcr.io/${{ env.IMAGE_PREFIX }}/backend:login-hanko
33+
34+
- name: Build and push frontend
35+
run: |
36+
docker build -t ghcr.io/${{ env.IMAGE_PREFIX }}/frontend:login-hanko \
37+
--target prod \
38+
--build-arg VITE_AUTH_PROVIDER=${{ vars.AUTH_PROVIDER || 'hanko' }} \
39+
--build-arg VITE_HANKO_URL=https://dev.login.hotosm.org \
40+
--build-arg VITE_FRONTEND_URL=https://testlogin.dronetm.hotosm.org \
41+
--build-arg VITE_API_URL=https://testlogin.dronetm.hotosm.org/api \
42+
--build-arg API_URL=https://testlogin.dronetm.hotosm.org/api \
43+
--build-arg BASE_URL=https://testlogin.dronetm.hotosm.org/api \
44+
--build-arg SITE_NAME="Drone TM Test" \
45+
./src/frontend
46+
docker push ghcr.io/${{ env.IMAGE_PREFIX }}/frontend:login-hanko
47+
48+
deploy:
49+
needs: build
50+
runs-on: ubuntu-latest
51+
environment: testlogin
52+
steps:
53+
- uses: actions/checkout@v4
54+
- uses: webfactory/ssh-agent@v0.9.0
55+
with:
56+
ssh-private-key: ${{ secrets.EC2_SSH_KEY }}
57+
58+
- name: Add host to known_hosts
59+
run: ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts
60+
61+
- name: Deploy
62+
env:
63+
EC2_HOST: ${{ secrets.EC2_HOST }}
64+
EC2_USER: ${{ secrets.EC2_USER }}
65+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
66+
GH_ACTOR: ${{ github.actor }}
67+
AUTH_PROVIDER: ${{ vars.AUTH_PROVIDER || 'hanko' }}
68+
run: |
69+
ssh $EC2_USER@$EC2_HOST << ENDSSH
70+
set -e
71+
72+
# Ensure Traefik is running
73+
if ! docker ps | grep -q traefik; then
74+
echo "ERROR: Traefik not running. Run setup-test-server.sh first."
75+
exit 1
76+
fi
77+
78+
APP_DIR="/opt/dronetm-test"
79+
80+
# Setup inicial si no existe
81+
if [ ! -d "\$APP_DIR" ]; then
82+
sudo mkdir -p \$APP_DIR
83+
sudo chown \$USER:\$USER \$APP_DIR
84+
git clone -b login-hanko https://github.com/hotosm/drone-tm.git \$APP_DIR
85+
echo "✓ Cloned repository"
86+
fi
87+
88+
cd \$APP_DIR
89+
90+
# Pull latest changes
91+
git fetch origin login-hanko
92+
git reset --hard origin/login-hanko
93+
echo "✓ Updated to latest login-hanko"
94+
95+
# Create .env with defaults (ok for testing)
96+
cat > .env << EOF
97+
POSTGRES_USER=dtm
98+
POSTGRES_PASSWORD=dtm
99+
POSTGRES_DB=dtm_db
100+
SECRET_KEY=test-secret-key-for-testing-only-min-32-chars
101+
S3_ACCESS_KEY=minioadmin
102+
S3_SECRET_KEY=minioadmin
103+
AUTH_PROVIDER=${AUTH_PROVIDER}
104+
EOF
105+
echo "✓ Created .env"
106+
107+
# Login to GHCR
108+
echo "${GH_TOKEN}" | docker login ghcr.io -u ${GH_ACTOR} --password-stdin
109+
110+
# Pull and deploy
111+
docker compose -f compose.login-hanko.yaml pull
112+
docker compose -f compose.login-hanko.yaml up -d --force-recreate
113+
114+
# Cleanup
115+
docker image prune -af
116+
117+
echo "✓ Deployment complete"
118+
ENDSSH

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ env/
44
./build/
55
develop-eggs/
66
dist/
7+
# Allow auth-libs dist (distributed from auth-libs repo)
8+
!src/frontend/auth-libs/**/dist/
9+
!src/backend/auth-libs/dist/
710
downloads/
811
eggs/
912
.eggs/

compose.login-hanko.yaml

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# compose.test.yaml - Test environment for login-hanko branch
2+
# Deploy to: testlogin.dronetm.hotosm.org
3+
# Requires: Traefik running in /opt/traefik with hotosm-test network
4+
5+
services:
6+
# Frontend syncs built files to a shared volume
7+
frontend:
8+
image: ghcr.io/hotosm/drone-tm/frontend:login-hanko
9+
restart: unless-stopped
10+
volumes:
11+
- frontend-html:/frontend_html
12+
networks:
13+
- internal
14+
15+
# Nginx serves the static files
16+
nginx:
17+
image: nginx:alpine
18+
restart: unless-stopped
19+
depends_on:
20+
- frontend
21+
volumes:
22+
- frontend-html:/usr/share/nginx/html:ro
23+
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
24+
networks:
25+
- hotosm-test
26+
labels:
27+
- "traefik.enable=true"
28+
- "traefik.http.routers.dronetm-frontend.rule=Host(`testlogin.dronetm.hotosm.org`) && !PathPrefix(`/api`)"
29+
- "traefik.http.routers.dronetm-frontend.entrypoints=websecure"
30+
- "traefik.http.routers.dronetm-frontend.tls.certresolver=letsencrypt"
31+
- "traefik.http.services.dronetm-frontend.loadbalancer.server.port=80"
32+
33+
backend:
34+
image: ghcr.io/hotosm/drone-tm/backend:login-hanko
35+
restart: unless-stopped
36+
deploy:
37+
replicas: 4
38+
depends_on:
39+
- db
40+
- redis
41+
environment:
42+
- DATABASE_URL=postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
43+
- AUTH_PROVIDER=${AUTH_PROVIDER:-hanko}
44+
- HANKO_API_URL=https://dev.login.hotosm.org
45+
- JWT_ISSUER=https://dev.login.hotosm.org
46+
- SECRET_KEY=${SECRET_KEY}
47+
- COOKIE_SECRET=${SECRET_KEY}
48+
- S3_ENDPOINT=http://minio:9000
49+
- S3_ACCESS_KEY=${S3_ACCESS_KEY}
50+
- S3_SECRET_KEY=${S3_SECRET_KEY}
51+
- S3_BUCKET_NAME=dronetm
52+
- S3_DOWNLOAD_ROOT=https://s3.testlogin.dronetm.hotosm.org
53+
- DRAGONFLY_DSN=redis://redis:6379/0
54+
- FRONTEND_URL=https://testlogin.dronetm.hotosm.org
55+
- BACKEND_URL=https://testlogin.dronetm.hotosm.org/api
56+
- EXTRA_CORS_ORIGINS=["https://testlogin.dronetm.hotosm.org","https://portal.hotosm.org","https://dev.login.hotosm.org"]
57+
- ADMIN_EMAILS=hernangigena@gmail.com,justina@animus.com.ar,andreatchirillano@hotmail.com,emilio.mariscal@hotosm.org
58+
networks:
59+
- hotosm-test
60+
- internal
61+
labels:
62+
- "traefik.enable=true"
63+
- "traefik.http.routers.dronetm-backend.rule=Host(`testlogin.dronetm.hotosm.org`) && PathPrefix(`/api`)"
64+
- "traefik.http.routers.dronetm-backend.entrypoints=websecure"
65+
- "traefik.http.routers.dronetm-backend.tls.certresolver=letsencrypt"
66+
- "traefik.http.services.dronetm-backend.loadbalancer.server.port=8000"
67+
68+
db:
69+
image: postgis/postgis:14-3.4
70+
restart: unless-stopped
71+
environment:
72+
- POSTGRES_USER=${POSTGRES_USER}
73+
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
74+
- POSTGRES_DB=${POSTGRES_DB}
75+
volumes:
76+
- dronetm-db:/var/lib/postgresql/data
77+
networks:
78+
- internal
79+
80+
redis:
81+
image: redis:7-alpine
82+
restart: unless-stopped
83+
networks:
84+
- internal
85+
86+
minio:
87+
image: minio/minio:latest
88+
restart: unless-stopped
89+
command: server /data --console-address ":9001"
90+
environment:
91+
- MINIO_ROOT_USER=${S3_ACCESS_KEY}
92+
- MINIO_ROOT_PASSWORD=${S3_SECRET_KEY}
93+
volumes:
94+
- dronetm-minio:/data
95+
networks:
96+
- internal
97+
- hotosm-test
98+
labels:
99+
- "traefik.enable=true"
100+
# MinIO Console (Web UI)
101+
- "traefik.http.routers.dronetm-minio-console.rule=Host(`minio.testlogin.dronetm.hotosm.org`)"
102+
- "traefik.http.routers.dronetm-minio-console.entrypoints=websecure"
103+
- "traefik.http.routers.dronetm-minio-console.tls.certresolver=letsencrypt"
104+
- "traefik.http.routers.dronetm-minio-console.service=dronetm-minio-console"
105+
- "traefik.http.services.dronetm-minio-console.loadbalancer.server.port=9001"
106+
# MinIO S3 API (for presigned URLs)
107+
- "traefik.http.routers.dronetm-minio-api.rule=Host(`s3.testlogin.dronetm.hotosm.org`)"
108+
- "traefik.http.routers.dronetm-minio-api.entrypoints=websecure"
109+
- "traefik.http.routers.dronetm-minio-api.tls.certresolver=letsencrypt"
110+
- "traefik.http.routers.dronetm-minio-api.service=dronetm-minio-api"
111+
- "traefik.http.services.dronetm-minio-api.loadbalancer.server.port=9000"
112+
113+
migrations:
114+
image: ghcr.io/hotosm/drone-tm/backend:login-hanko
115+
command: alembic upgrade head
116+
depends_on:
117+
- db
118+
environment:
119+
- DATABASE_URL=postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
120+
networks:
121+
- internal
122+
123+
arq-worker:
124+
image: ghcr.io/hotosm/drone-tm/backend:login-hanko
125+
command: arq app.arq.tasks.WorkerSettings
126+
restart: unless-stopped
127+
depends_on:
128+
- db
129+
- redis
130+
environment:
131+
- DATABASE_URL=postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
132+
- DRAGONFLY_DSN=redis://redis:6379/0
133+
- S3_ENDPOINT=http://minio:9000
134+
- S3_ACCESS_KEY=${S3_ACCESS_KEY}
135+
- S3_SECRET_KEY=${S3_SECRET_KEY}
136+
- S3_BUCKET_NAME=dronetm
137+
- S3_DOWNLOAD_ROOT=https://s3.testlogin.dronetm.hotosm.org
138+
networks:
139+
- internal
140+
141+
networks:
142+
hotosm-test:
143+
external: true
144+
internal:
145+
146+
volumes:
147+
dronetm-db:
148+
dronetm-minio:
149+
frontend-html:

compose.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ services:
4646
S3_ENDPOINT_DOWNLOAD: ${S3_ENDPOINT_DOWNLOAD:-http://localhost:9000}
4747
networks:
4848
- dtm-network
49+
extra_hosts:
50+
- "host.docker.internal:host-gateway"
4951
restart: unless-stopped
5052
healthcheck:
5153
test: ["CMD", "curl", "-f", "http://localhost:8000/__lbheartbeat__"]

nginx.conf

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
server {
2+
listen 80;
3+
server_name _;
4+
root /usr/share/nginx/html;
5+
index index.html;
6+
7+
# SPA routing - serve index.html for all routes
8+
location / {
9+
try_files $uri $uri/ /index.html;
10+
}
11+
12+
# Cache static assets
13+
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
14+
expires 1y;
15+
add_header Cache-Control "public, immutable";
16+
}
17+
}

0 commit comments

Comments
 (0)