Skip to content

Sécurisation OAuth2/OIDC (keycloak, geonode, georchestra) #425

Description

@Gaetanbrl

Description

Dans le cadre d’un travail demandé par l’UMR LISA de l’Université de Corse, nous souhaitons faire évoluer mviewer studio afin qu’il puisse utiliser une authentification OpenID Connect (OIDC), par exemple avec :

  • un GeoNode configuré comme fournisseur OIDC ;
  • Keycloak ;
  • tout autre fournisseur compatible OpenID Connect.

Actuellement, mviewer studio délègue l’authentification à geOrchestra. Le Gateway transmet au backend les informations de l’utilisateur dans les en-têtes HTTP sec-username, sec-org, sec-roles, etc.

Le backend ne réalise donc pas lui-même l’authentification : il construit l’utilisateur courant à partir de ces en-têtes. Les rôles sont retournés par /api/user, mais ne sont actuellement pas utilisés pour autoriser ou interdire des fonctionnalités.

L’objectif est d’ajouter un mode d’authentification OIDC tout en conservant la compatibilité avec geOrchestra.

La logique métier de mviewer studio devra rester indépendante du fournisseur d’identité utilisé.

Avantages

  • conserver la compatibilité avec les installations geOrchestra existantes ;
  • permettre un déploiement sécurisé hors geOrchestra ;
  • utiliser un fournisseur standard tel que GeoNode, Keycloak ou un fournisseur Django ;
  • bénéficier du SSO ;
  • ne pas exposer les jetons OIDC au frontend.

Modes d’authentification

Le mode sera sélectionné par une variable d’environnement :

MVIEWERSTUDIO_AUTH_MODE=proxy

ou :

MVIEWERSTUDIO_AUTH_MODE=authlib

ou, lorsque la protection est déléguée à un reverse proxy :

MVIEWERSTUDIO_AUTH_MODE=oauth2-proxy

Le mode oauth2-proxy devra être résolu par le backend vers le mode interne proxy. La configuration geOrchestra existante devra rester prise en charge avec MVIEWERSTUDIO_AUTH_TYPE=georchestra.

Le mode authlib est réservé au client OIDC directement géré par mviewer studio. Un mode public (ou anonymous) reste disponible pour les installations non protégées ; le mode auto résout le mode à partir de la configuration et des en-têtes présents.

Mode résolu Configuration recommandée Cas d’utilisation Responsable de l’authentification
proxy (geOrchestra) MVIEWERSTUDIO_AUTH_TYPE=georchestra Installation existante avec geOrchestra Gateway et en-têtes sec-*. geOrchestra Gateway
proxy (oauth2-proxy) MVIEWERSTUDIO_AUTH_MODE=oauth2-proxy Protection OIDC déportée devant mviewer studio, notamment avec Keycloak. oauth2-proxy et le fournisseur OIDC
authlib MVIEWERSTUDIO_AUTH_MODE=authlib Client OIDC directement intégré à mviewer studio, par exemple avec GeoNode ou Keycloak. mviewer studio, via Authlib
public MVIEWERSTUDIO_AUTH_MODE=public Instance volontairement accessible sans authentification. Aucun
auto MVIEWERSTUDIO_AUTH_MODE=auto Détection technique à partir des en-têtes proxy ou de la configuration Authlib ; à réserver aux déploiements maîtrisés. Dépend de la détection

proxy sera le nom interne du mode commun aux intégrations geOrchestra et oauth2-proxy. Pour une nouvelle configuration avec oauth2-proxy, utiliser la valeur explicite MVIEWERSTUDIO_AUTH_MODE=oauth2-proxy.

Mode proxy : geOrchestra Gateway ou oauth2-proxy

Le fonctionnement actuel sera conservé :

geOrchestra Gateway → en-têtes sec-* → utilisateur mviewer studio

Le Gateway reste responsable de l’authentification et de la redirection vers sa mire de connexion.

Le mode oauth2-proxy devra prendre en charge les en-têtes courants transmis par le composant tiers :

En-tête Information transmise Utilisation dans mviewer studio
X-Auth-Request-User Identifiant de l’utilisateur Identifiant de secours lorsque le nom d’utilisateur préféré est absent.
X-Auth-Request-Preferred-Username Nom d’utilisateur préféré Identifiant principal de l’utilisateur.
X-Auth-Request-Given-Name Prénom Alimentation du champ firstname.
X-Auth-Request-Family-Name Nom Alimentation du champ lastname.
X-Auth-Request-Groups Groupes ou rôles Alimentation de l’organisation et des rôles.
X-Auth-Request-Access-Token Jeton d’accès OIDC Source de secours pour les claims lorsqu’ils ne sont pas fournis par les en-têtes.

Mode OIDC

mviewer studio devient un client OpenID Connect.

L’implémentation côté backend s’appuiera sur la bibliothèque open source Authlib, notamment pour la découverte OIDC, le flux Authorization Code avec PKCE, la validation des réponses et la gestion des jetons côté serveur.

Le fournisseur devra de préférence exposer son document de découverte :

<issuer>/.well-known/openid-configuration

