Skip to content

Commit 32b469f

Browse files
committed
feat: add magic link authentication
- Now can activate OIDC and/or MagicLink for user authentication. - Add page to choose authentication method (if only OIDC is enabled, auto redirecting to login screen)
1 parent f4b3430 commit 32b469f

Some content is hidden

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

57 files changed

+6951
-785
lines changed

.env.example

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,20 @@ POSTGRES_USER=ackifyr
1111
POSTGRES_PASSWORD=your_secure_password
1212
POSTGRES_DB=ackify
1313

14-
# OAuth2 Configuration - Generic Provider
14+
# ============================================================================
15+
# Authentication Configuration
16+
# ============================================================================
17+
# At least ONE authentication method must be enabled (OAuth or MagicLink)
18+
#
19+
# AUTO-DETECTION:
20+
# - OAuth is enabled if ACKIFY_OAUTH_CLIENT_ID and ACKIFY_OAUTH_CLIENT_SECRET are set
21+
# - MagicLink is enabled if ACKIFY_MAIL_HOST is configured
22+
#
23+
# You can override auto-detection with these variables:
24+
# ACKIFY_AUTH_OAUTH_ENABLED=true
25+
# ACKIFY_AUTH_MAGICLINK_ENABLED=true
26+
27+
# OAuth2 Configuration (OPTIONAL - remove if using MagicLink only)
1528
ACKIFY_OAUTH_CLIENT_ID=your_oauth_client_id
1629
ACKIFY_OAUTH_CLIENT_SECRET=your_oauth_client_secret
1730
ACKIFY_OAUTH_ALLOWED_DOMAIN=your-organization.com
@@ -35,6 +48,17 @@ ACKIFY_OAUTH_PROVIDER=google
3548
# GitLab specific (if using gitlab as provider and self-hosted)
3649
# ACKIFY_OAUTH_GITLAB_URL=https://gitlab.your-company.com
3750

51+
# Email Configuration for MagicLink Authentication (OPTIONAL - required for MagicLink)
52+
# If configured, enables passwordless authentication via email
53+
# ACKIFY_MAIL_HOST=smtp.example.com
54+
# ACKIFY_MAIL_PORT=587
55+
# ACKIFY_MAIL_USERNAME=your_smtp_username
56+
# ACKIFY_MAIL_PASSWORD=your_smtp_password
57+
# ACKIFY_MAIL_FROM=noreply@example.com
58+
# ACKIFY_MAIL_FROM_NAME=Ackify
59+
# ACKIFY_MAIL_TLS=true
60+
# ACKIFY_MAIL_STARTTLS=true
61+
3862
# Security Configuration
3963
ACKIFY_OAUTH_COOKIE_SECRET=your_base64_encoded_secret_key
4064
ACKIFY_ED25519_PRIVATE_KEY=your_base64_encoded_ed25519_private_key

