Post

🔐SSO Authelia

🔐SSO Authelia

Authelia est un serveur d’authentification et d’autorisation open source fournissant une authentification Ă  deux facteurs et une authentification unique (SSO) pour vos applications via un portail Web. Authelia se place devant votre reverse proxy (Nginx, Traefik, Caddy) et intercepte les requĂȘtes pour vĂ©rifier l’authentification.

SSO Authelia

Site web officiel : https://www.authelia.com/

  • Open source
  • SSO
  • Authentification 2F avec Yubikey
  • OTP basĂ© sur le temps avec Google Authenticator
  • Prend en charge LDAP
  • Prend en charge Active Directory
  • Fonctionne avec les proxys inverses
  • Notifications push avec duo
  • Bloquer les attaques de force brute
  • VĂ©rification d’identitĂ©
  • Une meilleure gestion des mots de passe
  • Flux de travail flexible
  • UI intuitif

Autres liens

Prérequis et liens

Un serveur debian + nginx

Installation binaire

Authelia releases

Installation binaire

1
2
3
4
5
6
7
8
9
10
11
# Téléchargement 
wget https://github.com/authelia/authelia/releases/download/v4.39.20/authelia-v4.39.20-linux-amd64.tar.gz
tar xzvf authelia-v4.39.20-linux-amd64.tar.gz
# Mise Ă  jour binaire
sudo systemctl stop authelia.service 
sudo cp authelia /usr/bin/authelia
sudo systemctl start authelia.service 
# Effacer
rm authelia*
# version
authelia -v  # authelia version v4.39.20

Structure

1
2
3
4
5
6
/etc/authelia/
├── authelia.log
└── configuration.yml
# droits
ls -ld /etc/authelia/
drwxr-xr-x 2 authelia authelia 4096  8 août  14:33 /etc/authelia/

Configurer Authelia

Configuration authelia sur serveur cwwk rnmkcy.eu 192.168.0.205

  • Serveur: 127.0.0.1:9091
  • Authentification: TOTP + Webauthnn
  • Notification: SMTP
  • Gestion utilisateurs: LLDAP ou fichier
  • Stockage: Base mysql ou Sqlite3

Gestion utilisateurs

LLDAP est utilisé pour la gestion utilisateur

Fichier base utilisateurs

1
sudo -u authelia nano /opt/authelia/config/users_database.yml

Format yaml:

1
2
3
4
5
6
7
8
9
users:
  mathys:
    disabled: false
    displayname: "Mathys"
    password: "$argon2id$v=19$m=65536,t=3,p=4$..." # voir étape suivante
    email: "mathys@verycloud.fr"
    groups:
      - "admin"
      - "users"

Générer le hash du mot de passe

1
sudo authelia crypto hash generate argon2

Entrez votre mot de passe. Copiez le hash résultant et collez-le dans users_database.yml.

Stockage

Stockage géré par une base mariadb (mysl)
Créer une base mariadb
Host : 127.0.0.1
Base : authelia
Utilisateur : authelia
Mot de passe : Mot_de_passe_base_authelia

En ligne de commande

1
sudo mariadb -e "create database authelia; grant all privileges on authelia.* to authelia@'localhost' identified by 'Mot_de_passe_base_authelia'; flush privileges;"

WebAuthn

ParamĂštre user_verification :

  • discouraged : Le client sera dĂ©couragĂ© de demander une vĂ©rification Ă  l’utilisateur.
  • preferred : Le client, s’il est conforme, demandera Ă  l’utilisateur une vĂ©rification si l’appareil la prend en charge.
  • required : Le client demandera Ă  l’utilisateur une vĂ©rification ou Ă©chouera si l’appareil ne prend pas en charge la vĂ©rification.

attestation_conveyance_preference:

  • none : Le client recevra l’instruction de ne pas effectuer de transfert de propriĂ©tĂ©.
  • indirect : Le client reçoit l’instruction d’effectuer le transfert de propriĂ©tĂ©, mais il peut choisir la maniĂšre de le faire, y compris en utilisant une AC d’anonymisation tierce.
  • direct : Le client recevra l’instruction d’effectuer le transfert avec une dĂ©claration d’attestation directement signĂ©e par le dispositif.

Authelia (configuration.yml)

Configuration /etc/authelia/configuration.yml (épurée)

Etendre Réduire...
###############################################################################
#                           Authelia Configuration                            #
###############################################################################

# Rappel : certains secrets sont fournis via le fichier d'environnement
# /etc/authelia/authelia.env (JWT reset, session, LDAP, MySQL, SMTP, OIDC hmac).
# Ne pas committer ce fichier d'env dans un dépÎt public.

theme: dark

##
## Identity Validation Configuration
##
identity_validation:
  reset_password:
    jwt_secret: 'CHANGEZ_MOI'

server:
  address: 'tcp://127.0.0.1:9091/'
  disable_healthcheck: false
  tls:
    key: ""
    certificate: ""
  endpoints:
    enable_pprof: false
    enable_expvars: false

log:
  level: warn
  file_path: '/etc/authelia/authelia.log'

totp:
  issuer: rnmkcy.eu
  period: 30
  skew: 1
  allowed_digits:
    - 6

webauthn:
  disable: false
  enable_passkey_login: false
  display_name: 'Authelia'
  attestation_conveyance_preference: 'indirect'
  timeout: '60 seconds'
  filtering:
    permitted_aaguids: []
    prohibited_aaguids: []
    prohibit_backup_eligibility: false
  selection_criteria:
    attachment: ''
    discoverability: 'preferred'
    user_verification: 'preferred'
  metadata:
    enabled: false
    validate_trust_anchor: true
    validate_entry: true
    validate_entry_permit_zero_aaguid: false
    validate_status: true
    validate_status_permitted: []
    validate_status_prohibited:
      - 'REVOKED'
      - 'USER_KEY_PHYSICAL_COMPROMISE'
      - 'USER_KEY_REMOTE_COMPROMISE'
      - 'USER_VERIFICATION_BYPASS'
      - 'ATTESTATION_KEY_COMPROMISE'

authentication_backend:
  password_reset:
    disable: false
  refresh_interval: 1m
  ldap:
    implementation: lldap
    address: ldap://127.0.0.1:3890
    timeout: 5s
    start_tls: false
    base_dn: dc=rnmkcy,dc=eu
    additional_users_dn: ou=people
    users_filter: "(&({username_attribute}={input})(objectClass=person))"
    additional_groups_dn: ou=groups
    groups_filter: "(member={dn})"
    user: uid=admin,ou=people,dc=rnmkcy,dc=eu
    password: 'CHANGEZ_MOI'
    attributes:
      mail: 'mail'
      username: 'uid'
      group_name: 'cn'
      display_name: 'displayName'

access_control:
  default_policy: deny
  networks:
    - name: internal
      networks:
        - '192.168.0.0/24'
        - '192.168.10.0/24'
  rules:
    - domain:
        - 'auth.rnmkcy.eu'
      policy: bypass
    - domain:
        - 'site.rnmkcy.eu'
      policy: two_factor

session:
  secret: 'CHANGEZ_MOI'
  name: 'authelia_session'
  same_site: 'lax'
  inactivity: '45m'
  expiration: '12h'
  remember_me: '7d'
  cookies:
    - domain: 'rnmkcy.eu'
      authelia_url: 'https://auth.rnmkcy.eu'
      default_redirection_url: 'https://rnmkcy.eu'
      name: 'authelia_session'
      same_site: 'lax'
      inactivity: '45m'
      expiration: '12h'
      remember_me: '7d'
  redis:
    host: localhost
    port: 6379
    #password: 'CHANGEZ_MOI'
    database_index: 0
    maximum_active_connections: 10
    minimum_idle_connections: 0

