{
  config,
  lib,
  ...
}: let
  authelia = "authelia-procopius";
in {
  networking.firewall.allowedTCPPorts = [
    9091
  ];

  services.authelia.instances.procopius = {
    enable = true;
    settings = {
      theme = "auto";
      server = {
        buffers = {
          read = 16384;
          write = 16384;
        };
      };
      authentication_backend.ldap = {
        implementation = "lldap";
        address = "ldap://localhost:3890";
        base_dn = "dc=procopius,dc=dk";
        user = "uid=authelia,ou=people,dc=procopius,dc=dk";
      };
      definitions = {
        network = {
          internal = [
            "192.168.1.0/24"
          ];
        };
      };
      access_control = {
        default_policy = "deny";
        # We want this rule to be low priority so it doesn't override the others
        rules = lib.mkAfter [
          {
            domain = [
              "proxmox.procopius.dk"
              "traefik.procopius.dk"
              "prometheus.procopius.dk"
              "alertmanager.procopius.dk"
            ];
            policy = "one_factor";
            subject = [
              ["group:server-admin"]
            ];
          }
          # bypass /api and /ping
          {
            domain = ["*.procopius.dk"];
            policy = "bypass";
            resources = [
              "^/api$"
              "^/api/"
              "^/ping$"
            ];
          }
          # media
          {
            domain = [
              "sonarr.procopius.dk"
              "radarr.procopius.dk"
              "readarr.procopius.dk"
              "lidarr.procopius.dk"
              "bazarr.procopius.dk"
              "prowlarr.procopius.dk"
            ];
            policy = "one_factor";
            subject = [
              ["group:media-admin"]
            ];
          }
          # authenticated
          {
            domain = [
              "gatus.procopius.dk"
            ];
            policy = "one_factor";
          }
          # bypass auth internally
          # {
          #   domain = [
          #     "gatus.procopius.dk"
          #     "prometheus.procopius.dk"
          #     "alertmanager.procopius.dk"
          #     "sonarr.procopius.dk"
          #     "radarr.procopius.dk"
          #     "readarr.procopius.dk"
          #     "lidarr.procopius.dk"
          #     "bazarr.procopius.dk"
          #     "prowlarr.procopius.dk"
          #   ];
          #   policy = "bypass";
          #   networks = [
          #     "internal"
          #   ];
          # }
        ];
      };
      storage.postgres = {
        address = "unix:///run/postgresql";
        database = authelia;
        username = authelia;
        # I'm using peer authentication, so this doesn't actually matter, but Authelia
        # complains if I don't have it.
        # https://github.com/authelia/authelia/discussions/7646
        password = authelia;
      };
      session = {
        redis.host = "/var/run/redis-procopius/redis.sock";
        cookies = [
          {
            domain = "procopius.dk";
            authelia_url = "https://authelia.procopius.dk";
            # The period of time the user can be inactive for before the session is destroyed
            inactivity = "1M";
            # The period of time before the cookie expires and the session is destroyed
            expiration = "3M";
            # The period of time before the cookie expires and the session is destroyed
            # when the remember me box is checked
            remember_me = "1y";
          }
        ];
      };
      notifier.smtp = {
        address = "smtp://mail.procopius.dk";
        username = "authelia@procopius.dk";
        sender = "authelia@procopius.dk";
      };
      log.level = "info";
      # identity_providers.oidc = {
      #   # https://www.authelia.com/integration/openid-connect/openid-connect-1.0-claims/#restore-functionality-prior-to-claims-parameter
      #   claims_policies = {
      #     # karakeep.id_token = ["email"];
      #   };
      #   cors = {
      #     endpoints = ["token"];
      #     allowed_origins_from_client_redirect_uris = true;
      #   };
      #   authorization_policies.default = {
      #     default_policy = "one_factor";
      #     rules = [
      #       {
      #         policy = "deny";
      #         subject = "group:lldap_strict_readonly";
      #       }
      #     ];
      #   };
      # };
      # Necessary for Traefik integration
      # See https://www.authelia.com/integration/proxies/traefik/#implementation
      server.endpoints.authz.forward-auth.implementation = "ForwardAuth";
    };
    # Templates don't work correctly when parsed from Nix, so our OIDC clients are defined here
    # settingsFiles = [./oidc_clients.yaml];
    secrets = with config.sops; {
      jwtSecretFile = secrets."authelia/jwt_secret".path;
      # oidcIssuerPrivateKeyFile = secrets."authelia/jwks".path;
      # oidcHmacSecretFile = secrets."authelia/hmac_secret".path;
      sessionSecretFile = secrets."authelia/session_secret".path;
      storageEncryptionKeyFile = secrets."authelia/storage_encryption_key".path;
    };
    environmentVariables = with config.sops; {
      AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE =
        secrets."authelia/lldap_authelia_password".path;
      AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = secrets.smtp-password_authelia.path;
    };
  };

  # Give Authelia access to the Redis socket
  users.users.${authelia}.extraGroups = ["redis-procopius"];

  systemd.services.${authelia} = let
    dependencies = [
      "lldap.service"
      "postgresql.service"
      "redis-procopius.service"
    ];
  in {
    # Authelia requires LLDAP, PostgreSQL, and Redis to be running
    after = dependencies;
    requires = dependencies;
    # Required for templating
    serviceConfig.Environment = "X_AUTHELIA_CONFIG_FILTERS=template";
  };

  sops.secrets = {
    "authelia/hmac_secret".owner = authelia;
    "authelia/jwks".owner = authelia;
    "authelia/jwt_secret".owner = authelia;
    "authelia/session_secret".owner = authelia;
    "authelia/storage_encryption_key".owner = authelia;
    # The password for the `authelia` LLDAP user
    "authelia/lldap_authelia_password".owner = authelia;
    smtp-password_authelia = {
      owner = authelia;
      key = "service_accounts/authelia/password";
    };
  };
}
