{"id":31618087,"url":"https://github.com/dbeaumont/chat-app","last_synced_at":"2026-05-09T05:33:12.055Z","repository":{"id":315605379,"uuid":"1060172142","full_name":"dbeaumont/chat-app","owner":"dbeaumont","description":"Exemple de chat fullstack angular + java springboot + postgresql","archived":false,"fork":false,"pushed_at":"2025-11-09T21:26:42.000Z","size":225,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"develop","last_synced_at":"2025-11-09T23:22:38.458Z","etag":null,"topics":["angular","docker-compose","springboot3"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dbeaumont.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/security-cheatsheet.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-19T13:45:29.000Z","updated_at":"2025-11-09T21:26:45.000Z","dependencies_parsed_at":"2025-09-19T16:26:12.213Z","dependency_job_id":"0627f151-aef7-46ce-add9-bb1647ffd230","html_url":"https://github.com/dbeaumont/chat-app","commit_stats":null,"previous_names":["dbeaumont/chat-app"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/dbeaumont/chat-app","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbeaumont%2Fchat-app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbeaumont%2Fchat-app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbeaumont%2Fchat-app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbeaumont%2Fchat-app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dbeaumont","download_url":"https://codeload.github.com/dbeaumont/chat-app/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dbeaumont%2Fchat-app/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32808455,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"online","status_checked_at":"2026-05-09T02:00:06.633Z","response_time":123,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","docker-compose","springboot3"],"created_at":"2025-10-06T13:45:27.962Z","updated_at":"2026-05-09T05:33:12.045Z","avatar_url":"https://github.com/dbeaumont.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Chat App — Stack complète (Angular + Spring Boot + PostgreSQL + Keycloak + Caddy)\n\n\u003e Démarrage rapide avec `docker compose up --build` ou avec le facilitateur `make up`\n\u003e Frontend : https://localhost • Backend : https://localhost/api • Keycloak : https://localhost/keycloak\n\n## Sommaire\n- [Chat App — Stack complète (Angular + Spring Boot + PostgreSQL + Keycloak + Caddy)](#chat-app--stack-complète-angular--spring-boot--postgresql--keycloak)\n  - [Sommaire](#sommaire)\n  - [Vue d’ensemble](#vue-densemble)\n  - [Architecture \\\u0026 Services](#architecture--services)\n  - [Endpoints](#endpoints)\n    - [HTTPS (via Caddy)](#https-via-caddy)\n    - [HTTP](#http)\n  - [Sécurité (OIDC / JWT / CORS)](#sécurité-oidc--jwt--cors)\n    - [Principe](#principe)\n    - [Alignement `issuer` et JWK (important en Docker)](#alignement-issuer-et-jwk-important-en-docker)\n    - [CORS](#cors)\n  - [Variables d’environnement](#variables-denvironnement)\n  - [Démarrage](#démarrage)\n    - [Fichier `.env` minimal (optionnel)](#fichier-env-minimal-optionnel)\n  - [Tests manuels (curl)](#tests-manuels-curl)\n  - [Diagramme Mermaid détaillé](#diagramme-mermaid-détaillé)\n  - [Dépannage](#dépannage)\n  - [Documentation supplémentaire](#documentation-supplémentaire)\n  - [Smoke tests (script)](#smoke-tests-script)\n  - [Make (raccourcis)](#make-raccourcis)\n    - [Générer un `.env`](#générer-un-env)\n    - [Générer un `.env.dev` (développement)](#générer-un-envdev-développement)\n    - [Commandes principales :](#commandes-principales)\n    - [Développement local (hors Docker) :](#développement-local-hors-docker)\n    - [Outils \\\u0026 utilitaires :](#outils--utilitaires)\n    - [Keycloak (import de realm prêt à l’emploi)](#keycloak-import-de-realm-prêt-à-lemploi)\n  - [Troubleshooting](#troubleshooting)\n\n## Vue d’ensemble\nApplication de démonstration **SPA Angular** (front) + **API Spring Boot** (back), sécurisée par **Keycloak (OIDC)**, avec le terminateur SSL **Caddy**.  \nLa **base PostgreSQL** stocke les messages. La télémétrie est exposée à **Prometheus/Grafana**.\n\n## Architecture \u0026 Services\n- **frontend** (Angular + Nginx) : sert l’UI et émet des appels HTTP vers `backend`.\n- **backend** (Spring Boot 3) : API REST (`/api/**`), Resource Server **JWT**.\n- **keycloak** : serveur OIDC, gère identité \u0026 jetons, flow **Authorization Code Flow with PKCE**.\n- **db (PostgreSQL)** : stockage applicatif.\n- **prometheus / grafana / cadvisor** : monitoring (optionnel).\n- **caddy** : terminateur SSL et proxy.\n\n## Endpoints\n\n### HTTPS (via Caddy)\n- **frontend** : https://localhost/\n- **keycloak** : https://localhost/keycloak\n- **backend**  : https://localhost/api\n\n### HTTP\n- **prometheus** : http://localhost:9090\n- **grafana** : http://localhost:3000\n\n## Sécurité (OIDC / JWT / CORS)\n### Principe\n- L’utilisateur s’authentifie via **OIDC Authorization Code + PKCE** auprès de **Keycloak**.\n- Le **frontend** (SPA dans le navigateur) récupère un **access token (JWT)** et l’envoie en header `Authorization: Bearer …` vers le **backend**.\n- Le **backend** valide le JWT (signature + `iss` + audience/exp) avec le **JWK Set URI** fourni par Keycloak.\n\n### Alignement `issuer` et JWK (important en Docker)\n- Le navigateur parle à Keycloak via **`http://localhost:8081`** → le JWT contient `iss = http://localhost:8081/realms/demo`.\n- Le conteneur backend résout Keycloak via **`http://keycloak:8080`** (réseau Docker).  \n  **Solution mise en place (Option A)** :\n  - `OIDC_ISSUER_URI=http://localhost:8081/realms/demo`\n  - `SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=http://keycloak:8080/realms/demo/protocol/openid-connect/certs`\n\nAinsi **l’issuer** correspond bien au token du navigateur, tandis que **la découverte des clés** se fait côté réseau Docker.\n\n### CORS\n- Autorise les origines configurées par `CORS_ALLOWED_ORIGINS` (voir `docker-compose.yml`).  \n- Les requêtes **préflight** (`OPTIONS`) sont **permises** côté backend.\n\n## Variables d’environnement\nPrincipales variables (voir `docker-compose.yml`) :\n\n| Variable | Service | Rôle |\n|---|---|---|\n| `API_BASE_URL` | frontend | URL publique de l’API (injection runtime via Nginx) |\n| `OIDC_ISSUER_URI` | backend | Issuer OIDC attendu par la validation JWT |\n| `SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI` | backend | Emplacement du JWK Set (depuis le réseau Docker) |\n| `CORS_ALLOWED_ORIGINS` | backend | Liste des origines autorisées (CORS) |\n| `SPRING_DATASOURCE_*` | backend | Connexion PostgreSQL |\n| `SERVER_PORT` | backend | Port interne Spring Boot (mappé à 9080 par Docker) |\n\n## Démarrage\n```bash\ndocker compose up --build\n```\n\n- Frontend : http://localhost:8888  \n- Backend : http://localhost:9080  \n- Keycloak : http://localhost:8081\n\n### Fichier `.env`\nPour créer un fichier `.env` (à la racine du projet) :\n```env\nOIDC_ISSUER_URI=http://localhost:8081/realms/demo\nSPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI=http://keycloak:8080/realms/demo/protocol/openid-connect/certs\nCORS_ALLOWED_ORIGINS=http://localhost,http://localhost:8888\n```\n\n## Tests manuels (curl)\n1) **Vérifier CORS / préflight** (depuis n’importe quel shell) :\n```bash\ncurl -i -X OPTIONS http://localhost:9080/api/messages   -H 'Origin: http://localhost:8888'   -H 'Access-Control-Request-Method: GET'\n```\nAttendu : `HTTP/1.1 200 OK` et entêtes CORS.\n\n2) **Appeler l’API avec un JWT** : récupérez d’abord un token via l’appli front (onglet réseau), puis :\n```bash\nexport TOKEN='eyJhbGciOiJSUzI1NiIsInR5cCI...'   # votre access_token\ncurl -i http://localhost:9080/api/messages   -H \"Authorization: Bearer $TOKEN\"\n```\n\n## Diagramme Mermaid détaillé\n\u003e **Flux complet OIDC (Authorization Code + PKCE) + appels API JWT + CORS**\n\n```mermaid\nsequenceDiagram\n    participant U as Utilisateur (Navigateur)\n    participant F as Frontend (Angular, http://localhost:8888)\n    participant B as Backend (Spring Boot, http://localhost:9080)\n    participant K as Keycloak (http://localhost:8081 / http://keycloak:8080)\n    participant DB as PostgreSQL\n\n    Note over F,K: Découverte OIDC\u003cbr/\u003eF charge le discovery document\n    U-\u003e\u003eF: Accès UI\n    F-\u003e\u003eK: GET /.well-known/openid-configuration\n    K--\u003e\u003eF: JSON (issuer, auth_endpoint, token_endpoint, jwks_uri, ...)\n\n    Note over U,F: Login OIDC (Authorization Code + PKCE)\n    U-\u003e\u003eK: Redirection vers /auth (code+challenge)\n    K--\u003e\u003eU: Écran de login\n    U-\u003e\u003eK: Credentials\n    K--\u003e\u003eF: redirect_uri?code=...\u0026state=...\n\n    Note over F,K: Échange du code contre un token\n    F-\u003e\u003eK: POST /token (code + verifier)\n    K--\u003e\u003eF: { access_token (JWT), id_token, expires_in, ... }\n\n    Note over F,B: Appels API avec Bearer\n    U-\u003e\u003eF: Action UI (ex: lister messages)\n    F-\u003e\u003eB: GET /api/messages (Authorization: Bearer access_token)\n    B-\u003e\u003eK: GET JWK Set (jwks_uri) [via http://keycloak:8080]\n    K--\u003e\u003eB: {keys:[{kid,kty,n,e,...}]}\n    B--\u003e\u003eB: Validation JWT (signature, iss=http://localhost:8081/realms/demo, exp, ...)\n    B-\u003e\u003eDB: SELECT * FROM message\n    DB--\u003e\u003eB: Résultats\n    B--\u003e\u003eF: 200 OK (JSON)\n    F--\u003e\u003eU: Render UI\n\n    Note over U,B: CORS préflight si requis\n    U-\u003e\u003eB: OPTIONS /api/messages (Origin: http://localhost:8888)\n    B--\u003e\u003eU: 200 + Access-Control-Allow-*\n```\n\n## Dépannage\n- **401 Unauthorized** :  \n  - Le header `Authorization` est absent côté front → vérifier `AuthInterceptor` et session OIDC.  \n  - Claim `iss` différente de `OIDC_ISSUER_URI` → corriger `OIDC_ISSUER_URI`.  \n  - Backend ne peut pas joindre le JWK → vérifier `SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI` (URL côté réseau Docker).\n- **CORS bloqué** :  \n  - Vérifier `CORS_ALLOWED_ORIGINS` et que `OPTIONS /**` est bien `permitAll()`.\n- **Actuator health** : http://localhost:9080/actuator/health (ouvert sans auth).\n\n---\n\n## Documentation supplémentaire\n- [OIDC — Séquence détaillée](docs/oidc-sequence.md)\n- [PKCE — State Machine](docs/pkce-state-machine.md)\n- [Topologie réseau Docker/Host](docs/network-topology.md)\n- [Sécurité — Cheatsheet](docs/security-cheatsheet.md)\n\n- [Troubleshooting](docs/troubleshooting.md)\n\n## Smoke tests (script)\nUn script de **tests rapides** est fourni :\n```bash\nTOKEN=$(./scripts/get-access-token.sh)   # génère un access token\n```\n\n```bash\n./scripts/smoke-tests.sh                   # utilise $TOKEN si défini\nTOKEN=eyJ... ./scripts/smoke-tests.sh      # passe le token inline\n./scripts/smoke-tests.sh --token eyJ...    # passe le token en argument\n```\n\nOptions : `--host`, `--api`, `--front`, `--kc` pour personnaliser les ports/host.\n\n## Make (raccourcis)\n\n### Générer un `.env`\n```bash\nmake env   # crée/écrase .env avec des valeurs par défaut (profils Spring inclus)\n```\n\n\n### Générer un `.env.dev` (développement)\n```bash\nmake env-dev   # crée .env.dev (ne remplace pas .env)\n# Pour l'utiliser avec docker compose:\n#   docker compose --env-file .env.dev up --build\n# ou configurez votre outil/IDE pour utiliser .env.dev\n```\n\n\n### Commandes principales :\n\n```bash\nmake up            # démarre tout (build si nécessaire)\nmake down          # arrête et supprime\nmake logs          # logs de tous les services\nmake smoke TOKEN=eyJ...   # lance les smoke tests avec un access token\nmake restart       # restart complet\nmake ps            # statut des services\n```\n\n### Développement local (hors Docker) :\n\n```bash\nmake backend-test        # tests unitaires backend\nmake backend-it          # tests d'intégration (profile it)\nmake backend-dev-run     # lance le backend (profil dev)\nmake frontend-install    # npm ci\nmake frontend-build      # build prod frontend\nmake frontend-serve      # dev server Angular\n```\n\n### Outils \u0026 utilitaires :\n\n```bash\nmake keycloak-restart    # restart du service Keycloak\nmake keycloak-admin      # affiche l'URL de console Admin\nmake clean               # nettoyage dist/ et target/\nmake prune               # prune docker (dangling)\n```\n\n### Keycloak (import de realm prêt à l’emploi)\nUn export **realm** est fourni : `keycloak/realm-demo-with-client.json`.  \nIl est importé automatiquement au démarrage grâce au volume et au paramètre `--import-realm`.\n\n## Troubleshooting\n\nTester la santé du server :\n```bash\ncurl -i http://localhost:9080/actuator/health\n```\n\nTester l'appel d'une api :\n```bash\nTOKEN=$(./scripts/get-access-token.sh)   # DEV uniquement\ncurl -i http://localhost:9080/api/messages -H \"Authorization: Bearer $TOKEN\"\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdbeaumont%2Fchat-app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdbeaumont%2Fchat-app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdbeaumont%2Fchat-app/lists"}