regulation:
  max_retries: 3
  find_time: 5m
  ban_time: 15m

storage:
  encryption_key: 'CHANGEZ_MOI'
  mysql:
    address: '127.0.0.1:3306'
    database: authelia
    username: authelia
    password: 'CHANGEZ_MOI'

notifier:
  disable_startup_check: false
  smtp:
    address: 'smtp://yannig.net:587'
    timeout: '5s'
    username: 'yann@yannig.net'
    password: 'CHANGEZ_MOI'
    sender: 'yann@yannig.net'
    identifier: 'yannig.net'
    subject: '[Authelia] {title}'
    startup_check_address: 'yann@yannig.net'
    disable_require_tls: false
    disable_starttls: false
    disable_html_emails: false

identity_providers:
  oidc:
    hmac_secret: 'CHANGEZ_MOI'
    jwks:
      - key_id: 'authelia'
        algorithm: 'RS256'
        use: 'sig'
        certificate_chain: |
          -----BEGIN CERTIFICATE-----
          MIIDFzCCAf+gAwIBAgIQKh+loa2
          -----END CERTIFICATE-----
        key: |
          -----BEGIN PRIVATE KEY-----
          MIIEvQIBADANBgkqhkiG9w0BAQE
          -----END PRIVATE KEY-----
    enable_client_debug_messages: false
    minimum_parameter_entropy: 8
    enforce_pkce: 'public_clients_only'
    enable_pkce_plain_challenge: false
    enable_jwt_access_token_stateless_introspection: false
    discovery_signed_response_alg: 'none'
    discovery_signed_response_key_id: ''
    require_pushed_authorization_requests: false
    lifespans:
      access_token: '1h'
      authorize_code: '1m'
      id_token: '1h'
      refresh_token: '90m'
    cors:
      endpoints:
        - authorization
        - token
        - revocation
        - introspection
      allowed_origins:
        - 'https://immich.rnmkcy.eu'
      allowed_origins_from_client_redirect_uris: false

    clients:
      - client_id: immich
        client_name: Immich OIDC
        # Le client_secret reste dans le YAML (hashed), pas dans l'env.
        client_secret: '$pbkdf2-sha512$310000$/4LKM4la/dw013yHgOr1Ug$vUvPxJY6AKKBaV7SaIC1b/BEtKPnqfhz3MKnGSQFOsTNUFspDnBC/qfBfXx0LgUZdcpZ63gbOEQU8Qz0UKB.KQ'
        public: false
        authorization_policy: 'two_factor'
        redirect_uris:
          - 'https://immich.rnmkcy.eu/auth/login'
          - 'https://immich.rnmkcy.eu/user-settings'
          - 'app.immich:///oauth-callback'
        scopes:
          - 'openid'
          - 'profile'
          - 'email'
        userinfo_signed_response_alg: 'none'
        token_endpoint_auth_method: 'client_secret_post'

      - client_id: 'forgejo'
        client_name: 'Forgejo'
        client_secret: '$pbkdf2-sha512$310000$rhOo4zW8lnTSJmPxzizI4w$8CiHPKElYYhOIFSYyDYE34ToBY.c.iwF5sAmgMm.dnnNLKq9uVAECeb2pk6s12cKosnWoLtnhKvjoLLyZLgvPQ'
        public: false
        authorization_policy: 'two_factor'
        require_pkce: true
        pkce_challenge_method: 'S256'
        redirect_uris:
          - 'https://forgejo.rnmkcy.eu/user/oauth2/authelia/callback'
        scopes:
          - 'openid'
          - 'email'
          - 'profile'
          - 'groups'
        response_types:
          - 'code'
        grant_types:
          - 'authorization_code'
        access_token_signed_response_alg: 'none'
        userinfo_signed_response_alg: 'none'
        token_endpoint_auth_method: 'client_secret_post'

      - client_id: 'portainer'
        client_name: 'Portainer'
        client_secret: '$pbkdf2-sha512$310000$2nPrEUGwRI64lVZAMs.bsw$JPMilMlHhYxMwfbU1dcL1JChJ5o.Uk8iEI297DMgXrAV2wVP/njCBgCfjQBvqtLbM9TNVEStJMKNbc3O99vA0Q'
        public: false
        authorization_policy: 'two_factor'
        require_pkce: false
        pkce_challenge_method: ''
        redirect_uris:
          - 'https://portainer.home.arpa'
        scopes:
          - 'openid'
          - 'profile'
          - 'groups'
          - 'email'
        response_types:
          - 'code'
        grant_types:
          - 'authorization_code'
        access_token_signed_response_alg: 'none'
        userinfo_signed_response_alg: 'none'
        token_endpoint_auth_method: 'client_secret_post'

      - client_id: 'technitium'
        client_name: 'technitium OIDC'
        client_secret: '$pbkdf2-sha512$310000$IxPfnOlpxWpaikXDw.qTSg$1d6xUB2YxVzwg.d7rNF.JhESblbfSgM.nuQJWrZgyj21IGKC7LCDHtMnuLvzNmuGy9Hr3INsL02tCbqXCwHOgQ'
        public: false
        authorization_policy: 'two_factor'
        redirect_uris:
          - 'https://dns.rnmkcy.eu/sso/callback'
        scopes:
          - 'openid'
          - 'profile'
          - 'email'
        userinfo_signed_response_alg: 'none'
        token_endpoint_auth_method: 'client_secret_post'

Vérifier la configuration en mode su

1
sudo authelia validate-config --config /etc/authelia/configuration.yml

Activation service authelia

Lancer, activer et vérifier

1
2
sudo systemctl enable authelia --now
sudo journalctl -u authelia 
1
oct. 30 18:56:33 alder systemd[1]: Started authelia.service - Authelia authentication and authorization server.

auth.rnmkcy.eu

Comme Authelia affiche une page de connexion/authentification, il doit ĂȘtre exĂ©cutĂ© sur un canal de transport cryptĂ© pour Ă©viter les attaques de l’homme du milieu (MITM).

Il ne dĂ©marrera pas tant que ce canal n’aura pas Ă©tĂ© correctement configurĂ©.

Comme d’habitude avec HTTPS, pour dĂ©marrer une connexion TLS, vous avez besoin d’un certificat. La solution la plus simple est d’utiliser Let’s Encrypt ou ZeroSSL avec son outil certbot pour automatiser la rĂ©cupĂ©ration et le renouvellement des certificats. Il ne s’agit pas d’un tutoriel sur la mise en place du HTTPS, donc je liste les Ă©tapes Ă  rĂ©aliser avec un reverse proxy NGINX, n’hĂ©sitez pas Ă  personnaliser si vous utilisez un autre reverse proxy pour le HTTPS.

Proxy nginx

Le fichier /etc/nginx/conf.d/auth.rnmkcy.eu.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
server {
    listen 80;
    server_name auth.rnmkcy.eu;

    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name auth.rnmkcy.eu;

    # Configuration TLS moderne (ciphers, protocoles, etc.)
    include /etc/nginx/conf.d/ssl-modern.inc;

    # Cache de session TLS
    ssl_session_cache shared:SSL:2m;

    # Sécurité HTTP strict transport (HSTS)
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # Durcissement des en-tĂȘtes sĂ©curitĂ©
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;

    location / {
        # Authelia écoute en HTTP sur localhost
        proxy_pass http://127.0.0.1:9091/;

        # HTTP/1.1 vers le backend + pas de Connection: close
        proxy_http_version 1.1;
        proxy_set_header Connection "";

        # Contexte client original
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Contexte de la requĂȘte originale
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Uri $request_uri;
        proxy_set_header X-Forwarded-Method $request_method;

        # URL complùte d’origine (utile pour redirections / logs)
        proxy_set_header X-Original-URL $scheme://$http_host$request_uri;

        # Timeouts raisonnables pour le backend
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }
}