Un client confidentiel mviewer studio devra être déclaré avec notamment :

Paramètre Description
client_id Identifiant public du client OIDC déclaré pour mviewer studio.
client_secret Secret associé au client confidentiel ; il est conservé exclusivement côté backend.
redirect_uri URL de callback de mviewer studio, appelée après l’authentification auprès du fournisseur.
post_logout_redirect_uri URL de retour vers mviewer studio après la déconnexion du fournisseur.

Le flux utilisé sera l’Authorization Code Flow, avec PKCE lorsque cela est possible.

Les scopes par défaut seront :

openid profile email

Le fournisseur devra fournir les informations nécessaires à mviewer studio :

identifiant, prénom, nom, organisation et rôles

Le backend devra normaliser les aliases de claims les plus courants. La configuration d’un mapping arbitraire pour chaque attribut devra pouvoir couvrir les fournisseurs non standards.

L’organisation est particulièrement importante, car elle détermine les espaces de travail et de publication.

Déploiement avec oauth2-proxy

Le mode oauth2-proxy permet de déployer mviewer studio derrière oauth2-proxy, qui assure l’authentification OIDC et la protection des accès en amont de l’application.

Navigateur → oauth2-proxy → mviewer studio
                 ↓
         fournisseur OIDC

Dans cette architecture, mviewer studio ne réalisera ni redirection vers le fournisseur d’identité ni gestion de jetons OIDC. Il construira l’utilisateur courant à partir d’en-têtes HTTP de confiance transmis par oauth2-proxy, avec un fallback possible sur le jeton d’accès transmis par le proxy. Les noms d’en-têtes reconnus devront être configurables si nécessaire.

Le backend ne devra pas être accessible directement depuis l’extérieur afin d’empêcher l’usurpation de ces en-têtes.

Impacts dans mviewer studio

Prérequis

Mode Prérequis
geOrchestra Gateway Disposer d’un Gateway geOrchestra protégeant l’accès à mviewer studio et transmettant les en-têtes sec-username, sec-firstname, sec-lastname, sec-org et sec-roles.
authlib Disposer d’un fournisseur OIDC accessible avec un document de découverte, déclarer mviewer studio comme client confidentiel, configurer les URL de callback et de déconnexion, fournir les claims attendus et utiliser HTTPS en production.
oauth2-proxy Déployer oauth2-proxy devant mviewer studio, le configurer avec un fournisseur OIDC et un client dédié, transmettre les en-têtes d’identité attendus et empêcher l’accès direct au backend.
public Ne requiert aucun fournisseur d’identité ; ce mode doit être limité aux instances volontairement publiques.
auto Réunir les prérequis du mode qui sera détecté ; ce mode est réservé aux déploiements maîtrisés afin d’éviter une sélection non souhaitée.

Backend

Abstraction de l’authentification

Le backend devra s’appuyer sur une résolution commune des modes et des adaptateurs d’identité :

MVIEWERSTUDIO_AUTH_MODE
├── proxy     → adaptateur geOrchestra ou OIDC transmis par reverse proxy
├── authlib   → session OIDC gérée par l’application
└── public    → utilisateur anonyme

Structure possible :

src/auth_mode.py
src/services/auth/authlib.py
src/utils/auth/georchestra.py
src/utils/auth/oidc.py
src/utils/auth/authlib_session.py

Les trois modes devront produire le même modèle utilisateur interne :

username
firstname
lastname
organisation
normalize_name
roles

Le reste du backend pourra ainsi continuer à utiliser le modèle User existant.

Routes OIDC

Les routes exposées sont :

/auth/login
/auth/callback
/logout
/api/logout

Elles permettront d’ouvrir la mire OIDC, de traiter le retour d’authentification et de fermer la session.

Ces routes doivent rester accessibles sans session. Les pages de mviewer studio et les endpoints métier seront, eux, protégés.

Le parcours attendu est :

/mviewer studio/?app=123
    ↓
/auth/login?next=/mviewer studio/?app=123
    ↓
mire du fournisseur OIDC
    ↓
/auth/callback
    ↓
/mviewer studio/?app=123

Le paramètre next devra être validé afin d’éviter les redirections vers un site externe.

Protection de mviewer studio

Le comportement attendu est :

navigation non authentifiée → HTTP 302 vers /auth/login
appel API non authentifié    → HTTP 401

Cela évite qu’un appel JavaScript reçoive une page HTML de connexion à la place d’une réponse API.

En mode proxy, aucune redirection OIDC ne sera déclenchée par mviewer studio : la protection reste respectivement assurée par le Gateway geOrchestra ou par oauth2-proxy.

Sessions et jetons

Les jetons OIDC ne devront pas être exposés au frontend.

Ils seront gérés par le backend au moyen d’une session et d’un cookie configuré avec :

Secure
HttpOnly
SameSite=Lax

Le backend devra utiliser une session serveur pour stocker les jetons et les claims. La configuration explicite des attributs de cookie Secure, HttpOnly et SameSite devra être assurée pour chaque déploiement de production.

Configuration

Exemple de configuration :