.github/workflows/e2e-tests.yml

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# SPDX-License-Identifier: AGPL-3.0-or-later
2+
name: E2E Tests
3+
4+
on:
5+
push:
6+
branches: [main, develop]
7+
pull_request:
8+
branches: [main, develop]
9+
10+
jobs:
11+
cypress-run:
12+
runs-on: ubuntu-latest
13+
14+
services:
15+
postgres:
16+
image: postgres:16-alpine
17+
env:
18+
POSTGRES_USER: ackify
19+
POSTGRES_PASSWORD: testpassword
20+
POSTGRES_DB: ackify_test
21+
options: >-
22+
--health-cmd pg_isready
23+
--health-interval 10s
24+
--health-timeout 5s
25+
--health-retries 5
26+
ports:
27+
- 5432:5432
28+
29+
mailhog:
30+
image: mailhog/mailhog:latest
31+
ports:
32+
- 1025:1025
33+
- 8025:8025
34+
35+
steps:
36+
- name: Checkout
37+
uses: actions/checkout@v4
38+
39+
- name: Setup Go
40+
uses: actions/setup-go@v5
41+
with:
42+
go-version: '1.24.5'
43+
cache: true
44+
45+
- name: Setup Node.js
46+
uses: actions/setup-node@v4
47+
with:
48+
node-version: '22'
49+
cache: 'npm'
50+
cache-dependency-path: webapp/package-lock.json
51+
52+
- name: Install frontend dependencies
53+
working-directory: webapp
54+
run: npm ci
55+
56+
- name: Build frontend
57+
working-directory: webapp
58+
run: npm run build
59+
60+
- name: Install backend dependencies
61+
run: go mod download
62+
63+
- name: Run database migrations
64+
env:
65+
ACKIFY_DB_DSN: "postgres://ackify:testpassword@localhost:5432/ackify_test?sslmode=disable"
66+
run: |
67+
go run ./backend/cmd/migrate/main.go up
68+
69+
- name: Generate Ed25519 keys
70+
run: |
71+
go run ./backend/cmd/community/keygen.go > /tmp/ed25519.key || true
72+
if [ ! -f /tmp/ed25519.key ]; then
73+
echo "Generating Ed25519 key for testing"
74+
# Generate a test key if keygen doesn't exist
75+
echo "test_private_key_base64_encoded_here" > /tmp/ed25519.key
76+
fi
77+
78+
- name: Start Ackify server
79+
env:
80+
ACKIFY_DB_DSN: "postgres://ackify:testpassword@localhost:5432/ackify_test?sslmode=disable"
81+
ACKIFY_BASE_URL: "http://localhost:8080"
82+
ACKIFY_ORGANISATION: "Ackify Test"
83+
ACKIFY_OAUTH_PROVIDER: "github"
84+
ACKIFY_OAUTH_CLIENT_ID: "test_client_id"
85+
ACKIFY_OAUTH_CLIENT_SECRET: "test_client_secret"
86+
ACKIFY_OAUTH_COOKIE_SECRET: "dGVzdF9jb29raWVfc2VjcmV0X2Zvcl90ZXN0aW5nXzEyMzQ1Njc4OTA="
87+
ACKIFY_ED25519_PRIVATE_KEY: "dGVzdF9wcml2YXRlX2tleV9mb3JfdGVzdGluZ19vbmx5XzEyMzQ1Njc4OTA="
88+
ACKIFY_LISTEN_ADDR: ":8080"
89+
ACKIFY_ADMIN_EMAILS: "admin@test.com"
90+
ACKIFY_MAIL_HOST: "localhost"
91+
ACKIFY_MAIL_PORT: "1025"
92+
ACKIFY_MAIL_TLS: "false"
93+
ACKIFY_MAIL_STARTTLS: "false"
94+
ACKIFY_MAIL_FROM: "noreply@ackify.test"
95+
ACKIFY_MAIL_FROM_NAME: "Ackify Test"
96+
ACKIFY_LOG_LEVEL: "debug"
97+
run: |
98+
go build -o ackify ./backend/cmd/community
99+
./ackify &
100+
echo $! > /tmp/ackify.pid
101+
102+
# Wait for server to be ready
103+
timeout 30 bash -c 'until curl -s http://localhost:8080/api/v1/health > /dev/null; do sleep 1; done'
104+
105+
- name: Run Cypress tests
106+
uses: cypress-io/github-action@v6
107+
with:
108+
working-directory: webapp
109+
install: false
110+
wait-on: 'http://localhost:8080/api/v1/health'
111+
wait-on-timeout: 60
112+
browser: chrome
113+
headless: true
114+
env:
115+
CYPRESS_baseUrl: http://localhost:8080
116+
CYPRESS_mailhogUrl: http://localhost:8025
117+
118+
- name: Upload Cypress screenshots
119+
uses: actions/upload-artifact@v4
120+
if: failure()
121+
with:
122+
name: cypress-screenshots
123+
path: webapp/cypress/screenshots
124+
retention-days: 7
125+
126+
- name: Upload Cypress videos
127+
uses: actions/upload-artifact@v4
128+
if: failure()
129+
with:
130+
name: cypress-videos
131+
path: webapp/cypress/videos
132+
retention-days: 7
133+
134+
- name: Stop Ackify server
135+
if: always()
136+
run: |
137+
if [ -f /tmp/ackify.pid ]; then
138+
kill $(cat /tmp/ackify.pid) || true
139+
fi

README.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Prove that collaborators have read and acknowledged important documents with **E
2929

3030
**Key Features**:
3131
- ✅ Ed25519 cryptographic signatures
32-
-OAuth2 authentication (Google, GitHub, GitLab, custom)
32+
-**Flexible authentication**: OAuth2 (Google, GitHub, GitLab, custom) or MagicLink (passwordless email)
3333
- ✅ One signature per user/document (database enforced)
3434
- ✅ Immutable audit trail
3535
- ✅ Expected signers tracking with email reminders
@@ -45,7 +45,9 @@ Prove that collaborators have read and acknowledged important documents with **E
4545
### Prerequisites
4646

4747
- Docker & Docker Compose
48-
- OAuth2 credentials (Google, GitHub, or GitLab)
48+
- **At least ONE authentication method**:
49+
- OAuth2 credentials (Google, GitHub, or GitLab), OR
50+
- SMTP server for MagicLink (passwordless email authentication)
4951

5052
### Installation
5153

@@ -111,15 +113,31 @@ POSTGRES_USER=ackifyr
111113
POSTGRES_PASSWORD=your_secure_password
112114
POSTGRES_DB=ackify
113115

114-
# OAuth2 (example with Google)
116+
# Security (generate with: openssl rand -base64 32)
117+
ACKIFY_OAUTH_COOKIE_SECRET=your_base64_secret
118+
119+
# ============================================================================
120+
# Authentication (choose AT LEAST ONE method)
121+
# ============================================================================
122+
123+
# Option 1: OAuth2 (Google, GitHub, GitLab, custom)
115124
ACKIFY_OAUTH_PROVIDER=google
116125
ACKIFY_OAUTH_CLIENT_ID=your_client_id
117126
ACKIFY_OAUTH_CLIENT_SECRET=your_client_secret
118127