Puis :

1
2
sudo nginx -t
sudo systemctl reload nginx

Ensuite, pointer le navigateur sur https://auth.rnmkcy.eu pour vĂ©rifier que le portail Authelia s’affiche correctement.

Utilisation SSO - Modifier application

Authelia affiche une page de connexion/authentification, il doit ĂȘtre exĂ©cutĂ© sur un canal de transport https

L’application doit ĂȘtre adaptĂ©e pour traiter avec un serveur d’authentification externe.
Lorsque Authelia a validĂ© l’utilisateur, il redirige vers votre application.
Cette redirection contient des en-tĂȘtes spĂ©cifiques que votre application doit traiter pour procĂ©der Ă  sa crĂ©ation de session habituelle (typiquement, votre application Ă©mettra son Set-Cookie ou une requĂȘte de stockage local ou un jeton JWT ou 
).
Il s’agit simplement de contourner la page de connexion.

Nginx snippets

Création dossier snippets

1
sudo mkdir -p /etc/nginx/snippets

Les fichiers de configuration nginx

Le premier extrait est une route interne /authelia qui n’est pas accessible depuis l’internet, qui est utilisĂ©e dans vos applications pour affirmer que la session Authelia est valide.

Snippet authelia.conf

1
sudo nano /etc/nginx/snippets/authelia.conf

Contenu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Internal Authelia verify endpoint
location = /authelia {
    # accessible uniquement en interne (subrequests) — requĂȘtes externes → 404
    internal;
    # n'envoie pas le corps de la requĂȘte au backend (gain d'efficacitĂ©)
    proxy_pass_request_body off;
    # neutralise Content-Length (évite d'envoyer une taille quand pas de body)
    proxy_set_header Content-Length "";
    # transmet l'hĂŽte original ($host) au backend
    proxy_set_header Host $host;
    # URL complÚte d'origine (schéma://host + request_uri)
    proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
    # indique le protocole d'origine (http ou https)
    proxy_set_header X-Forwarded-Proto $scheme;
    # transmet l'hÎte d'origine (inclut le port si présent)
    proxy_set_header X-Forwarded-Host $http_host;
    # transmet l'URI d'origine (chemin + query string)
    proxy_set_header X-Forwarded-Uri $request_uri;
    # ajoute l'IP cliente à X-Forwarded-For (conserve proxys intermédiaires)
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    # envoie l'IP source de la connexion nginx (IP client directe)
    proxy_set_header X-Real-IP $remote_addr;
    # redirige la sous-requĂȘte vers l'API interne Authelia /api/verify sur localhost:9091
    proxy_pass http://127.0.0.1:9091/api/verify;
    # délai maximum pour lire la réponse du backend = 10 secondes
    proxy_read_timeout 10s;
}

Vous aurez aussi besoin d’un snippet pour l’authentification (interne) elle-mĂȘme, donc sauvegardez ce snippet sous /etc/nginx/snippets/auth.conf:
Typiquement, ce snippets fait en sorte que NGINX gĂ©nĂšre une requĂȘte vers l’emplacement interne /authelia Ă  chaque fois qu’une requĂȘte d’utilisateur est faite vers votre application. Il vĂ©rifie le code de rĂ©sultat HTTP de la requĂȘte interne. Si c’est 200, la requĂȘte de l’utilisateur est acceptĂ©e, sinon il est redirigĂ© vers la page de login Authelia.

Snippet auth.conf

1
sudo nano /etc/nginx/snippets/auth.conf

Contenu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Protect with Authelia
# effectue une sous-requĂȘte interne vers /authelia pour vĂ©rifier l'accĂšs
auth_request /authelia;
# stocke l'URL complÚte demandée (schéma://host+URI) dans la variable $target_url
auth_request_set $target_url $scheme://$http_host$request_uri;
# récupÚre le header Remote-User renvoyé par Authelia (nom d'utilisateur authentifié)
auth_request_set $user $upstream_http_remote_user;
# récupÚre le header Remote-Groups renvoyé par Authelia (groupes / rÎles)
auth_request_set $groups $upstream_http_remote_groups;
# transmet l'utilisateur authentifié au backend en tant que header Remote-User
proxy_set_header Remote-User $user;
# transmet les groupes de l'utilisateur au backend en tant que header Remote-Groups
proxy_set_header Remote-Groups $groups;

# en cas de 401 (non autorisé) renvoie une redirection 302 vers le portail d'authentification,
# avec ?rd=<URL d'origine> pour que l'IDP puisse rediriger l'utilisateur vers la ressource demandée aprÚs login
error_page 401 =302 https://auth.rnmkcy.eu/?rd=$target_url;

Exemple de protection

Enfin, dans la description de votre site d’application, vous devrez inclure ces extraits. Le fichier authelia.conf va dans le bloc serveur et le fichier auth.conf va dans n’importe quel endroit que vous voulez protĂ©ger.
Vous Ă©viterez typiquement de protĂ©ger les actifs statiques et les redirections, puisqu’il n’y a pas d’intĂ©rĂȘt Ă  vĂ©rifier l’authentification pour ceux-ci.

Application site

Authentification Totp Yubikeys Passkeys

Totp

On va ajouter une authentification TOTP
Se connecter sur authelia et cliquer sur authelia

Mot de passe usage unique

Sur la boßte de réception

Saisir le code ID

Suivre la procédure

Si vous n”avez pas google authenticator
Prendre une photo avec l’appareil android pour dĂ©coder

Cliquer sur le lien pour copier le code

Copier le contenu de secret dans le totp de cockpit keepassxc

Cliquer sur suivant et décocher qrcode

Vérification fonctionnement avec un code généré par cockpi keepassxc

Si tout est OK

et un message de confirmation

Yubikeys WebAuthn

Configuration authelia prérequise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## WebAuthn Actif
##
webauthn:
  ## Désactiver Webauthn avec true.
  disable: false

# l'application web utilisant la double authentification
access_control:
  default_policy: deny
  rules:
    - domain:
        - "site.rnmkcy.eu"
      policy: two_factor

Clé yubikey insérée dans un port USB

Se connecter sur le lien https://auth.rnmkcy.eu avec Firefox

Utilisateur yann (exemple)

Saisir utilisateur “yann” et son mot de passe


Choisir la méthode


Un message est envoyé

Sur la boßte de réception

Saisir le code ID

Une nouvelle fenĂȘtre s’ouvre

La clé est enregistrée

et un message de confirmation

Se connecter de nouveau Ă  cockpit

Page de connexion auhelia

Validation par clé

Page authentification cockpit

Ajouter une autre clé yubikey
La seconde clé est enregistrée

et un message de confirmation

Utilisateur yano (exemple)

L’application KeepassXC et son extension firefox sont actives

Ouvrir le portail authentification https://auth.rnmkcy.eu et saisir yano et son mot de passe

Ajouter la mĂ©thode d’authentification “WebAuthn”

Renseigner libellé et suivant

Un popup Keepassxc apparaĂźt

Cliquer sur Enregistrer comme nouveau


Un identifiant webauthn a été ajouté

Sur le navigateur, il faut mémoriser et autoriser Keepassxc

Ce popup n’apparaüt qu’a la premiùre connexion yano sur le portail

Ouvrir le portail authentification https://auth.rnmkcy.eu

Passkeys KeepassXC

Si l’application KeepassXC et son extension firefox sont actifs il est possible de crĂ©er une passkey

Authelia

KeepassXC

Authelia fournisseur OIDC

La configuration Authelia est dĂ©finie dans un fichier de configuration.yml. Ce fichier permet de dĂ©finir les paramĂštres de configuration OIDC nĂ©cessaires, tels qu’un fournisseur et un client.