MVIEWERSTUDIO_AUTH_MODE=authlib

MVIEWERSTUDIO_AUTHLIB_ISSUER=https://auth.example.org
# Ou, de façon explicite : MVIEWERSTUDIO_AUTHLIB_METADATA_URL=https://auth.example.org/.well-known/openid-configuration
MVIEWERSTUDIO_AUTHLIB_CLIENT_ID=mviewerstudio
MVIEWERSTUDIO_AUTHLIB_CLIENT_SECRET=secret
MVIEWERSTUDIO_AUTHLIB_SCOPE="openid profile email"

MVIEWERSTUDIO_AUTHLIB_GROUPS_CLAIM=groups
MVIEWERSTUDIO_AUTHLIB_ALLOWED_GROUPS=editor,admin
OIDC_END_SESSION_ENDPOINT=https://auth.example.org/logout

Les claims utilisateur reconnus nativement sont notamment preferred_username, username, email, sub, given_name, family_name, organization / organisation / org, roles, realm_access.roles et les claims de groupes usuels. Le claim de groupes peut être défini par MVIEWERSTUDIO_AUTHLIB_GROUPS_CLAIM (liste de noms séparés par des virgules).

Les chemins de claims imbriqués génériques ne sont pas pris en charge à ce jour. Le cas Keycloak realm_access.roles est pris en charge nativement.

Exemple de configuration des en-têtes transmis par un reverse proxy :

MVIEWERSTUDIO_AUTH_MODE=oauth2-proxy
MVIEWERSTUDIO_AUTH_TYPE=keycloak
OAUTH2_PROXY_OIDC_ISSUER_URL=https://auth.example.org/realms/mviewer
OAUTH2_PROXY_CLIENT_ID=mviewerstudio

Une configuration de référence Docker / reverse proxy avec oauth2-proxy devra être fournie dans la documentation.

Organisation

Le claim d’organisation devra être équivalent à l’actuel sec-org.

Deux comportements devront être configurables : utiliser DEFAULT_ORG lorsque le claim est absent, ou rendre le claim obligatoire et retourner 403 Forbidden lorsqu’il est absent ou vide.

Rôles

Les rôles seront conservés dans le modèle utilisateur et retournés par /api/user.

Dans un premier temps, aucun contrôle fonctionnel supplémentaire ne sera ajouté à partir des rôles. Ils seront conservés dans le modèle utilisateur et retournés par /api/user. Le filtrage des rôles et groupes restera assuré en amont par le Gateway ou par oauth2-proxy.

Route utilisateur

La route existante :

GET /api/user

devra conserver le même format de réponse, que l’utilisateur provienne de geOrchestra, d’OIDC ou d’un reverse proxy.

Frontend

Le frontend devra rester indépendant du fournisseur d’identité.

Il continuera à récupérer l’utilisateur courant via :

GET /api/user

En mode authlib :

  • un appel API retournant 401 utilise l’URL login_url renvoyée par le backend ;
  • la déconnexion utilise /logout ou /api/logout ;
  • aucun jeton ni secret OIDC ne sera stocké dans le navigateur.

En mode geOrchestra, les mécanismes de connexion et de déconnexion existants seront conservés.

En mode proxy, les mécanismes de connexion et de déconnexion sont ceux du composant tiers, par exemple oauth2-proxy.

Critères d'acceptation

Voici les critères que je propose :

  • Un utilisateur authentifié via le Gateway geOrchestra peut accéder à mviewer studio.
  • Un utilisateur non authentifié derrière geOrchestra est redirigé vers la mire geOrchestra.
  • Un utilisateur authentifié via le mode authlib peut accéder à mviewer studio.
  • Un utilisateur non authentifié en mode OIDC est redirigé vers la mire du fournisseur configuré.
  • Après authentification, l’utilisateur revient sur mviewer studio.
  • Authlib est utilisé pour l’implémentation du client OIDC côté backend.
  • Le flux Authorization Code utilise PKCE.
  • Les fournisseurs GeoNode, Keycloak et tout fournisseur OIDC compatible peuvent être utilisés par configuration.
  • L’accès direct aux routes métier sans authentification est impossible.
  • Les appels API non authentifiés retournent 401.
  • Le frontend utilise l’URL login_url fournie par l’API après réception d’un 401 en mode authlib.
  • Le paramètre next est limité à une URL locale de mviewer studio.
  • Un utilisateur authentifié mais non autorisé reçoit 403.
  • Après déconnexion, l’accès à mviewer studio provoque une nouvelle redirection vers la mire.
  • Le mode geOrchestra existant reste compatible via MVIEWERSTUDIO_AUTH_TYPE=georchestra.
  • Un déploiement derrière oauth2-proxy permet d’accéder à mviewer studio après authentification.
  • En mode proxy, mviewer studio ne gère ni redirection vers le fournisseur d’identité ni session OIDC locale.
  • Les attributs utilisateur transmis par oauth2-proxy sont correctement convertis vers le modèle utilisateur interne.
  • Le backend ne peut pas être accédé publiquement en contournant oauth2-proxy.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions