reactor(opaque): normalize to OXICLOUD_AUTH_OPAQUE_* variables
This commit is contained in:
+1
-1
@@ -172,7 +172,7 @@ COPY --from=app --chmod=755 /app/release/oxicloud /usr/local/bin/
|
|||||||
# ship; it only mutates `storage.files` rows whose name ≠ NFC(name).
|
# ship; it only mutates `storage.files` rows whose name ≠ NFC(name).
|
||||||
COPY --from=app --chmod=755 /app/release/migrate-nfc-filenames /usr/local/bin/
|
COPY --from=app --chmod=755 /app/release/migrate-nfc-filenames /usr/local/bin/
|
||||||
# Ship the OPAQUE server-setup generator alongside the server so operators
|
# Ship the OPAQUE server-setup generator alongside the server so operators
|
||||||
# can generate their `OXICLOUD_OPAQUE_SERVER_SETUP` value inside the
|
# can generate their `OXICLOUD_AUTH_OPAQUE_SERVER_SETUP` value inside the
|
||||||
# container without a separate Rust toolchain:
|
# container without a separate Rust toolchain:
|
||||||
# docker run --rm <image> opaque-setup # prints the base64 value
|
# docker run --rm <image> opaque-setup # prints the base64 value
|
||||||
# One-shot, side-effect-free — safe to include; the runtime doesn't
|
# One-shot, side-effect-free — safe to include; the runtime doesn't
|
||||||
|
|||||||
@@ -119,20 +119,20 @@ The verification-piggyback flow above deliberately **bypasses the `has_password`
|
|||||||
|
|
||||||
OPAQUE (RFC 9807) replaces the traditional "browser sends passphrase, server hashes it" flow with a two-round cryptographic exchange in which the passphrase **never leaves the client**. On registration the client encrypts a random key blob under the passphrase and uploads that opaque envelope. On login the client proves possession of the passphrase without transmitting it — the server can neither read it nor derive it from what it stores.
|
OPAQUE (RFC 9807) replaces the traditional "browser sends passphrase, server hashes it" flow with a two-round cryptographic exchange in which the passphrase **never leaves the client**. On registration the client encrypts a random key blob under the passphrase and uploads that opaque envelope. On login the client proves possession of the passphrase without transmitting it — the server can neither read it nor derive it from what it stores.
|
||||||
|
|
||||||
This is the substrate for planned end-to-end encryption work (see `docs/plan/opaque.md` for the full multi-phase roadmap). This build ships **Phase 0 only** — the primitives, migration column, and configuration substrate. Endpoints are inert until `OXICLOUD_OPAQUE_MODE` is enabled in a future release.
|
This is the substrate for planned end-to-end encryption work (see `docs/plan/opaque.md` for the full multi-phase roadmap). This build ships **Phase 0 only** — the primitives, migration column, and configuration substrate. Endpoints are inert until `OXICLOUD_AUTH_OPAQUE_MODE` is enabled in a future release.
|
||||||
|
|
||||||
### When to enable OPAQUE
|
### When to enable OPAQUE
|
||||||
|
|
||||||
OPAQUE only touches the password login path. If your deployment doesn't use password auth at all — you've set `OXICLOUD_AUTH_METHODS=oidc`, or `magic_link`, or the OIDC master-rule has locked things down to SSO only — OPAQUE has nothing to shadow and there's no reason to enable it. **Leave every `OXICLOUD_OPAQUE_*` variable at default** (unset). No `OXICLOUD_OPAQUE_SERVER_SETUP` is required in that case; the server won't ask for one.
|
OPAQUE only touches the password login path. If your deployment doesn't use password auth at all — you've set `OXICLOUD_AUTH_METHODS=oidc`, or `magic_link`, or the OIDC master-rule has locked things down to SSO only — OPAQUE has nothing to shadow and there's no reason to enable it. **Leave every `OXICLOUD_AUTH_OPAQUE_*` variable at default** (unset). No `OXICLOUD_AUTH_OPAQUE_SERVER_SETUP` is required in that case; the server won't ask for one.
|
||||||
|
|
||||||
Even if you accidentally set `OXICLOUD_OPAQUE_MODE=migrate` in an OIDC-only deployment, the boot-time cross-check downgrades the effective mode to `off` and emits an audit-channel INFO explaining why. This is intentional so operators aren't blocked by a setup requirement for a feature they don't use.
|
Even if you accidentally set `OXICLOUD_AUTH_OPAQUE_MODE=migrate` in an OIDC-only deployment, the boot-time cross-check downgrades the effective mode to `off` and emits an audit-channel INFO explaining why. This is intentional so operators aren't blocked by a setup requirement for a feature they don't use.
|
||||||
|
|
||||||
### Enabling OPAQUE (when the endpoints ship in Phase 1)
|
### Enabling OPAQUE (when the endpoints ship in Phase 1)
|
||||||
|
|
||||||
Password-using deployments will opt in via three env vars:
|
Password-using deployments will opt in via three env vars:
|
||||||
|
|
||||||
1. **`OXICLOUD_OPAQUE_MODE`** — set to `migrate` for the dual-mode phase where both OPAQUE and legacy password login are accepted, then later to `opaque_only` after most users have completed migration.
|
1. **`OXICLOUD_AUTH_OPAQUE_MODE`** — set to `migrate` for the dual-mode phase where both OPAQUE and legacy password login are accepted, then later to `opaque_only` after most users have completed migration.
|
||||||
2. **`OXICLOUD_OPAQUE_SERVER_SETUP`** — generated once and persisted like your JWT secret. Rotating this invalidates every user's registration; treat it as one of the crown jewels. Two ways to generate:
|
2. **`OXICLOUD_AUTH_OPAQUE_SERVER_SETUP`** — generated once and persisted like your JWT secret. Rotating this invalidates every user's registration; treat it as one of the crown jewels. Two ways to generate:
|
||||||
```bash
|
```bash
|
||||||
# Docker (recommended in production — no toolchain needed):
|
# Docker (recommended in production — no toolchain needed):
|
||||||
docker run --rm ghcr.io/atalayalabs/oxicloud:latest opaque-setup
|
docker run --rm ghcr.io/atalayalabs/oxicloud:latest opaque-setup
|
||||||
@@ -141,9 +141,9 @@ Password-using deployments will opt in via three env vars:
|
|||||||
cargo run --bin opaque-setup
|
cargo run --bin opaque-setup
|
||||||
```
|
```
|
||||||
Both print the base64 value on stdout (with guidance on stderr, so shell pipelines like `$(docker run ... opaque-setup)` capture cleanly).
|
Both print the base64 value on stdout (with guidance on stderr, so shell pipelines like `$(docker run ... opaque-setup)` capture cleanly).
|
||||||
3. **`OXICLOUD_OPAQUE_KSF_*`** — client-side Argon2id key-stretching cost. Defaults (256 MiB / 3 iter / 4 lanes) are appropriate for modern desktop / phone hardware. Bumping later is safe (only affects new registrations); lowering is not (still-registered users get a security downgrade the next time they change their passphrase).
|
3. **`OXICLOUD_AUTH_OPAQUE_KSF_*`** — client-side Argon2id key-stretching cost. Defaults (256 MiB / 3 iter / 4 lanes) are appropriate for modern desktop / phone hardware. Bumping later is safe (only affects new registrations); lowering is not (still-registered users get a security downgrade the next time they change their passphrase).
|
||||||
|
|
||||||
The `OXICLOUD_HASH_*` variables (server-side legacy Argon2) and `OXICLOUD_OPAQUE_KSF_*` (client-side OPAQUE Argon2) are intentionally separate: the server-side path is RAM-bounded by concurrent-login traffic and needs to stay modest; the client-side path is single-user per attempt and can afford much higher memory. Tuning them together would force a bad compromise in one direction or the other.
|
The `OXICLOUD_HASH_*` variables (server-side legacy Argon2) and `OXICLOUD_AUTH_OPAQUE_KSF_*` (client-side OPAQUE Argon2) are intentionally separate: the server-side path is RAM-bounded by concurrent-login traffic and needs to stay modest; the client-side path is single-user per attempt and can afford much higher memory. Tuning them together would force a bad compromise in one direction or the other.
|
||||||
|
|
||||||
### What OPAQUE does NOT touch
|
### What OPAQUE does NOT touch
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -41,7 +41,7 @@ Most runtime variables use the `OXICLOUD_` prefix. A few build-time or allocator
|
|||||||
| `OXICLOUD_JWT_SECRET` | (auto-generated) | JWT signing secret; auto-persisted to `<STORAGE_PATH>/.jwt_secret` if unset |
|
| `OXICLOUD_JWT_SECRET` | (auto-generated) | JWT signing secret; auto-persisted to `<STORAGE_PATH>/.jwt_secret` if unset |
|
||||||
| `OXICLOUD_ACCESS_TOKEN_EXPIRY_SECS` | `3600` | Access token lifetime (1 hour) |
|
| `OXICLOUD_ACCESS_TOKEN_EXPIRY_SECS` | `3600` | Access token lifetime (1 hour) |
|
||||||
| `OXICLOUD_REFRESH_TOKEN_EXPIRY_SECS` | `604800` | Refresh token lifetime (7 days); active sessions auto-renew on use |
|
| `OXICLOUD_REFRESH_TOKEN_EXPIRY_SECS` | `604800` | Refresh token lifetime (7 days); active sessions auto-renew on use |
|
||||||
| `OXICLOUD_HASH_MEMORY_COST` | `65536` | Argon2id memory cost in KiB (64 MiB). **Server-side** — used by the legacy password path (`POST /api/auth/login`) and the app-password Basic-Auth verifier. Distinct from `OXICLOUD_OPAQUE_KSF_*` (client-side). |
|
| `OXICLOUD_HASH_MEMORY_COST` | `65536` | Argon2id memory cost in KiB (64 MiB). **Server-side** — used by the legacy password path (`POST /api/auth/login`) and the app-password Basic-Auth verifier. Distinct from `OXICLOUD_AUTH_OPAQUE_KSF_*` (client-side). |
|
||||||
| `OXICLOUD_HASH_TIME_COST` | `3` | Argon2id iteration count for the server-side legacy path. |
|
| `OXICLOUD_HASH_TIME_COST` | `3` | Argon2id iteration count for the server-side legacy path. |
|
||||||
| `OXICLOUD_HASH_PARALLELISM` | `2` | Argon2id parallelism lanes for the server-side legacy path. |
|
| `OXICLOUD_HASH_PARALLELISM` | `2` | Argon2id parallelism lanes for the server-side legacy path. |
|
||||||
| `OXICLOUD_DISABLE_REGISTRATION` | false | Disable registration of new user accounts |
|
| `OXICLOUD_DISABLE_REGISTRATION` | false | Disable registration of new user accounts |
|
||||||
@@ -52,15 +52,15 @@ Most runtime variables use the `OXICLOUD_` prefix. A few build-time or allocator
|
|||||||
|
|
||||||
### OPAQUE aPAKE (zero-knowledge password login)
|
### OPAQUE aPAKE (zero-knowledge password login)
|
||||||
|
|
||||||
OPAQUE (RFC 9807) is a zero-knowledge password-authenticated key exchange: the passphrase never leaves the client, not on registration and not on login. It's shipped in stages (see `docs/plan/opaque.md`); this build carries the **substrate only** — endpoints are inert until `OXICLOUD_OPAQUE_MODE` is set. See `docs/config/authentication.md` for the phase rollout, the migration plan, and admin-facing guidance.
|
OPAQUE (RFC 9807) is a zero-knowledge password-authenticated key exchange: the passphrase never leaves the client, not on registration and not on login. It's shipped in stages (see `docs/plan/opaque.md`); this build carries the **substrate only** — endpoints are inert until `OXICLOUD_AUTH_OPAQUE_MODE` is set. See `docs/config/authentication.md` for the phase rollout, the migration plan, and admin-facing guidance.
|
||||||
|
|
||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `OXICLOUD_OPAQUE_MODE` | `off` | Runtime mode. `off` = endpoints 404 (default). `migrate` = endpoints live, legacy `POST /api/auth/login` still accepted. `opaque_only` = endpoints live, legacy refused for users with an envelope. **Effective-mode cross-check**: when `password` is not in `OXICLOUD_AUTH_METHODS`, the mode is auto-downgraded to `off` with an audit-channel INFO line (OPAQUE only replaces the password path — nothing to shadow in an OIDC-only or magic-link-only deployment). So OIDC / magic-link-only operators can safely ignore every `OXICLOUD_OPAQUE_*` variable. |
|
| `OXICLOUD_AUTH_OPAQUE_MODE` | `off` | Runtime mode. `off` = endpoints 404 (default). `migrate` = endpoints live, legacy `POST /api/auth/login` still accepted. `opaque_only` = endpoints live, legacy refused for users with an envelope. **Effective-mode cross-check**: when `password` is not in `OXICLOUD_AUTH_METHODS`, the mode is auto-downgraded to `off` with an audit-channel INFO line (OPAQUE only replaces the password path — nothing to shadow in an OIDC-only or magic-link-only deployment). So OIDC / magic-link-only operators can safely ignore every `OXICLOUD_AUTH_OPAQUE_*` variable. |
|
||||||
| `OXICLOUD_OPAQUE_SERVER_SETUP` | — | Base64-encoded `ServerSetup` blob. **Required** when `OXICLOUD_OPAQUE_MODE != off` AND password is enabled — the server refuses to start with a helpful error otherwise. Generate once with the `opaque-setup` CLI subcommand and persist the value like your JWT secret. **Never rotate** — rotating invalidates every user's envelope (they'd all need to reset their passphrase). |
|
| `OXICLOUD_AUTH_OPAQUE_SERVER_SETUP` | — | Base64-encoded `ServerSetup` blob. **Required** when `OXICLOUD_AUTH_OPAQUE_MODE != off` AND password is enabled — the server refuses to start with a helpful error otherwise. Generate once with the `opaque-setup` CLI subcommand and persist the value like your JWT secret. **Never rotate** — rotating invalidates every user's envelope (they'd all need to reset their passphrase). |
|
||||||
| `OXICLOUD_OPAQUE_KSF_MEMORY_KIB` | `262144` | Client-side Argon2id memory cost in KiB (256 MiB). Runs on the user's device during OPAQUE login/registration, not on the server. Distinct from `OXICLOUD_HASH_MEMORY_COST` (server-side legacy path). Higher values slow brute-force after a hypothetical envelope leak but also slow login on the user's device. |
|
| `OXICLOUD_AUTH_OPAQUE_KSF_MEMORY_KIB` | `262144` | Client-side Argon2id memory cost in KiB (256 MiB). Runs on the user's device during OPAQUE login/registration, not on the server. Distinct from `OXICLOUD_HASH_MEMORY_COST` (server-side legacy path). Higher values slow brute-force after a hypothetical envelope leak but also slow login on the user's device. |
|
||||||
| `OXICLOUD_OPAQUE_KSF_ITERATIONS` | `3` | Client-side Argon2id iteration count. |
|
| `OXICLOUD_AUTH_OPAQUE_KSF_ITERATIONS` | `3` | Client-side Argon2id iteration count. |
|
||||||
| `OXICLOUD_OPAQUE_KSF_PARALLELISM` | `4` | Client-side Argon2id parallelism lanes. |
|
| `OXICLOUD_AUTH_OPAQUE_KSF_PARALLELISM` | `4` | Client-side Argon2id parallelism lanes. |
|
||||||
|
|
||||||
### Rate Limiting & Account Lockout
|
### Rate Limiting & Account Lockout
|
||||||
|
|
||||||
|
|||||||
+8
-10
@@ -188,15 +188,13 @@ DATABASE_URL=postgres://postgres:postgres@localhost:5432/oxicloud
|
|||||||
# Parallelism lanes (default: 2)
|
# Parallelism lanes (default: 2)
|
||||||
#OXICLOUD_HASH_PARALLELISM=2
|
#OXICLOUD_HASH_PARALLELISM=2
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# --- OPAQUE aPAKE (zero-knowledge password login, RFC 9807) ------------------
|
||||||
# OPAQUE aPAKE (zero-knowledge password login, RFC 9807)
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# OPAQUE replaces `POST /api/auth/login` with a zero-knowledge exchange:
|
# OPAQUE replaces `POST /api/auth/login` with a zero-knowledge exchange:
|
||||||
# the passphrase never leaves the client, not on registration and not on
|
# the passphrase never leaves the client, not on registration and not on
|
||||||
# login. This is the substrate for later E2EE work.
|
# login. This is the substrate for later E2EE work.
|
||||||
#
|
#
|
||||||
# Phase 0 (this build) ships the primitives only — endpoints are inert
|
# Phase 0 (this build) ships the primitives only — endpoints are inert
|
||||||
# until `OXICLOUD_OPAQUE_MODE` is set. Leave everything commented for a
|
# until `OXICLOUD_AUTH_OPAQUE_MODE` is set. Leave everything commented for a
|
||||||
# no-op install; OIDC-only and magic-link-only deployments never need to
|
# no-op install; OIDC-only and magic-link-only deployments never need to
|
||||||
# touch OPAQUE at all (see the effective-mode downgrade below).
|
# touch OPAQUE at all (see the effective-mode downgrade below).
|
||||||
|
|
||||||
@@ -208,12 +206,12 @@ DATABASE_URL=postgres://postgres:postgres@localhost:5432/oxicloud
|
|||||||
# is disabled via OXICLOUD_AUTH_METHODS (OPAQUE has nothing to shadow) —
|
# is disabled via OXICLOUD_AUTH_METHODS (OPAQUE has nothing to shadow) —
|
||||||
# an audit-channel log line explains why. So enabling this without
|
# an audit-channel log line explains why. So enabling this without
|
||||||
# password in OXICLOUD_AUTH_METHODS is a no-op, not a boot error.
|
# password in OXICLOUD_AUTH_METHODS is a no-op, not a boot error.
|
||||||
#OXICLOUD_OPAQUE_MODE=off
|
#OXICLOUD_AUTH_OPAQUE_MODE=off
|
||||||
|
|
||||||
# Persistent OPAQUE server keypair (base64-encoded ServerSetup blob).
|
# Persistent OPAQUE server keypair (base64-encoded ServerSetup blob).
|
||||||
# Generated ONCE per deployment; rotating this invalidates every user's
|
# Generated ONCE per deployment; rotating this invalidates every user's
|
||||||
# registration (they'd all be forced to re-register on next login). Only
|
# registration (they'd all be forced to re-register on next login). Only
|
||||||
# required when `OXICLOUD_OPAQUE_MODE != off` AND password auth is
|
# required when `OXICLOUD_AUTH_OPAQUE_MODE != off` AND password auth is
|
||||||
# enabled — otherwise the value is ignored.
|
# enabled — otherwise the value is ignored.
|
||||||
#
|
#
|
||||||
# Generate on first-time enable:
|
# Generate on first-time enable:
|
||||||
@@ -225,7 +223,7 @@ DATABASE_URL=postgres://postgres:postgres@localhost:5432/oxicloud
|
|||||||
# pipelines capture cleanly). Paste the printed line into your env or
|
# pipelines capture cleanly). Paste the printed line into your env or
|
||||||
# secrets manager. NEVER regenerate — treat it like your JWT secret;
|
# secrets manager. NEVER regenerate — treat it like your JWT secret;
|
||||||
# losing it forces every user to reset their passphrase.
|
# losing it forces every user to reset their passphrase.
|
||||||
#OXICLOUD_OPAQUE_SERVER_SETUP=
|
#OXICLOUD_AUTH_OPAQUE_SERVER_SETUP=
|
||||||
|
|
||||||
# Client-side Argon2id key-stretching parameters (RFC 9807 KSF).
|
# Client-side Argon2id key-stretching parameters (RFC 9807 KSF).
|
||||||
# These run on the USER'S DEVICE during OPAQUE login/registration —
|
# These run on the USER'S DEVICE during OPAQUE login/registration —
|
||||||
@@ -238,11 +236,11 @@ DATABASE_URL=postgres://postgres:postgres@localhost:5432/oxicloud
|
|||||||
# the user's next password change.
|
# the user's next password change.
|
||||||
#
|
#
|
||||||
# Memory cost in KiB (default: 262144 = 256 MiB)
|
# Memory cost in KiB (default: 262144 = 256 MiB)
|
||||||
#OXICLOUD_OPAQUE_KSF_MEMORY_KIB=262144
|
#OXICLOUD_AUTH_OPAQUE_KSF_MEMORY_KIB=262144
|
||||||
# Iterations (default: 3)
|
# Iterations (default: 3)
|
||||||
#OXICLOUD_OPAQUE_KSF_ITERATIONS=3
|
#OXICLOUD_AUTH_OPAQUE_KSF_ITERATIONS=3
|
||||||
# Parallelism lanes (default: 4)
|
# Parallelism lanes (default: 4)
|
||||||
#OXICLOUD_OPAQUE_KSF_PARALLELISM=4
|
#OXICLOUD_AUTH_OPAQUE_KSF_PARALLELISM=4
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# RATE LIMITING & ACCOUNT LOCKOUT
|
# RATE LIMITING & ACCOUNT LOCKOUT
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ pub struct AuthApplicationService {
|
|||||||
/// `AuthConfig::require_verified_email`.
|
/// `AuthConfig::require_verified_email`.
|
||||||
require_verified_email: bool,
|
require_verified_email: bool,
|
||||||
/// OPAQUE envelope repo — populated when the OPAQUE substrate is
|
/// OPAQUE envelope repo — populated when the OPAQUE substrate is
|
||||||
/// wired (`OXICLOUD_OPAQUE_MODE != off`). `login()` consults it to
|
/// wired (`OXICLOUD_AUTH_OPAQUE_MODE != off`). `login()` consults it to
|
||||||
/// enforce the Phase 4 gate: once a user has completed at least
|
/// enforce the Phase 4 gate: once a user has completed at least
|
||||||
/// one successful OPAQUE handshake (`opaque_migrated_at IS NOT
|
/// one successful OPAQUE handshake (`opaque_migrated_at IS NOT
|
||||||
/// NULL`), legacy `POST /api/auth/login` is refused for that
|
/// NULL`), legacy `POST /api/auth/login` is refused for that
|
||||||
@@ -366,7 +366,7 @@ impl AuthApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Wire the OPAQUE envelope repo. Called by the DI factory when the
|
/// Wire the OPAQUE envelope repo. Called by the DI factory when the
|
||||||
/// OPAQUE substrate is configured (`OXICLOUD_OPAQUE_MODE != off`).
|
/// OPAQUE substrate is configured (`OXICLOUD_AUTH_OPAQUE_MODE != off`).
|
||||||
/// Enables the Phase 4 legacy-login gate — see the field docstring.
|
/// Enables the Phase 4 legacy-login gate — see the field docstring.
|
||||||
pub fn with_opaque_repo(
|
pub fn with_opaque_repo(
|
||||||
mut self,
|
mut self,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! `opaque-setup` — one-shot operator helper that mints a fresh
|
//! `opaque-setup` — one-shot operator helper that mints a fresh
|
||||||
//! [`opaque_ke::ServerSetup`] and prints its base64 encoding to stdout.
|
//! [`opaque_ke::ServerSetup`] and prints its base64 encoding to stdout.
|
||||||
//!
|
//!
|
||||||
//! The output goes into `OXICLOUD_OPAQUE_SERVER_SETUP` (env var or secrets
|
//! The output goes into `OXICLOUD_AUTH_OPAQUE_SERVER_SETUP` (env var or secrets
|
||||||
//! manager) and MUST be persisted verbatim. Rotating it invalidates every
|
//! manager) and MUST be persisted verbatim. Rotating it invalidates every
|
||||||
//! user's registration — treat it like the JWT secret, only more so.
|
//! user's registration — treat it like the JWT secret, only more so.
|
||||||
//!
|
//!
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
//! ```text
|
//! ```text
|
||||||
//! cargo run --bin opaque-setup > opaque_setup.b64
|
//! cargo run --bin opaque-setup > opaque_setup.b64
|
||||||
//! # or paste directly into your env / .env file:
|
//! # or paste directly into your env / .env file:
|
||||||
//! echo "OXICLOUD_OPAQUE_SERVER_SETUP=$(cargo run --bin opaque-setup)" >> .env
|
//! echo "OXICLOUD_AUTH_OPAQUE_SERVER_SETUP=$(cargo run --bin opaque-setup)" >> .env
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! The generated value is a small (~64 byte) Ristretto255 keypair
|
//! The generated value is a small (~64 byte) Ristretto255 keypair
|
||||||
@@ -22,13 +22,13 @@ use oxicloud::infrastructure::services::opaque_service::OpaqueService;
|
|||||||
fn main() {
|
fn main() {
|
||||||
let b64 = OpaqueService::generate_server_setup_b64();
|
let b64 = OpaqueService::generate_server_setup_b64();
|
||||||
// Print JUST the value — no trailing newline commentary — so shell
|
// Print JUST the value — no trailing newline commentary — so shell
|
||||||
// pipelines (`OXICLOUD_OPAQUE_SERVER_SETUP=$(cargo run --bin opaque-setup)`)
|
// pipelines (`OXICLOUD_AUTH_OPAQUE_SERVER_SETUP=$(cargo run --bin opaque-setup)`)
|
||||||
// capture cleanly without needing `tr -d '\n'` afterwards.
|
// capture cleanly without needing `tr -d '\n'` afterwards.
|
||||||
println!("{b64}");
|
println!("{b64}");
|
||||||
// Guidance goes to stderr so it doesn't contaminate the pipeline.
|
// Guidance goes to stderr so it doesn't contaminate the pipeline.
|
||||||
eprintln!();
|
eprintln!();
|
||||||
eprintln!("=== OPAQUE server setup generated. ===");
|
eprintln!("=== OPAQUE server setup generated. ===");
|
||||||
eprintln!("Persist the line above in OXICLOUD_OPAQUE_SERVER_SETUP.");
|
eprintln!("Persist the line above in OXICLOUD_AUTH_OPAQUE_SERVER_SETUP.");
|
||||||
eprintln!("NEVER rotate: rotating invalidates every user's registration.");
|
eprintln!("NEVER rotate: rotating invalidates every user's registration.");
|
||||||
eprintln!("Treat this value like your JWT secret.");
|
eprintln!("Treat this value like your JWT secret.");
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-16
@@ -1667,17 +1667,17 @@ pub struct OpaqueConfig {
|
|||||||
/// [`crate::infrastructure::services::opaque_service::OpaqueMode`]
|
/// [`crate::infrastructure::services::opaque_service::OpaqueMode`]
|
||||||
/// for the state-machine and the phase-plan mapping.
|
/// for the state-machine and the phase-plan mapping.
|
||||||
///
|
///
|
||||||
/// Env: `OXICLOUD_OPAQUE_MODE` (`off` | `migrate` | `opaque_only`).
|
/// Env: `OXICLOUD_AUTH_OPAQUE_MODE` (`off` | `migrate` | `opaque_only`).
|
||||||
/// Default: `off`.
|
/// Default: `off`.
|
||||||
pub mode: crate::infrastructure::services::opaque_service::OpaqueMode,
|
pub mode: crate::infrastructure::services::opaque_service::OpaqueMode,
|
||||||
/// Base64-encoded [`opaque_ke::ServerSetup`] blob. Generated once
|
/// Base64-encoded [`opaque_ke::ServerSetup`] blob. Generated once
|
||||||
/// per deployment and persisted verbatim — rotating this invalidates
|
/// per deployment and persisted verbatim — rotating this invalidates
|
||||||
/// every user's registration. Runbook: on first boot with
|
/// every user's registration. Runbook: on first boot with
|
||||||
/// `OXICLOUD_OPAQUE_MODE != off`, if this is unset, print a fatal
|
/// `OXICLOUD_AUTH_OPAQUE_MODE != off`, if this is unset, print a fatal
|
||||||
/// message with a fresh setup for the operator to paste into their
|
/// message with a fresh setup for the operator to paste into their
|
||||||
/// env, then exit.
|
/// env, then exit.
|
||||||
///
|
///
|
||||||
/// Env: `OXICLOUD_OPAQUE_SERVER_SETUP`. No default.
|
/// Env: `OXICLOUD_AUTH_OPAQUE_SERVER_SETUP`. No default.
|
||||||
pub server_setup_b64: Option<String>,
|
pub server_setup_b64: Option<String>,
|
||||||
/// Ciphersuite version stamped into `auth.users.opaque_ciphersuite_version`
|
/// Ciphersuite version stamped into `auth.users.opaque_ciphersuite_version`
|
||||||
/// on registration. Bumping this without changing the actual
|
/// on registration. Bumping this without changing the actual
|
||||||
@@ -1690,15 +1690,15 @@ pub struct OpaqueConfig {
|
|||||||
/// the client can construct a matching `argon2::Argon2` before
|
/// the client can construct a matching `argon2::Argon2` before
|
||||||
/// running `ClientRegistration::start` / `ClientLogin::start`.
|
/// running `ClientRegistration::start` / `ClientLogin::start`.
|
||||||
///
|
///
|
||||||
/// Env: `OXICLOUD_OPAQUE_KSF_MEMORY_KIB`. Default: `262144` (256 MiB).
|
/// Env: `OXICLOUD_AUTH_OPAQUE_KSF_MEMORY_KIB`. Default: `262144` (256 MiB).
|
||||||
pub ksf_memory_kib: u32,
|
pub ksf_memory_kib: u32,
|
||||||
/// Client-side Argon2id iteration count.
|
/// Client-side Argon2id iteration count.
|
||||||
///
|
///
|
||||||
/// Env: `OXICLOUD_OPAQUE_KSF_ITERATIONS`. Default: `3`.
|
/// Env: `OXICLOUD_AUTH_OPAQUE_KSF_ITERATIONS`. Default: `3`.
|
||||||
pub ksf_iterations: u32,
|
pub ksf_iterations: u32,
|
||||||
/// Client-side Argon2id parallelism (lanes).
|
/// Client-side Argon2id parallelism (lanes).
|
||||||
///
|
///
|
||||||
/// Env: `OXICLOUD_OPAQUE_KSF_PARALLELISM`. Default: `4`.
|
/// Env: `OXICLOUD_AUTH_OPAQUE_KSF_PARALLELISM`. Default: `4`.
|
||||||
pub ksf_parallelism: u32,
|
pub ksf_parallelism: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1723,33 +1723,33 @@ impl OpaqueConfig {
|
|||||||
pub fn from_env() -> Self {
|
pub fn from_env() -> Self {
|
||||||
use std::env;
|
use std::env;
|
||||||
let mut cfg = Self::default();
|
let mut cfg = Self::default();
|
||||||
if let Ok(v) = env::var("OXICLOUD_OPAQUE_MODE") {
|
if let Ok(v) = env::var("OXICLOUD_AUTH_OPAQUE_MODE") {
|
||||||
match crate::infrastructure::services::opaque_service::OpaqueMode::parse(&v) {
|
match crate::infrastructure::services::opaque_service::OpaqueMode::parse(&v) {
|
||||||
Some(m) => cfg.mode = m,
|
Some(m) => cfg.mode = m,
|
||||||
None => {
|
None => {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
target: "oxicloud::config",
|
target: "oxicloud::config",
|
||||||
value = %v,
|
value = %v,
|
||||||
"OXICLOUD_OPAQUE_MODE has an unrecognised value — keeping default (off). \
|
"OXICLOUD_AUTH_OPAQUE_MODE has an unrecognised value — keeping default (off). \
|
||||||
Accepted: off | migrate | opaque_only"
|
Accepted: off | migrate | opaque_only"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Ok(v) = env::var("OXICLOUD_OPAQUE_SERVER_SETUP") {
|
if let Ok(v) = env::var("OXICLOUD_AUTH_OPAQUE_SERVER_SETUP") {
|
||||||
cfg.server_setup_b64 = Some(v);
|
cfg.server_setup_b64 = Some(v);
|
||||||
}
|
}
|
||||||
if let Ok(v) = env::var("OXICLOUD_OPAQUE_KSF_MEMORY_KIB")
|
if let Ok(v) = env::var("OXICLOUD_AUTH_OPAQUE_KSF_MEMORY_KIB")
|
||||||
&& let Ok(n) = v.parse::<u32>()
|
&& let Ok(n) = v.parse::<u32>()
|
||||||
{
|
{
|
||||||
cfg.ksf_memory_kib = n;
|
cfg.ksf_memory_kib = n;
|
||||||
}
|
}
|
||||||
if let Ok(v) = env::var("OXICLOUD_OPAQUE_KSF_ITERATIONS")
|
if let Ok(v) = env::var("OXICLOUD_AUTH_OPAQUE_KSF_ITERATIONS")
|
||||||
&& let Ok(n) = v.parse::<u32>()
|
&& let Ok(n) = v.parse::<u32>()
|
||||||
{
|
{
|
||||||
cfg.ksf_iterations = n;
|
cfg.ksf_iterations = n;
|
||||||
}
|
}
|
||||||
if let Ok(v) = env::var("OXICLOUD_OPAQUE_KSF_PARALLELISM")
|
if let Ok(v) = env::var("OXICLOUD_AUTH_OPAQUE_KSF_PARALLELISM")
|
||||||
&& let Ok(n) = v.parse::<u32>()
|
&& let Ok(n) = v.parse::<u32>()
|
||||||
{
|
{
|
||||||
cfg.ksf_parallelism = n;
|
cfg.ksf_parallelism = n;
|
||||||
@@ -1764,7 +1764,7 @@ impl OpaqueConfig {
|
|||||||
/// OIDC-only or magic-link-only (`OXICLOUD_AUTH_METHODS=oidc` or
|
/// OIDC-only or magic-link-only (`OXICLOUD_AUTH_METHODS=oidc` or
|
||||||
/// `=magic_link`) has no password path for OPAQUE to shadow; any
|
/// `=magic_link`) has no password path for OPAQUE to shadow; any
|
||||||
/// non-`Off` mode would be a no-op that still nagged them for
|
/// non-`Off` mode would be a no-op that still nagged them for
|
||||||
/// `OXICLOUD_OPAQUE_SERVER_SETUP` at boot.
|
/// `OXICLOUD_AUTH_OPAQUE_SERVER_SETUP` at boot.
|
||||||
///
|
///
|
||||||
/// This helper resolves the misconfig quietly: if password isn't in
|
/// This helper resolves the misconfig quietly: if password isn't in
|
||||||
/// the allowlist AND OPAQUE mode is non-`Off`, we downgrade to `Off`
|
/// the allowlist AND OPAQUE mode is non-`Off`, we downgrade to `Off`
|
||||||
@@ -1787,7 +1787,7 @@ impl OpaqueConfig {
|
|||||||
event = "opaque.mode_downgraded",
|
event = "opaque.mode_downgraded",
|
||||||
reason = "password_auth_disabled",
|
reason = "password_auth_disabled",
|
||||||
configured_mode = ?self.mode,
|
configured_mode = ?self.mode,
|
||||||
"OXICLOUD_OPAQUE_MODE is configured but password auth is disabled \
|
"OXICLOUD_AUTH_OPAQUE_MODE is configured but password auth is disabled \
|
||||||
via OXICLOUD_AUTH_METHODS — treating OPAQUE as off. \
|
via OXICLOUD_AUTH_METHODS — treating OPAQUE as off. \
|
||||||
OPAQUE only replaces the password login path; enable password \
|
OPAQUE only replaces the password login path; enable password \
|
||||||
in OXICLOUD_AUTH_METHODS to make this setting take effect."
|
in OXICLOUD_AUTH_METHODS to make this setting take effect."
|
||||||
@@ -2490,7 +2490,7 @@ pub struct AppConfig {
|
|||||||
pub auth: AuthConfig,
|
pub auth: AuthConfig,
|
||||||
/// OPAQUE (RFC 9807) zero-knowledge password auth configuration.
|
/// OPAQUE (RFC 9807) zero-knowledge password auth configuration.
|
||||||
/// Substrate only in Phase 0 — endpoints are inert until
|
/// Substrate only in Phase 0 — endpoints are inert until
|
||||||
/// `OXICLOUD_OPAQUE_MODE != off`.
|
/// `OXICLOUD_AUTH_OPAQUE_MODE != off`.
|
||||||
pub opaque: OpaqueConfig,
|
pub opaque: OpaqueConfig,
|
||||||
/// Feature configuration
|
/// Feature configuration
|
||||||
pub features: FeaturesConfig,
|
pub features: FeaturesConfig,
|
||||||
@@ -4332,7 +4332,7 @@ mod tests {
|
|||||||
//
|
//
|
||||||
// OPAQUE is fundamentally a password mechanism; enabling its mode when
|
// OPAQUE is fundamentally a password mechanism; enabling its mode when
|
||||||
// password auth is disabled would be a no-op that still nagged
|
// password auth is disabled would be a no-op that still nagged
|
||||||
// operators for `OXICLOUD_OPAQUE_SERVER_SETUP` at boot. The
|
// operators for `OXICLOUD_AUTH_OPAQUE_SERVER_SETUP` at boot. The
|
||||||
// `effective_mode` helper resolves that quietly by downgrading to
|
// `effective_mode` helper resolves that quietly by downgrading to
|
||||||
// Off + emitting an audit log, and these tests pin the truth table.
|
// Off + emitting an audit log, and these tests pin the truth table.
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -1936,7 +1936,7 @@ impl AppServiceFactory {
|
|||||||
// `OXICLOUD_AUTH_METHODS` (password must be enabled for OPAQUE to
|
// `OXICLOUD_AUTH_METHODS` (password must be enabled for OPAQUE to
|
||||||
// have anything to shadow), so OIDC-only / magic-link-only
|
// have anything to shadow), so OIDC-only / magic-link-only
|
||||||
// deployments transparently get `opaque_service = None` even if
|
// deployments transparently get `opaque_service = None` even if
|
||||||
// the operator accidentally set `OXICLOUD_OPAQUE_MODE=migrate`.
|
// the operator accidentally set `OXICLOUD_AUTH_OPAQUE_MODE=migrate`.
|
||||||
//
|
//
|
||||||
// Failing here (missing SERVER_SETUP, malformed base64, ciphersuite
|
// Failing here (missing SERVER_SETUP, malformed base64, ciphersuite
|
||||||
// drift) refuses server boot — same fail-closed posture as the
|
// drift) refuses server boot — same fail-closed posture as the
|
||||||
@@ -1957,9 +1957,9 @@ impl AppServiceFactory {
|
|||||||
DomainError::internal_error(
|
DomainError::internal_error(
|
||||||
"OpaqueInit",
|
"OpaqueInit",
|
||||||
format!(
|
format!(
|
||||||
"OXICLOUD_OPAQUE_MODE={:?} but the OPAQUE service failed: {}. \
|
"OXICLOUD_AUTH_OPAQUE_MODE={:?} but the OPAQUE service failed: {}. \
|
||||||
Persist a valid OXICLOUD_OPAQUE_SERVER_SETUP or set \
|
Persist a valid OXICLOUD_AUTH_OPAQUE_SERVER_SETUP or set \
|
||||||
OXICLOUD_OPAQUE_MODE=off. Refusing to start.",
|
OXICLOUD_AUTH_OPAQUE_MODE=off. Refusing to start.",
|
||||||
effective, e
|
effective, e
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -2850,9 +2850,9 @@ pub struct AppState {
|
|||||||
pub auth_service: Option<AuthServices>,
|
pub auth_service: Option<AuthServices>,
|
||||||
/// OPAQUE aPAKE substrate (RFC 9807). Populated only when
|
/// OPAQUE aPAKE substrate (RFC 9807). Populated only when
|
||||||
/// [`OpaqueConfig::effective_mode`] is not `Off` — that method
|
/// [`OpaqueConfig::effective_mode`] is not `Off` — that method
|
||||||
/// cross-checks `OXICLOUD_OPAQUE_MODE` against
|
/// cross-checks `OXICLOUD_AUTH_OPAQUE_MODE` against
|
||||||
/// `OXICLOUD_AUTH_METHODS` so an OIDC-only or magic-link-only
|
/// `OXICLOUD_AUTH_METHODS` so an OIDC-only or magic-link-only
|
||||||
/// deployment gets `None` here even if `OXICLOUD_OPAQUE_MODE` was
|
/// deployment gets `None` here even if `OXICLOUD_AUTH_OPAQUE_MODE` was
|
||||||
/// set (with an audit-channel INFO explaining why). `None` also
|
/// set (with an audit-channel INFO explaining why). `None` also
|
||||||
/// means the future OPAQUE endpoints must 404 — a handler that
|
/// means the future OPAQUE endpoints must 404 — a handler that
|
||||||
/// unwraps this without a nil check would break the phase gate.
|
/// unwraps this without a nil check would break the phase gate.
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ mod integration_tests {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Fresh server setup for this test only — mirrors what the
|
// Fresh server setup for this test only — mirrors what the
|
||||||
// DI factory would load from OXICLOUD_OPAQUE_SERVER_SETUP.
|
// DI factory would load from OXICLOUD_AUTH_OPAQUE_SERVER_SETUP.
|
||||||
let mut server_rng = OsRng;
|
let mut server_rng = OsRng;
|
||||||
let server_setup = ServerSetup::<OxiCloudSuite>::new(&mut server_rng);
|
let server_setup = ServerSetup::<OxiCloudSuite>::new(&mut server_rng);
|
||||||
|
|
||||||
|
|||||||
@@ -72,10 +72,10 @@ pub struct OpaqueService {
|
|||||||
|
|
||||||
impl OpaqueService {
|
impl OpaqueService {
|
||||||
/// Build the service from runtime config. Expects the operator to have
|
/// Build the service from runtime config. Expects the operator to have
|
||||||
/// persisted the server setup already (via `OXICLOUD_OPAQUE_SERVER_SETUP`);
|
/// persisted the server setup already (via `OXICLOUD_AUTH_OPAQUE_SERVER_SETUP`);
|
||||||
/// call [`OpaqueService::generate_server_setup_b64`] first-time and print
|
/// call [`OpaqueService::generate_server_setup_b64`] first-time and print
|
||||||
/// the value for the operator to paste into their env before enabling
|
/// the value for the operator to paste into their env before enabling
|
||||||
/// `OXICLOUD_OPAQUE_MODE`.
|
/// `OXICLOUD_AUTH_OPAQUE_MODE`.
|
||||||
///
|
///
|
||||||
/// Rejects with `InternalError` if the setup is missing / malformed, or
|
/// Rejects with `InternalError` if the setup is missing / malformed, or
|
||||||
/// with `AccessDenied` if the mode is `off` (guarding against
|
/// with `AccessDenied` if the mode is `off` (guarding against
|
||||||
@@ -84,14 +84,14 @@ impl OpaqueService {
|
|||||||
if config.mode == OpaqueMode::Off {
|
if config.mode == OpaqueMode::Off {
|
||||||
return Err(DomainError::access_denied(
|
return Err(DomainError::access_denied(
|
||||||
"opaque",
|
"opaque",
|
||||||
"OPAQUE is disabled (OXICLOUD_OPAQUE_MODE=off)",
|
"OPAQUE is disabled (OXICLOUD_AUTH_OPAQUE_MODE=off)",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let setup_b64 = config.server_setup_b64.as_deref().ok_or_else(|| {
|
let setup_b64 = config.server_setup_b64.as_deref().ok_or_else(|| {
|
||||||
DomainError::new(
|
DomainError::new(
|
||||||
ErrorKind::InternalError,
|
ErrorKind::InternalError,
|
||||||
"opaque",
|
"opaque",
|
||||||
"OXICLOUD_OPAQUE_SERVER_SETUP is required when OPAQUE is enabled — \
|
"OXICLOUD_AUTH_OPAQUE_SERVER_SETUP is required when OPAQUE is enabled — \
|
||||||
generate one with `oxicloud opaque-setup` and persist it in the env",
|
generate one with `oxicloud opaque-setup` and persist it in the env",
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
@@ -141,7 +141,7 @@ impl OpaqueService {
|
|||||||
|
|
||||||
/// Generate a fresh server setup and return it as base64. Called once
|
/// Generate a fresh server setup and return it as base64. Called once
|
||||||
/// per deployment; the returned string must be persisted in
|
/// per deployment; the returned string must be persisted in
|
||||||
/// `OXICLOUD_OPAQUE_SERVER_SETUP` and NEVER rotated (rotating
|
/// `OXICLOUD_AUTH_OPAQUE_SERVER_SETUP` and NEVER rotated (rotating
|
||||||
/// invalidates every existing envelope — see
|
/// invalidates every existing envelope — see
|
||||||
/// `docs/plan/opaque.md` §Phase 0).
|
/// `docs/plan/opaque.md` §Phase 0).
|
||||||
pub fn generate_server_setup_b64() -> String {
|
pub fn generate_server_setup_b64() -> String {
|
||||||
@@ -160,7 +160,7 @@ fn decode_server_setup(b64: &str) -> Result<ServerSetup<OxiCloudSuite>, DomainEr
|
|||||||
DomainError::new(
|
DomainError::new(
|
||||||
ErrorKind::InternalError,
|
ErrorKind::InternalError,
|
||||||
"opaque",
|
"opaque",
|
||||||
format!("OXICLOUD_OPAQUE_SERVER_SETUP is not valid base64: {e}"),
|
format!("OXICLOUD_AUTH_OPAQUE_SERVER_SETUP is not valid base64: {e}"),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
ServerSetup::<OxiCloudSuite>::deserialize(&bytes).map_err(|e| {
|
ServerSetup::<OxiCloudSuite>::deserialize(&bytes).map_err(|e| {
|
||||||
@@ -168,7 +168,7 @@ fn decode_server_setup(b64: &str) -> Result<ServerSetup<OxiCloudSuite>, DomainEr
|
|||||||
ErrorKind::InternalError,
|
ErrorKind::InternalError,
|
||||||
"opaque",
|
"opaque",
|
||||||
format!(
|
format!(
|
||||||
"OXICLOUD_OPAQUE_SERVER_SETUP payload does not match ciphersuite v1: {e}. \
|
"OXICLOUD_AUTH_OPAQUE_SERVER_SETUP payload does not match ciphersuite v1: {e}. \
|
||||||
If you rotated the ciphersuite, every user must re-register."
|
If you rotated the ciphersuite, every user must re-register."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -256,7 +256,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let err = OpaqueService::from_config(cfg).expect_err("must reject missing setup");
|
let err = OpaqueService::from_config(cfg).expect_err("must reject missing setup");
|
||||||
assert_eq!(err.kind, ErrorKind::InternalError);
|
assert_eq!(err.kind, ErrorKind::InternalError);
|
||||||
assert!(err.to_string().contains("OXICLOUD_OPAQUE_SERVER_SETUP"));
|
assert!(err.to_string().contains("OXICLOUD_AUTH_OPAQUE_SERVER_SETUP"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
# 1. The server booted with the OPAQUE substrate loaded — proved
|
# 1. The server booted with the OPAQUE substrate loaded — proved
|
||||||
# transitively by the fact that this suite reached the
|
# transitively by the fact that this suite reached the
|
||||||
# `--test-report` stage at all. `tests/common/server.env` sets
|
# `--test-report` stage at all. `tests/common/server.env` sets
|
||||||
# `OXICLOUD_OPAQUE_MODE=migrate` + a persisted `SERVER_SETUP`;
|
# `OXICLOUD_AUTH_OPAQUE_MODE=migrate` + a persisted `SERVER_SETUP`;
|
||||||
# a boot failure (bad base64, missing setup, ciphersuite drift)
|
# a boot failure (bad base64, missing setup, ciphersuite drift)
|
||||||
# would 500 every request or refuse to bind the port.
|
# would 500 every request or refuse to bind the port.
|
||||||
#
|
#
|
||||||
@@ -191,7 +191,7 @@ jsonpath "$.error_type" == "InvalidCredentials"
|
|||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────
|
||||||
# Case 9 — Params publish returns enabled=true under the test
|
# Case 9 — Params publish returns enabled=true under the test
|
||||||
# env (`OXICLOUD_OPAQUE_MODE=migrate`), the current
|
# env (`OXICLOUD_AUTH_OPAQUE_MODE=migrate`), the current
|
||||||
# ciphersuite version (1 — see `docs/config/env.md`),
|
# ciphersuite version (1 — see `docs/config/env.md`),
|
||||||
# and the fast test-only KSF params
|
# and the fast test-only KSF params
|
||||||
# (memoryKib=8 / iter=1 / lanes=1 from server.env).
|
# (memoryKib=8 / iter=1 / lanes=1 from server.env).
|
||||||
|
|||||||
@@ -136,12 +136,12 @@ OXICLOUD_TRUST_PROXY_CIDR=0.0.0.0/0
|
|||||||
# test env — real deployments call `opaque-setup` and paste the output.
|
# test env — real deployments call `opaque-setup` and paste the output.
|
||||||
# Never reuse this value outside CI. Regenerate any time with:
|
# Never reuse this value outside CI. Regenerate any time with:
|
||||||
# cargo run --bin opaque-setup
|
# cargo run --bin opaque-setup
|
||||||
OXICLOUD_OPAQUE_MODE=migrate
|
OXICLOUD_AUTH_OPAQUE_MODE=migrate
|
||||||
OXICLOUD_OPAQUE_SERVER_SETUP="ZY4hAGa1MNyE7Ht+8ksLcyMmi/K2iJvxQly+DdfllUxjiH0+CjCt4hG6+9Y68jGet2L213dV0hajCbr4fXnekkWtUxqLr+butVHEksZ9NJRuZTvS6SMC73yf/yku4WUHT1NSRB2yHurAFmYn75D9wdA1VaXTuwgO/u5i1pvcsQs="
|
OXICLOUD_AUTH_OPAQUE_SERVER_SETUP="ZY4hAGa1MNyE7Ht+8ksLcyMmi/K2iJvxQly+DdfllUxjiH0+CjCt4hG6+9Y68jGet2L213dV0hajCbr4fXnekkWtUxqLr+butVHEksZ9NJRuZTvS6SMC73yf/yku4WUHT1NSRB2yHurAFmYn75D9wdA1VaXTuwgO/u5i1pvcsQs="
|
||||||
# Fast Argon2id — CI machines are underpowered vs production (256 MiB
|
# Fast Argon2id — CI machines are underpowered vs production (256 MiB
|
||||||
# default would drag every test-scaffold future OPAQUE handshake in
|
# default would drag every test-scaffold future OPAQUE handshake in
|
||||||
# Phase 1+). Matches the params used in the round-trip unit test.
|
# Phase 1+). Matches the params used in the round-trip unit test.
|
||||||
OXICLOUD_OPAQUE_KSF_MEMORY_KIB=8
|
OXICLOUD_AUTH_OPAQUE_KSF_MEMORY_KIB=8
|
||||||
OXICLOUD_OPAQUE_KSF_ITERATIONS=1
|
OXICLOUD_AUTH_OPAQUE_KSF_ITERATIONS=1
|
||||||
OXICLOUD_OPAQUE_KSF_PARALLELISM=1
|
OXICLOUD_AUTH_OPAQUE_KSF_PARALLELISM=1
|
||||||
|
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ export async function startStack(): Promise<Stack> {
|
|||||||
// OPAQUE substrate off for the container E2E — same rationale as the
|
// OPAQUE substrate off for the container E2E — same rationale as the
|
||||||
// playwright.config.ts / playwright.coverage.config.ts overrides.
|
// playwright.config.ts / playwright.coverage.config.ts overrides.
|
||||||
// The Hurl suite exercises OPAQUE via server.env directly.
|
// The Hurl suite exercises OPAQUE via server.env directly.
|
||||||
OXICLOUD_OPAQUE_MODE: 'off',
|
OXICLOUD_AUTH_OPAQUE_MODE: 'off',
|
||||||
OXICLOUD_OPAQUE_SERVER_SETUP: '',
|
OXICLOUD_AUTH_OPAQUE_SERVER_SETUP: '',
|
||||||
})
|
})
|
||||||
// /health is a fast liveness probe (no DB hit); 200 means the HTTP server
|
// /health is a fast liveness probe (no DB hit); 200 means the HTTP server
|
||||||
// is up and sqlx migrations have completed.
|
// is up and sqlx migrations have completed.
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ export default defineConfig({
|
|||||||
// need the boot-time init nor the ~200 KiB WASM client. Blanking
|
// need the boot-time init nor the ~200 KiB WASM client. Blanking
|
||||||
// the inherited commonEnv values takes the DI factory's
|
// the inherited commonEnv values takes the DI factory's
|
||||||
// `effective_mode == Off` short-circuit.
|
// `effective_mode == Off` short-circuit.
|
||||||
OXICLOUD_OPAQUE_MODE: 'off',
|
OXICLOUD_AUTH_OPAQUE_MODE: 'off',
|
||||||
OXICLOUD_OPAQUE_SERVER_SETUP: '',
|
OXICLOUD_AUTH_OPAQUE_SERVER_SETUP: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ export default defineConfig({
|
|||||||
// WASM bundle nor the boot-time substrate init. Blank the two OPAQUE
|
// WASM bundle nor the boot-time substrate init. Blank the two OPAQUE
|
||||||
// env vars inherited from commonEnv so the DI factory takes the
|
// env vars inherited from commonEnv so the DI factory takes the
|
||||||
// `effective_mode == Off` short-circuit path.
|
// `effective_mode == Off` short-circuit path.
|
||||||
OXICLOUD_OPAQUE_MODE: 'off',
|
OXICLOUD_AUTH_OPAQUE_MODE: 'off',
|
||||||
OXICLOUD_OPAQUE_SERVER_SETUP: '',
|
OXICLOUD_AUTH_OPAQUE_SERVER_SETUP: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user