Création certificat fournisseur

En consultant la documentation publique OIDC d’Authelia , la configuration contient plusieurs paramĂštres nĂ©cessitant soit une valeur secrĂšte, soit un certificat.

Ces Ă©lĂ©ments doivent ĂȘtre gĂ©nĂ©rĂ©s de maniĂšre sĂ©curisĂ©e avant d’ajuster la configuration.

Passer en mode su, puis se rendre dans le répertoire authelia

1
2
sudo -s # ou su -
cd /etc/authelia

Authelia identity_providers: oidc utilise un certificat (clé privée) pour la directive key et une chaßne de certificats (clé publique) pour la directive certificate_chain.

Vous pouvez générer les deux via la commande suivante :

1
authelia crypto certificate rsa generate --common-name auth.rnmkcy.eu && cat public.crt && cat private.pem

⚠ Veuillez ajuster le nom commun du certificat Ă  votre FQDN d’Authelia

Création fournisseur OIDC

Une fois le certificat RSA et le secret client créés de maniĂšre sĂ©curisĂ©e, nous pouvons commencer Ă  ajuster le fichier de configuration d’Authelia. Nous ajouterons un fournisseur d’identitĂ© en insĂ©rant nos secrets et informations de certificat auto-gĂ©nĂ©rĂ©s.

Mettez ce qui suit Ă  la fin de votre configuration Authelia :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
identity_providers:
  oidc:
    hmac_secret: 'my-secure-hmac-secret-key-to-change' #  Commande: openssl rand -base64 48
    jwks:
      - key_id: 'authelia'
        algorithm: 'RS256'
        use: 'sig'
        certificate_chain: |
          -----BEGIN CERTIFICATE-----
          <PASTE-HERE-YOUR-PUBLIC-KEY-DATA> # /etc/authelia/public.crt sur une ligne
          -----END CERTIFICATE-----
         key: |
          -----BEGIN PRIVATE KEY-----
          <PASTE-HERE-YOUR-PRIVATE-KEY-DATA> # /etc/authelia/private.pem sur une ligne
          -----END PRIVATE KEY-----
    enable_client_debug_messages: false
    minimum_parameter_entropy: 8
    enforce_pkce: 'public_clients_only'
    enable_pkce_plain_challenge: false
    enable_jwt_access_token_stateless_introspection: false
    discovery_signed_response_alg: 'none'
    discovery_signed_response_key_id: ''
    require_pushed_authorization_requests: false
    lifespans:
      access_token: '1h'
      authorize_code: '1m'
      id_token: '1h'
      refresh_token: '90m'
    cors:
      endpoints:
        - 'authorization'
        - 'token'
        - 'revocation'
        - 'introspection'
      allowed_origins:
        - 'https://immich.rnmkcy.eu'
      allowed_origins_from_client_redirect_uris: false

Vérifier la configuration en mode su

1
sudo authelia validate-config --config /etc/authelia/configuration.yml

Vous devez obtenir le message suivant, si tout est correct
Configuration parsed and loaded successfully without errors.

Authentification(autres modes)

A 2 facteurs (INACTIVE)

  1. Se connecter avec son compte forgejo
  2. Accédez aux ParamÚtres utilisateur en cliquant sur Configuration

Sécurité passkey (INACTIVE)

Une clĂ© d’accĂšs (en anglais, passkey) est une preuve numĂ©rique utilisĂ©e comme mĂ©thode d’authentification pour un site web ou une application. La clĂ© d’accĂšs est un type d’authentification sans mot de passe, promu par le World Wide Web Consortium et l’Alliance FIDO.(Citation WikipĂ©dia)

Contrairement Ă  un mot de passe, les passkeys ne peuvent pas ĂȘtre partagĂ©s, mĂ©morisĂ©s ou Ă©crits. Ils sont donc beaucoup moins vulnĂ©rables aux types d’attaques qui visent gĂ©nĂ©ralement les systĂšmes basĂ©s sur des mots de passe.

Si vous utilisez KeepassXC avec l’extension navigateur KeePassXC-Browser vous pouvez ajouter la sĂ©curitĂ© par clĂ© “passkey”

Ajouter une clé de sécurité

ParamÚtres utilisateur -> Sécurité

  1. Pseudonyme: forgepass
  2. Cliquer sur “Ajouter une clĂ© de sĂ©curitĂ©â€
  3. Une fenĂȘtre KeepassXC s’ouvre KeepassXC: Cliquer sur Enregistrer ,clĂ© d”accĂšs est créée
  4. La clé forgepass est générée sur Forgejo

Connexion depuis Firefox (extension keepassxc-browser)

Ouvrir le lien https://forgejo.rnmkcy.eu

Notre clé de sécurité est une PassKey KeepassXC

Sur la fenĂȘtre popup KeepassXC, cliquer sur Authentifier

A la premiĂšre connexion, il faut cocher “MĂ©moriser” et “Autoriser les Ă©lĂ©ments choisis”

Authentification LLDAP (INACTIVE)

Un serveur LLDAP est disponible localement: http://127.0.0.1:3890

Sélectionner LDAP (via BindDN)

  • HĂŽte: Le ip/hostname de votre serveur LLDAP, 127.0.0.1
  • Port: Port de votre serveur lldap 3890
  • Bind DN: uid=admin,ou=people,dc=rnmkcy,dc=eu
  • Bind Mot de passe admin lldap
  • Base de recherche utilisateur: ou=people,dc=rnmkcy,dc=eu
  • Filtre utilisateur: Si vous voulez que tous les utilisateurs puissent se connecter, utilisez (&(objectClass=person)(|(uid=%[1]s)(mail=%[1]s))) L’utilisateur ‘yann’ est dans le groupe ‘git’ (&(|(objectclass=inetOrgPerson))(|(memberof=cn=git,ou=groups,dc=rnmkcy,dc=eu)))

Pour se connecter, ils peuvent utiliser leur adresse e-mail ou leur nom d’utilisateur.

Si vous voulez seulement des membres un groupe spĂ©cifique pour ĂȘtre en mesure de se connecter, dans ce cas le groupe git_user, utiliser

(&(memberof=cn=git_user,ou=groups,dc=rnmkcy,dc=eu)(|(uid=%[1]s)(mail=%[1]s)))

Pour plus d’informations sur le filtre, voir: https://docs.gitea.io/en-us/authentication/#ldap-via-binddn

  • Filtre Admin (facultatif): Utiliser (memberof=cn=lldap_admin,ou=groups,dc=rnmkcy,dc=eu) si vous voulez que les administrateurs lldap deviennent des administrateurs Gitea. Laissez vide sinon.

  • Attribut nom d’utilisateur: uid
  • Attribut PrĂ©nom: givenName
  • Attribut Nom de famille : sn
  • Email Attribute: mail
  • Avatar Attribute: jpegPhoto

Au final

Authelia analyses

OK — avec ces tables, le moyen le plus fiable pour rĂ©pondre “qui est authentifiĂ© et avec quelle mĂ©thode” est de s’appuyer sur :

  • authentication_logs (pour ce qui s’est rĂ©ellement passĂ© pendant le login et la mĂ©thode/challenge)
  • et, si besoin, identity_verification / one_time_code / totp_history / webauthn_credentials (pour recouper “2FA effectuĂ©e” vs “challenge Ă©mis”).

Chercher la méthode directement dans authentication_logs

Fais d’abord ces inspections (sans donnĂ©es sensibles) :

1
SHOW COLUMNS FROM authentication_logs;
1
2
3
4
5
6
7
8
9
10
11
12
13
+----------------+--------------+------+-----+---------------------+----------------+
| Field          | Type         | Null | Key | Default             | Extra          |
+----------------+--------------+------+-----+---------------------+----------------+
| id             | int(11)      | NO   | PRI | NULL                | auto_increment |
| time           | timestamp    | NO   | MUL | current_timestamp() |                |
| successful     | tinyint(1)   | NO   |     | NULL                |                |
| banned         | tinyint(1)   | NO   |     | 0                   |                |
| username       | varchar(100) | NO   |     | NULL                |                |
| auth_type      | varchar(8)   | NO   |     | 1FA                 |                |
| remote_ip      | varchar(39)  | YES  |     | NULL                |                |
| request_uri    | text         | YES  |     | NULL                |                |
| request_method | varchar(8)   | NO   |     |                     |                |
+----------------+--------------+------+-----+---------------------+----------------+

Puis lance une requĂȘte “quick win” :

1
2
3
4
5
SELECT
  *
FROM authentication_logs
ORDER BY id DESC
LIMIT 50;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
+-----+---------------------+------------+--------+------------+-----------+---------------------------------------+-------------------------------+----------------+
| id  | time                | successful | banned | username   | auth_type | remote_ip                             | request_uri                   | request_method |
+-----+---------------------+------------+--------+------------+-----------+---------------------------------------+-------------------------------+----------------+
| 121 | 2026-07-24 09:04:49 |          1 |      0 | yano       | WebAuthn  | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 120 | 2026-07-24 09:04:11 |          1 |      0 | yano       | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 119 | 2026-07-24 09:03:41 |          1 |      0 | yano       | WebAuthn  | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 118 | 2026-07-24 09:03:27 |          1 |      0 | yano       | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 117 | 2026-07-24 08:56:59 |          1 |      0 | yano       | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 116 | 2026-07-24 08:38:08 |          1 |      0 | yano       | WebAuthn  | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 115 | 2026-07-24 08:32:39 |          1 |      0 | yano       | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 114 | 2026-07-24 08:31:58 |          1 |      0 | yano       | WebAuthn  | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 113 | 2026-07-24 08:06:53 |          1 |      0 | yano       | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 112 | 2026-07-24 07:42:55 |          1 |      0 | yann       | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 111 | 2026-07-24 07:39:06 |          1 |      0 | yano       | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 110 | 2026-07-23 16:41:31 |          1 |      0 | yann       | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 109 | 2026-07-23 16:41:28 |          1 |      0 | yann       | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 108 | 2026-07-22 18:24:59 |          1 |      0 | technitium | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 107 | 2026-07-22 18:24:56 |          1 |      0 | technitium | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 106 | 2026-07-22 18:09:14 |          1 |      0 | technitium | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 105 | 2026-07-22 18:09:04 |          1 |      0 | technitium | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 104 | 2026-07-22 18:00:58 |          1 |      0 | yann       | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 103 | 2026-07-22 18:00:43 |          1 |      0 | yann       | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 102 | 2026-07-22 17:56:51 |          1 |      0 | technitium | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 101 | 2026-07-22 17:55:35 |          1 |      0 | technitium | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
| 100 | 2026-07-22 17:37:37 |          1 |      0 | technitium | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  99 | 2026-07-22 17:14:55 |          1 |      0 | technitium | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  98 | 2026-07-22 17:10:11 |          1 |      0 | technitium | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  97 | 2026-07-22 17:06:23 |          1 |      0 | yann       | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  96 | 2026-07-22 17:06:12 |          1 |      0 | yann       | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  95 | 2026-07-22 08:01:34 |          1 |      0 | portainer  | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  94 | 2026-07-22 08:01:31 |          1 |      0 | portainer  | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  93 | 2026-07-21 17:05:59 |          1 |      0 | portainer  | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  92 | 2026-07-21 17:05:55 |          1 |      0 | portainer  | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  91 | 2026-07-20 06:33:59 |          1 |      0 | yann       | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb | https://site.rnmkcy.eu/notes/ |                |
|  90 | 2026-07-20 06:33:57 |          1 |      0 | yann       | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb | https://site.rnmkcy.eu/notes/ |                |
|  89 | 2026-07-19 20:13:48 |          1 |      0 | portainer  | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  88 | 2026-07-19 20:13:45 |          1 |      0 | portainer  | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  87 | 2026-07-18 08:28:17 |          1 |      0 | portainer  | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  86 | 2026-07-18 08:28:14 |          1 |      0 | portainer  | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  85 | 2026-07-18 07:34:19 |          1 |      0 | portainer  | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  84 | 2026-07-18 07:34:16 |          1 |      0 | portainer  | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  83 | 2026-07-16 09:57:54 |          1 |      0 | yann       | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb | https://site.rnmkcy.eu/notes/ |                |
|  82 | 2026-07-16 09:57:51 |          1 |      0 | yann       | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb | https://site.rnmkcy.eu/notes/ |                |
|  81 | 2026-07-11 07:16:35 |          1 |      0 | portainer  | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  80 | 2026-07-11 07:16:32 |          1 |      0 | portainer  | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  79 | 2026-07-07 18:38:58 |          1 |      0 | portainer  | TOTP      | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  78 | 2026-07-07 18:38:56 |          1 |      0 | portainer  | 1FA       | 2a01:e0a:95a:e2f0:c16f:1f56:c4fd:7dcb |                               |                |
|  77 | 2026-07-06 08:44:57 |          1 |      0 | yann       | TOTP      | 2a01:e0a:95a:e2f0:d357:db0:cdb0:70ab  | https://site.rnmkcy.eu/notes/ |                |
|  76 | 2026-07-06 08:44:54 |          1 |      0 | yann       | 1FA       | 2a01:e0a:95a:e2f0:d357:db0:cdb0:70ab  | https://site.rnmkcy.eu/notes/ |                |
|  75 | 2026-07-06 07:13:14 |          1 |      0 | portainer  | TOTP      | 2a01:e0a:95a:e2f0:d357:db0:cdb0:70ab  |                               |                |
|  74 | 2026-07-06 07:13:12 |          1 |      0 | portainer  | 1FA       | 2a01:e0a:95a:e2f0:d357:db0:cdb0:70ab  |                               |                |
|  73 | 2026-07-03 20:01:33 |          1 |      0 | yann       | TOTP      | 2a01:e0a:95a:e2f0:d357:db0:cdb0:70ab  | https://site.rnmkcy.eu/       |                |
|  72 | 2026-07-03 20:01:30 |          1 |      0 | yann       | 1FA       | 2a01:e0a:95a:e2f0:d357:db0:cdb0:70ab  | https://site.rnmkcy.eu/       |                |
+-----+---------------------+------------+--------+------------+-----------+---------------------------------------+-------------------------------+----------------+

En regardant les colonnes retournées, cherche des champs du style (les noms exacts varient) :

  • Les champs concernĂ©s: username et auth_type
1
2
3
4
5
6
7
8
SELECT
  username,
  auth_type,
  COUNT(*) AS nb
FROM authentication_logs
WHERE auth_type IN ('TOTP','1FA','WebAuthn')
GROUP BY username, auth_type
ORDER BY nb DESC;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+---------------------------+-----------+----+
| username                  | auth_type | nb |
+---------------------------+-----------+----+
| yann                      | 1FA       | 23 |
| portainer                 | 1FA       | 22 |
| yann                      | TOTP      | 21 |
| portainer                 | TOTP      | 20 |
| yano                      | 1FA       |  6 |
| technitium                | 1FA       |  5 |
| admin@yannig.net          | 1FA       |  4 |
| yano                      | WebAuthn  |  4 |
| forgeadmin                | 1FA       |  4 |
| technitium                | TOTP      |  4 |
|                           | 1FA       |  2 |
| forgeadmin                | TOTP      |  2 |
| immich                    | 1FA       |  1 |
| admin                     | 1FA       |  1 |
| stalwart-admin            | 1FA       |  1 |
| stalwart-admin@yannig.net | 1FA       |  1 |
+---------------------------+-----------+----+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
MariaDB [authelia]> SELECT   * FROM identity_verification;
Empty set (0,001 sec)

MariaDB [authelia]> SHOW COLUMNS FROM oauth2_openid_connect_session;
+--------------------+--------------+------+-----+---------------------+----------------+
| Field              | Type         | Null | Key | Default             | Extra          |
+--------------------+--------------+------+-----+---------------------+----------------+
| id                 | int(11)      | NO   | PRI | NULL                | auto_increment |
| challenge_id       | char(36)     | NO   | MUL | NULL                |                |
| request_id         | varchar(40)  | NO   | MUL | NULL                |                |
| client_id          | varchar(255) | NO   | MUL | NULL                |                |
| signature          | varchar(255) | NO   |     | NULL                |                |
| subject            | char(36)     | NO   | MUL | NULL                |                |
| requested_at       | timestamp    | NO   |     | current_timestamp() |                |
| requested_scopes   | text         | NO   |     | NULL                |                |
| granted_scopes     | text         | NO   |     | NULL                |                |
| requested_audience | text         | YES  |     | NULL                |                |
| granted_audience   | text         | YES  |     | NULL                |                |
| active             | tinyint(1)   | NO   |     | 0                   |                |
| revoked            | tinyint(1)   | NO   |     | 0                   |                |
| form_data          | text         | NO   |     | NULL                |                |
| session_data       | blob         | NO   |     | NULL                |                |
+--------------------+--------------+------+-----+---------------------+----------------+
15 rows in set (0,002 sec)

MariaDB [authelia]> SHOW COLUMNS FROM totp_configurations;
+--------------+--------------+------+-----+---------------------+----------------+
| Field        | Type         | Null | Key | Default             | Extra          |
+--------------+--------------+------+-----+---------------------+----------------+
| id           | int(11)      | NO   | PRI | NULL                | auto_increment |
| created_at   | timestamp    | NO   |     | current_timestamp() |                |
| last_used_at | timestamp    | YES  |     | NULL                |                |
| username     | varchar(100) | NO   | UNI | NULL                |                |
| issuer       | varchar(100) | YES  |     | NULL                |                |
| algorithm    | varchar(6)   | NO   |     | SHA1                |                |
| digits       | int(11)      | NO   |     | 6                   |                |
| period       | int(11)      | NO   |     | 30                  |                |
| secret       | blob         | NO   |     | NULL                |                |
+--------------+--------------+------+-----+---------------------+----------------+
9 rows in set (0,002 sec)

MariaDB [authelia]> SHOW COLUMNS FROM webauthn_users;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| rpid     | varchar(512) | NO   | MUL | NULL    |                |
| username | varchar(100) | NO   |     | NULL    |                |
| userid   | char(64)     | NO   |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
4 rows in set (0,001 sec)

MariaDB [authelia]> SHOW COLUMNS FROM webauthn_credentials;
+--------------------+--------------+------+-----+---------------------+----------------+
| Field              | Type         | Null | Key | Default             | Extra          |
+--------------------+--------------+------+-----+---------------------+----------------+
| id                 | int(11)      | NO   | PRI | NULL                | auto_increment |
| created_at         | timestamp    | NO   |     | current_timestamp() |                |
| last_used_at       | timestamp    | YES  |     | NULL                |                |
| rpid               | varchar(512) | NO   | MUL | NULL                |                |
| username           | varchar(100) | NO   |     | NULL                |                |
| description        | varchar(30)  | NO   |     | NULL                |                |
| kid                | varchar(512) | NO   | UNI | NULL                |                |
| aaguid             | char(36)     | YES  |     | NULL                |                |
| attestation_format | varchar(32)  | YES  |     | NULL                |                |
| attachment         | varchar(64)  | NO   |     | NULL                |                |
| transport          | varchar(64)  | YES  |     |                     |                |
| sign_count         | int(11)      | YES  |     | 0                   |                |
| clone_warning      | tinyint(1)   | NO   |     | 0                   |                |
| legacy             | tinyint(1)   | NO   |     | 0                   |                |
| discoverable       | tinyint(1)   | NO   |     | NULL                |                |
| present            | tinyint(1)   | NO   |     | 0                   |                |
| verified           | tinyint(1)   | NO   |     | 0                   |                |
| backup_eligible    | tinyint(1)   | NO   |     | 0                   |                |
| backup_state       | tinyint(1)   | NO   |     | 0                   |                |
| public_key         | blob         | NO   |     | NULL                |                |
| attestation        | blob         | YES  |     | NULL                |                |
| attestation_type   | varchar(32)  | NO   |     |                     |                |
+--------------------+--------------+------+-----+---------------------+----------------+
22 rows in set (0,001 sec)

MariaDB [authelia]> SHOW COLUMNS FROM user_opaque_identifier;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| service    | varchar(20)  | NO   | MUL | NULL    |                |
| sector_id  | varchar(255) | NO   |     | NULL    |                |
| username   | varchar(100) | NO   |     | NULL    |                |
| identifier | char(36)     | NO   | UNI | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
5 rows in set (0,001 sec)

MariaDB [authelia]> SHOW COLUMNS FROM user_preferences;
+----------------------+--------------+------+-----+---------+----------------+
| Field                | Type         | Null | Key | Default | Extra          |
+----------------------+--------------+------+-----+---------+----------------+
| id                   | int(11)      | NO   | PRI | NULL    | auto_increment |
| username             | varchar(100) | NO   | UNI | NULL    |                |
| second_factor_method | varchar(11)  | NO   |     | NULL    |                |
+----------------------+--------------+------+-----+---------+----------------+

Utilisateurs + auth “par dĂ©faut” (1FA/TOTP/WEBAUTHN)

Si ton objectif est “tous les utilisateurs rĂ©fĂ©rencĂ©s” + leur auth “par dĂ©faut”, pars de user_preferences et fais un LEFT JOIN vers user_opaque_identifier :

1
2
3
4
5
6
7
8
9
10
11
12
SELECT
  up.username,
  CASE
    WHEN LOWER(up.second_factor_method) = 'totp' THEN 'TOTP'
    WHEN LOWER(up.second_factor_method) = 'webauthn' THEN 'WEBAUTHN'
    ELSE '1FA'
  END AS default_auth_type,
  uoi.identifier AS user_identifier_opaque_identifier
FROM user_preferences up
LEFT JOIN user_opaque_identifier uoi
  ON uoi.username = up.username
ORDER BY up.username;
1
2
3
4
5
6
7
8
9
10
11
12
13
+---------------------------+-------------------+--------------------------------------+
| username                  | default_auth_type | user_identifier_opaque_identifier    |
+---------------------------+-------------------+--------------------------------------+
| admin@yannig.net          | TOTP              | NULL                                 |
| forgeadmin                | TOTP              | cdf46cc8-df28-490c-9846-98a040a2f0bd |
| immich                    | TOTP              | NULL                                 |
| portainer                 | TOTP              | a8f08772-1502-425d-8998-c71d45068713 |
| stalwart-admin            | TOTP              | NULL                                 |
| stalwart-admin@yannig.net | TOTP              | NULL                                 |
| technitium                | TOTP              | c45a31c9-255d-4e36-b639-edb6ba75b0aa |
| yann                      | TOTP              | 846ee529-5388-425c-853a-10bcc0db22c5 |
| yano                      | WEBAUTHN          | NULL                                 |
+---------------------------+-------------------+--------------------------------------+

Exemple pour yano

1
2
3
4
5
6
7
8
9
10
11
12
SELECT
  up.username,
  CASE
    WHEN LOWER(up.second_factor_method) = 'totp' THEN 'TOTP'
    WHEN LOWER(up.second_factor_method) = 'webauthn' THEN 'WEBAUTHN'
    ELSE '1FA'
  END AS default_auth_type,
  uoi.identifier AS user_identifier_opaque_identifier
FROM user_preferences up
LEFT JOIN user_opaque_identifier uoi
  ON uoi.username = up.username
WHERE up.username = 'yano';
1
2
3
4
5
+----------+-------------------+-----------------------------------+
| username | default_auth_type | user_identifier_opaque_identifier |
+----------+-------------------+-----------------------------------+
| yano     | WEBAUTHN          | NULL                              |
+----------+-------------------+-----------------------------------+

Le champ user_identifier_opaque_identifier est utilisé uniquement pour les connexions OIDC openid

1
SELECT * FROM user_opaque_identifier;
1
2
3
4
5
6
7
8
+----+---------+-----------+------------+--------------------------------------+
| id | service | sector_id | username   | identifier                           |
+----+---------+-----------+------------+--------------------------------------+
|  1 | openid  |           | portainer  | a8f08772-1502-425d-8998-c71d45068713 |
|  2 | openid  |           | forgeadmin | cdf46cc8-df28-490c-9846-98a040a2f0bd |
|  3 | openid  |           | technitium | c45a31c9-255d-4e36-b639-edb6ba75b0aa |
|  4 | openid  |           | yann       | 846ee529-5388-425c-853a-10bcc0db22c5 |
+----+---------+-----------+------------+--------------------------------------+

La consĂ©quence pour ta question (“utilisateurs rĂ©fĂ©rencĂ©s + 1FA/TOTP/WEBAUTHN”) :

  • user_preferences te donne bien la mĂ©thode par dĂ©faut (2FA) pour yano.
  • mais user_opaque_identifier ne te sert pas pour lister “tous les utilisateurs LDAP”, uniquement ceux qui ont un contexte OIDC openid pour lequel Authelia a gĂ©nĂ©rĂ© un identifier.

Lister tous les utilisateurs “rĂ©fĂ©rencĂ©s” cĂŽtĂ© prefs (par dĂ©faut)

1
2
3
4
5
6
7
8
9
SELECT
  up.username,
  CASE
    WHEN LOWER(up.second_factor_method) = 'totp' THEN 'TOTP'
    WHEN LOWER(up.second_factor_method) = 'webauthn' THEN 'WEBAUTHN'
    ELSE '1FA'
  END AS default_auth_type
FROM user_preferences up
ORDER BY up.username;
1
2
3
4
5
6
7
8
9
10
11
12
13
+---------------------------+-------------------+
| username                  | default_auth_type |
+---------------------------+-------------------+
| admin@yannig.net          | TOTP              |
| forgeadmin                | TOTP              |
| immich                    | TOTP              |
| portainer                 | TOTP              |
| stalwart-admin            | TOTP              |
| stalwart-admin@yannig.net | TOTP              |
| technitium                | TOTP              |
| yann                      | TOTP              |
| yano                      | WEBAUTHN          |
+---------------------------+-------------------+

Oui. Pour avoir un “schĂ©ma avec les liens entre tables” (FK / dĂ©pendances), il faut essentiellement : 1) exporter la structure (tables + colonnes + indexes) 2) extraire les relations (FOREIGN KEY) et, si Authelia n’en met pas beaucoup, dĂ©duire des “liens” via colonnes communes (ex: username, id, *_id, challenge_id, etc.)

Commencer par voir s’il y a des clĂ©s Ă©trangĂšres (FOREIGN KEY)

1
2
3
4
5
6
7
8
9
10
SELECT
  TABLE_NAME,
  CONSTRAINT_NAME,
  COLUMN_NAME,
  REFERENCED_TABLE_NAME,
  REFERENCED_COLUMN_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA='authelia'
  AND REFERENCED_TABLE_NAME IS NOT NULL