119-
# Security (generate with: openssl rand -base64 32)
120-
ACKIFY_OAUTH_COOKIE_SECRET=your_base64_secret
128+
# Option 2: MagicLink (passwordless email authentication)
129+
# ACKIFY_MAIL_HOST=smtp.example.com
130+
# ACKIFY_MAIL_PORT=587
131+
# ACKIFY_MAIL_USERNAME=your_smtp_username
132+
# ACKIFY_MAIL_PASSWORD=your_smtp_password
133+
# ACKIFY_MAIL_FROM=noreply@example.com
121134
```
122135

136+
**Auto-detection**:
137+
- OAuth is enabled automatically if `ACKIFY_OAUTH_CLIENT_ID` and `ACKIFY_OAUTH_CLIENT_SECRET` are set
138+
- MagicLink is enabled automatically if `ACKIFY_MAIL_HOST` is configured
139+
- You can use **both methods simultaneously** for maximum flexibility
140+
123141
See [docs/en/configuration.md](docs/en/configuration.md) for all options.
124142

125143
---
@@ -175,7 +193,7 @@ See [docs/en/configuration.md](docs/en/configuration.md) for all options.
175193
https://your-domain.com/?doc=security_policy_2025
176194
```
177195

178-
User authenticates via OAuth2 and signs with one click.
196+
User authenticates (OAuth2 or MagicLink) and signs with one click.
179197

180198
### Embed in Your Tools
181199

README_FR.md

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Prouvez que vos collaborateurs ont lu et pris connaissance de documents importan
2929

3030
**Fonctionnalités clés** :
3131
- ✅ Signatures cryptographiques Ed25519
32-
- ✅ Authentification OAuth2 (Google, GitHub, GitLab, custom)
32+
-**Authentification flexible** : OAuth2 (Google, GitHub, GitLab, custom) ou MagicLink (email sans mot de passe)
3333
- ✅ Une signature par utilisateur/document (contrainte base de données)
3434
- ✅ Piste d'audit immutable
3535
- ✅ Tracking signataires attendus avec rappels email
@@ -45,7 +45,9 @@ Prouvez que vos collaborateurs ont lu et pris connaissance de documents importan
4545
### Prérequis
4646

4747
- Docker & Docker Compose
48-
- Credentials OAuth2 (Google, GitHub, ou GitLab)
48+
- **Au moins UNE méthode d'authentification** :
49+
- Credentials OAuth2 (Google, GitHub, ou GitLab), OU
50+
- Serveur SMTP pour MagicLink (authentification email sans mot de passe)
4951

5052
### Installation
5153

@@ -111,15 +113,31 @@ POSTGRES_USER=ackifyr
111113
POSTGRES_PASSWORD=votre_mot_de_passe_securise
112114
POSTGRES_DB=ackify
113115

114-
# OAuth2 (exemple avec Google)
116+
# Sécurité (générer avec: openssl rand -base64 32)
117+
ACKIFY_OAUTH_COOKIE_SECRET=votre_secret_base64
118+
119+
# ============================================================================
120+
# Authentification (choisir AU MOINS UNE méthode)
121+
# ============================================================================
122+
123+
# Option 1 : OAuth2 (Google, GitHub, GitLab, custom)
115124
ACKIFY_OAUTH_PROVIDER=google
116125
ACKIFY_OAUTH_CLIENT_ID=votre_client_id
117126
ACKIFY_OAUTH_CLIENT_SECRET=votre_client_secret
118127

119-
# Sécurité (générer avec: openssl rand -base64 32)
120-
ACKIFY_OAUTH_COOKIE_SECRET=votre_secret_base64
128+
# Option 2 : MagicLink (authentification email sans mot de passe)
129+
# ACKIFY_MAIL_HOST=smtp.example.com
130+
# ACKIFY_MAIL_PORT=587
131+
# ACKIFY_MAIL_USERNAME=votre_utilisateur_smtp
132+
# ACKIFY_MAIL_PASSWORD=votre_mot_de_passe_smtp
133+
# ACKIFY_MAIL_FROM=noreply@example.com
121134
```
122135

136+
**Auto-détection** :
137+
- OAuth activé automatiquement si `ACKIFY_OAUTH_CLIENT_ID` et `ACKIFY_OAUTH_CLIENT_SECRET` sont définis
138+
- MagicLink activé automatiquement si `ACKIFY_MAIL_HOST` est configuré
139+
- Vous pouvez utiliser **les deux méthodes simultanément** pour une flexibilité maximale
140+
123141
Voir [docs/fr/configuration.md](docs/fr/configuration.md) pour toutes les options.
124142

125143
---
@@ -175,7 +193,7 @@ Voir [docs/fr/configuration.md](docs/fr/configuration.md) pour toutes les option
175193
https://votre-domaine.com/?doc=politique_securite_2025
176194
```
177195

178-
L'utilisateur s'authentifie via OAuth2 et signe en un clic.
196+
L'utilisateur s'authentifie (OAuth2 ou MagicLink) et signe en un clic.
179197

180198
### Intégrer dans vos Outils
181199

0 commit comments

Comments
 (0)