diff --git a/CHANGELOG.md b/CHANGELOG.md
index aefbeb94..fa268a0b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@
- `stacked` is now ignored on chart types that cannot stack, instead of displaying an empty chart.
- Screen readers now announce the title of the modal component instead of an unnamed dialog.
- `sqlpage.request_body` and `sqlpage.request_body_base64` now return NULL when the request has no body. A body that cannot be read, such as one exceeding the payload limit, is now reported as an error instead of being silently replaced with an empty body.
+ - List-valued configuration options, including OIDC paths and trusted audiences, can now be set through environment variables as space-separated lists.
- Datagrid rows with an icon or image no longer display an unnecessary en-dash placeholder, and an explicitly empty description remains empty.
- Charts can display reference lines. A row with a `yline` is drawn as a line across the chart at that value of the y axis, with the row's `label` and `color` for its text and its color. Reference lines are rows, so a chart can have as many of them as the query returns. A line follows its axis, so on a `horizontal` bar chart a `yline` is drawn down the chart rather than across it. They are not added to the total of a `stacked` chart, and are not filled in an `area` chart.
diff --git a/configuration.md b/configuration.md
index f685423a..9e1af1a3 100644
--- a/configuration.md
+++ b/configuration.md
@@ -19,19 +19,19 @@ Here are the available configuration options and their default values:
| `database_connection_max_lifetime_seconds` | SQLite: None All other: 60 minutes | Always close database connections after this amount of time. Set to 0 to disable. |
| `database_connection_retries` | 6 | Database connection attempts before giving up. Retries will happen every 5 seconds. |
| `database_connection_acquire_timeout_seconds` | 10 | How long to wait when acquiring a database connection from the pool before giving up and returning an error. |
-| `sqlite_extensions` | | An array of SQLite extensions to load, such as `mod_spatialite` |
+| `sqlite_extensions` | | An array of SQLite extensions to load, such as `mod_spatialite`. When set through an environment variable, use a space-separated list. |
| `web_root` | `.` | The root directory of the web server, where the `index.sql` file is located. Static file serving follows symlinks, so do not place symlinks under `web_root` that point to private paths (such as the `sqlpage/` config directory) or to files outside `web_root`, as their targets would become publicly reachable (see [`SECURITY.md`](./SECURITY.md)). |
| `site_prefix` | `/` | Base path of the site. If you want to host SQLPage at `https://example.com/sqlpage/`, set this to `/sqlpage/`. When using a reverse proxy, this allows hosting SQLPage together with other applications on the same subdomain. |
| `configuration_directory` | `./sqlpage/` | The directory where the `sqlpage.json` file is located. This is used to find the path to [`templates/`](https://sql-page.com/custom_components.sql), [`migrations/`](https://sql-page.com/your-first-sql-website/migrations.sql), and `on_connect.sql`. Obviously, this configuration parameter can be set only through environment variables, not through the `sqlpage.json` file itself in order to find the `sqlpage.json` file. Be careful not to use a path that is accessible from the public WEB_ROOT |
| `allow_exec` | false | Allow usage of the `sqlpage.exec` function. Do this only if all users with write access to sqlpage query files and to the optional `sqlpage_files` table on the database are trusted. |
| `max_uploaded_file_size` | 5242880 | Maximum size of forms and uploaded files in bytes. Defaults to 5 MiB. |
-| `oidc_protected_paths` | `["/"]` | A list of URL prefixes that should be protected by OIDC authentication. By default, all paths are protected (`["/"]`). If you want to make some pages public, you can restrict authentication to a sub-path, for instance `["/admin", "/users/settings"]`. All paths must start with a "/" and will be prepended by `site_prefix` if defined.|
-| `oidc_public_paths` | `[]` | A list of URL prefixes that should be publicly available. By default, no paths are publicly accessible (`[]`). If you want to make some pages public, you can bypass authentication for a sub-path, for instance `["/public/", "/assets/"]`. Keep in mind that without the closing backslashes, that any directory or file starting with `public` or `assets` will be publicly available. This will also overwrite any protected path restriction. If you have a private path `/private` and you define the public path `/private/public/` everything in `/private/public/` will be publicly accessible, while everything else in private will still need authentication. All paths must start with a "/" and will be prepended by `site_prefix` if defined.
+| `oidc_protected_paths` | `["/"]` | A list of URL prefixes that should be protected by OIDC authentication. By default, all paths are protected (`["/"]`). If you want to make some pages public, you can restrict authentication to a sub-path, for instance `["/admin", "/users/settings"]`. All paths must start with a "/" and will be prepended by `site_prefix` if defined. When set through an environment variable, use a space-separated list, such as `/admin /users/settings`.|
+| `oidc_public_paths` | `[]` | A list of URL prefixes that should be publicly available. By default, no paths are publicly accessible (`[]`). If you want to make some pages public, you can bypass authentication for a sub-path, for instance `["/public/", "/assets/"]`. Keep in mind that without the closing backslashes, that any directory or file starting with `public` or `assets` will be publicly available. This will also overwrite any protected path restriction. If you have a private path `/private` and you define the public path `/private/public/` everything in `/private/public/` will be publicly accessible, while everything else in private will still need authentication. All paths must start with a "/" and will be prepended by `site_prefix` if defined. When set through an environment variable, use a space-separated list, such as `/public/ /assets/`.
| `oidc_issuer_url` | | The base URL of the [OpenID Connect provider](#openid-connect-oidc-authentication). Required for enabling Single Sign-On. |
| `oidc_client_id` | sqlpage | The ID that identifies your SQLPage application to the OIDC provider. You get this when registering your app with the provider. |
| `oidc_client_secret` | | The secret key for your SQLPage application. Keep this confidential as it allows your app to authenticate with the OIDC provider. |
| `oidc_scopes` | openid email profile | Space-separated list of [scopes](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) your app requests from the OIDC provider. |
-| `oidc_additional_trusted_audiences` | unset | A list of additional audiences that are allowed in JWT tokens, beyond the client ID. When empty or unset, any additional audience is accepted. For increased security, set to an empty list `[]` to only allow the client ID as audience. |
+| `oidc_additional_trusted_audiences` | unset | A list of additional audiences that are allowed in JWT tokens, beyond the client ID. When empty or unset, any additional audience is accepted. For increased security, set to an empty list `[]` in the configuration file to only allow the client ID as audience. When set through an environment variable, use a space-separated list, such as `audience-one audience-two`. |
| `max_pending_rows` | 256 | Maximum number of rendered rows that can be queued up in memory when a client is slow to receive them. |
| `compress_responses` | false | When the client supports it, compress the http response body. This can save bandwidth and speed up page loading on slow connections, but can also increase CPU usage and cause rendering delays on pages that take time to render (because streaming responses are buffered for longer than necessary). |
| `https_domain` | | Domain name to request a certificate for. Setting this parameter will automatically make SQLPage listen on port 443 and request an SSL certificate. The server will take a little bit longer to start the first time it has to request a certificate. HTTPS is always served on port 443; if `listen_on` or `port` names a different port, SQLPage also serves plain HTTP there. |
diff --git a/src/app_config.rs b/src/app_config.rs
index ccf40f65..6e916645 100644
--- a/src/app_config.rs
+++ b/src/app_config.rs
@@ -501,6 +501,9 @@ fn env_config() -> config::Environment {
.try_parsing(true)
.list_separator(" ")
.with_list_parse_key("sqlite_extensions")
+ .with_list_parse_key("oidc_protected_paths")
+ .with_list_parse_key("oidc_public_paths")
+ .with_list_parse_key("oidc_additional_trusted_audiences")
}
fn deserialize_port<'de, D>(deserializer: D) -> Result