ORDER BY TABLE_NAME, CONSTRAINT_NAME, COLUMN_NAME;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+-----------------------------------+-----------------------------------------------------+------------------+---------------------------------+------------------------+
| TABLE_NAME                        | CONSTRAINT_NAME                                     | COLUMN_NAME      | REFERENCED_TABLE_NAME           | REFERENCED_COLUMN_NAME |
+-----------------------------------+-----------------------------------------------------+------------------+---------------------------------+------------------------+
| oauth2_access_token_session       | oauth2_access_token_session_challenge_id_fkey       | challenge_id     | oauth2_consent_session          | challenge_id           |
| oauth2_access_token_session       | oauth2_access_token_session_subject_fkey            | subject          | user_opaque_identifier          | identifier             |
| oauth2_authorization_code_session | oauth2_authorization_code_session_challenge_id_fkey | challenge_id     | oauth2_consent_session          | challenge_id           |
| oauth2_authorization_code_session | oauth2_authorization_code_session_subject_fkey      | subject          | user_opaque_identifier          | identifier             |
| oauth2_consent_preconfiguration   | oauth2_consent_preconfiguration_subject_fkey        | subject          | user_opaque_identifier          | identifier             |
| oauth2_consent_session            | oauth2_consent_session_preconfiguration_fkey        | preconfiguration | oauth2_consent_preconfiguration | id                     |
| oauth2_consent_session            | oauth2_consent_session_subject_fkey                 | subject          | user_opaque_identifier          | identifier             |
| oauth2_device_code_session        | oauth2_device_code_session_challenge_id_fkey        | challenge_id     | oauth2_consent_session          | challenge_id           |
| oauth2_device_code_session        | oauth2_device_code_session_subject_fkey             | subject          | user_opaque_identifier          | identifier             |
| oauth2_openid_connect_session     | oauth2_openid_connect_session_challenge_id_fkey     | challenge_id     | oauth2_consent_session          | challenge_id           |
| oauth2_openid_connect_session     | oauth2_openid_connect_session_subject_fkey          | subject          | user_opaque_identifier          | identifier             |
| oauth2_pkce_request_session       | oauth2_pkce_request_session_challenge_id_fkey       | challenge_id     | oauth2_consent_session          | challenge_id           |
| oauth2_pkce_request_session       | oauth2_pkce_request_session_subject_fkey            | subject          | user_opaque_identifier          | identifier             |
| oauth2_refresh_token_session      | oauth2_refresh_token_session_challenge_id_fkey      | challenge_id     | oauth2_consent_session          | challenge_id           |
| oauth2_refresh_token_session      | oauth2_refresh_token_session_subject_fkey           | subject          | user_opaque_identifier          | identifier             |
+-----------------------------------+-----------------------------------------------------+------------------+---------------------------------+------------------------+
erDiagram
  user_opaque_identifier {
    int id PK
    char identifier UK
  }

  oauth2_consent_preconfiguration {
    int id PK
    char subject FK
  }

  oauth2_consent_session {
    int id PK
    char challenge_id UK
    char subject FK
    int preconfiguration FK
  }

  oauth2_access_token_session {
    int id PK
    char challenge_id FK
    char subject FK
  }

  oauth2_authorization_code_session {
    int id PK
    char challenge_id FK
    char subject FK
  }

  oauth2_device_code_session {
    int id PK
    char challenge_id FK
    char subject FK
  }

  oauth2_openid_connect_session {
    int id PK
    char challenge_id FK
    char subject FK
  }

  oauth2_pkce_request_session {
    int id PK
    char challenge_id FK
    char subject FK
  }

  oauth2_refresh_token_session {
    int id PK
    char challenge_id FK
    char subject FK
  }

  user_opaque_identifier ||--o{ oauth2_access_token_session : "identifier = subject"
  user_opaque_identifier ||--o{ oauth2_authorization_code_session : "identifier = subject"
  user_opaque_identifier ||--o{ oauth2_consent_preconfiguration : "identifier = subject"
  user_opaque_identifier ||--o{ oauth2_consent_session : "identifier = subject"
  user_opaque_identifier ||--o{ oauth2_device_code_session : "identifier = subject"
  user_opaque_identifier ||--o{ oauth2_openid_connect_session : "identifier = subject"
  user_opaque_identifier ||--o{ oauth2_pkce_request_session : "identifier = subject"
  user_opaque_identifier ||--o{ oauth2_refresh_token_session : "identifier = subject"

  oauth2_consent_session ||--o{ oauth2_access_token_session : "challenge_id"
  oauth2_consent_session ||--o{ oauth2_authorization_code_session : "challenge_id"
  oauth2_consent_session ||--o{ oauth2_device_code_session : "challenge_id"
  oauth2_consent_session ||--o{ oauth2_openid_connect_session : "challenge_id"
  oauth2_consent_session ||--o{ oauth2_pkce_request_session : "challenge_id"
  oauth2_consent_session ||--o{ oauth2_refresh_token_session : "challenge_id"

  oauth2_consent_preconfiguration ||--o{ oauth2_consent_session : "id = preconfiguration"

Plus compacte

%%{init: { "theme": "base", "themeVariables": { "fontFamily": "monospace" }}}%%
flowchart LR
  %% Columns: TABLE_NAME | CONSTRAINT_NAME | COLUMN_NAME | REFERENCED_TABLE_NAME | REFERENCED_COLUMN_NAME

  oauth2_access_token_session:::t --> oauth2_consent_session:::t
  oauth2_access_token_session:::t --> oauth2_consent_session:::t
  oauth2_access_token_session:::t --> user_opaque_identifier:::t
  oauth2_access_token_session:::t --> user_opaque_identifier:::t

  oauth2_authorization_code_session:::t --> oauth2_consent_session:::t
  oauth2_authorization_code_session:::t --> oauth2_consent_session:::t
  oauth2_authorization_code_session:::t --> user_opaque_identifier:::t
  oauth2_authorization_code_session:::t --> user_opaque_identifier:::t

  oauth2_consent_preconfiguration:::t --> user_opaque_identifier:::t

  oauth2_consent_session:::t --> oauth2_consent_preconfiguration:::t
  oauth2_consent_session:::t --> user_opaque_identifier:::t

  oauth2_device_code_session:::t --> oauth2_consent_session:::t
  oauth2_device_code_session:::t --> oauth2_consent_session:::t
  oauth2_device_code_session:::t --> user_opaque_identifier:::t
  oauth2_device_code_session:::t --> user_opaque_identifier:::t

  oauth2_openid_connect_session:::t --> oauth2_consent_session:::t
  oauth2_openid_connect_session:::t --> oauth2_consent_session:::t
  oauth2_openid_connect_session:::t --> user_opaque_identifier:::t
  oauth2_openid_connect_session:::t --> user_opaque_identifier:::t

  oauth2_pkce_request_session:::t --> oauth2_consent_session:::t
  oauth2_pkce_request_session:::t --> oauth2_consent_session:::t
  oauth2_pkce_request_session:::t --> user_opaque_identifier:::t
  oauth2_pkce_request_session:::t --> user_opaque_identifier:::t

  oauth2_refresh_token_session:::t --> oauth2_consent_session:::t
  oauth2_refresh_token_session:::t --> oauth2_consent_session:::t
  oauth2_refresh_token_session:::t --> user_opaque_identifier:::t
  oauth2_refresh_token_session:::t --> user_opaque_identifier:::t

  classDef t fill:#f7f7ff,stroke:#666,stroke-width:1px,rx:8,ry:8;

Annexe

ID client

Les utilisateurs peuvent facilement gĂ©nĂ©rer un identifiant/identifiant client en suivant le guide GĂ©nĂ©ration d’une chaĂźne alphanumĂ©rique alĂ©atoire
Par exemple, les utilisateurs peuvent exécuter la commande

1
authelia crypto rand --length 72 --charset rfc3986

pour générer un identifiant/identifiant client de 72 caractÚres qui est imprimé.
Cette commande aléatoire évite également les problÚmes avec une application client/partie de confiance codant correctement les caractÚres car elle utilise les caractÚres non réservés RFC3986 .

Si un jeu de caractĂšres diffĂ©rent est utilisĂ© et que la valeur est diffĂ©rente lors du codage de l’URL, il imprimera Ă©galement cette valeur sĂ©parĂ©ment.

Secret client

Les utilisateurs peuvent facilement gĂ©nĂ©rer un secret client en suivant le guide GĂ©nĂ©ration d’un hachage de mot de passe alĂ©atoire
Par exemple, les utilisateurs peuvent exécuter la commande

1
authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986

⚠ Veuillez noter le mot de passe en clair ainsi que le hachage $pbkdf2 du mot de passe pour une utilisation ultĂ©rieure.

pour Ă  la fois gĂ©nĂ©rer un secret client de 72 caractĂšres qui est imprimĂ© et doit ĂȘtre utilisĂ© avec la partie utilisatrice et le hacher Ă  l’aide de PBKDF2 qui peut ĂȘtre stockĂ© dans la configuration Authelia. Cette commande alĂ©atoire Ă©vite Ă©galement les problĂšmes avec une application client/partie de confiance codant correctement les caractĂšres car elle utilise les caractĂšres non rĂ©servĂ©s RFC3986 .

Si un jeu de caractĂšres diffĂ©rent est utilisĂ© et que la valeur est diffĂ©rente lors du codage de l’URL, il imprimera Ă©galement cette valeur sĂ©parĂ©ment.

Réglage des facteurs de travail

Lors du hachage des secrets du client, Authelia effectue l’opĂ©ration de hachage pour authentifier le client lors de la rĂ©ception des demandes. Cette opĂ©ration de hachage prend du temps, de par sa conception (la partie travail du facteur travail) pour empĂȘcher un attaquant de tenter d’obtenir le secret client. Le temps nĂ©cessaire dĂ©pend de votre matĂ©riel et du facteur de travail.

Si les opĂ©rations de votre client expirent, vous devrez peut-ĂȘtre rĂ©duire le facteur de travail Ă  un niveau appropriĂ© pour votre client et les capacitĂ©s de votre matĂ©riel.

Pour tester la durée de différents facteurs de travail, vous pouvez la mesurer ainsi :

1
time authelia crypto hash generate pbkdf2 --variant sha512 --iterations 310000 --password insecure_password

Générer chaßne aléatoire

Generating a Random Alphanumeric String

Authelia. Le conteneur de docker Authelia ou binaire CLI peut ĂȘtre utilisĂ© pour gĂ©nĂ©rer une chaĂźne alphanumĂ©rique alĂ©atoire.

Utilisez le authelia crypto rand --helpcommander ou voir le guide de rĂ©fĂ©rence authelia crypto rand pour plus d’informations sur toutes les options disponibles.

1
authelia crypto rand --length 64 --charset alphanumeric

OpenSSL. La commande openssl sur Linux peut ĂȘtre utilisĂ©e pour gĂ©nĂ©rer une chaĂźne alphanumĂ©rique alĂ©atoire:

1
openssl rand -hex 64

Linux de base. Les commandes Linux de base peuvent ĂȘtre utilisĂ©es pour gĂ©nĂ©rer une chaĂźne alphanumĂ©rique alĂ©atoire:

1
2
LENGTH=64
tr -cd '[:alnum:]' < /dev/urandom | fold -w "${LENGTH}" | head -n 1 | tr -d '\n' ; echo
Cet article est sous licence CC BY 4.0 par l'auteur.