fix(swarm-authelia): guard bearerAuthz against a silent no-op on kind

Review found that nothing stopped `bearerAuthz = true` on an interactive
client. `renderClient` reads the flag only in the machine branch, so the
scope is never emitted: the client authenticates, is authorised for
nothing, and the build is green. `kind` defaults to `interactive`, so it
is reached by forgetting a field rather than by writing a wrong one —
this module's own failure mode one level up.

The other half of the review asked to relax the method assertion to
accept `null`, on the strength of the option's doc calling `null`
authelia's default. Measured instead: under `authelia.bearer.authz`
authelia refuses the omission outright, so the assertion was right and
the DOC was wrong. The doc now carries the exception, and the assertion
message says null is refused rather than leaving a reader to infer it.
This commit is contained in:
atlas 2026-08-24 13:14:23 +02:00
commit 94204ac82b

View file

@ -587,6 +587,15 @@ in
a successful consent** the login looks like it worked
right up to the last hop, and neither the redirect nor
the secret is at fault.
`null` is NOT accepted on a client with `bearerAuthz`.
Measured against authelia 4.39.20: under that scope the
method must be *stated*: omitting it is refused with
`must be configured as 'client_secret_basic', but it's
configured as` an empty string. The sentence above is
true of an ordinary client and false of that one, which
is exactly how a reviewer reads this option and concludes
the assertion below is wrong.
'';
};
@ -935,7 +944,30 @@ in
+ ". A confidential client carrying authelia.bearer.authz must "
+ "authenticate with client_secret_basic, client_secret_jwt or "
+ "private_key_jwt. Notably client_secret_post is refused, and "
+ "it is what an OAuth2 client library may reach for first.";
+ "it is what an OAuth2 client library may reach for first. "
+ "Leaving it null is refused too: authelia requires the method "
+ "to be STATED under this scope rather than defaulted.";
}
# Nothing else stops `bearerAuthz` on an interactive client, and it
# would silently do nothing: `renderClient` reads the flag only in
# the `machine` branch, so the scope is simply never emitted and the
# client authenticates fine while being authorised for nothing.
#
# That is this module's own failure mode one level up — a green
# build and a grant that does not exist — and `kind` defaults to
# `interactive`, so it is reached by FORGETTING a field rather than
# by writing a wrong one.
{
assertion = lib.all (c: !c.bearerAuthz || c.kind == "machine") cfg.oidc.clients;
message =
"services.hyperhive.swarm.authelia.oidc.clients: "
+ lib.concatStringsSep ", " (
map (c: "client '${c.id}'") (lib.filter (c: c.bearerAuthz && c.kind != "machine") cfg.oidc.clients)
)
+ " sets bearerAuthz with kind != \"machine\". The scope is only "
+ "emitted for machine clients, so this grants nothing while "
+ "evaluating and deploying cleanly. A browser client has a user "
+ "to authorise and does not need it.";
}
];