Configure portal user transforms: matchers, typed claims, role actions, conditional challenges, MFA, and deny rules. Use for authentication-time policy; stored account rules belong to configuration-users.
66
83%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Use for transform user or transform users inside an authentication portal.
caddyfile_authn_transform.go forwards the complete block to the selected
module's pkg/authn/transformer/parser; provisioning resolves individual
arguments and compiles the result again. Inspect go list -m -json github.com/greenpau/go-authcrunch before relying on sibling source. The
published v1.3.8 supports the grammar below.
The surrounding portal configuration owns wiring; static users own stored challenge rules. The authentication flow contract owns login and profile API behavior. Load those details only when changing the corresponding boundary, rather than reloading the portal router for a transform.
Every block needs at least one matcher and one action. Conditions combine as
match-all. Ordinary bare match retains the historical exact match spelling
in adapted JSON; match any stays unconditional and match github retains its
provider-specific spelling, including malformed statements for shared validation.
Classification uses the shared parser, so a claim value containing the word match remains an action.
These are alternative statements inside a transform, not a complete config:
match any
match realm local
field email exists
field picture not exists
partial match email @example.com
no regex match any role ^authp/(admin|user)$
action add role authp/user
action overwrite roles authp/user
action drop matched role
action delete org
require mfa
deny
ui link "User Profile" /auth/profile/ icon "las la-cog" target_blankACL strategies are exact, partial, prefix, suffix, and regex, with
optional no and any according to pkg/acl/condition.go. Field aliases come
from pkg/acl/acl.go: for example role/group/groups → roles, mail →
email, and subject → sub. amr is a list of verified methods.
action is optional before add, overwrite, delete and drop; it does
not prefix require. block and deny are synonyms. Actions and matching
transforms retain declaration order. overwrite accepts known claim fields; delete also removes custom fields.
Custom claims use add with an explicit type:
add matrix_id "@{claims.sub}:matrix.example.com" as string
add teams "operations team" support as string list
add nested metadata label with "literal value" as string
add nested empty as mapA custom scalar needs exactly one value. List aliases are list, string_list
and the two keywords string list. Nested paths need at least one key; values
follow with, and an empty map uses as map. Nested values are literal;
ordinary string/list actions expand claim placeholders. Follow
pkg/authn/transformer/parser/custom_fields.go and its runtime consumer rather
than inferring grammar from JSON.
{env.*} and whole-value secrets:<id>:<key> resolve during Caddy provisioning.
{claims.*} templates survive that pass only in transform arguments and expand
at authentication time in action values. ACL matcher values remain literal.
Quotes, spaces and secrets remain a single argument;
empty resolved tokens and unknown Caddy placeholders in configured arguments
fail provisioning. Encoded native JSON actions and matchers must each contain
one line; reject CR/LF before decoding so a later CSV record cannot disappear.
Resolved multiline transform values also fail shared validation. Caddy does not
recursively expand inserted replacement data. See
runtime resolution.
Inside a portal, require both a stable account ID and organization membership:
transform user {
match github id exact 12345678
match github org exact acme
action add role authp/admin
}For alternatives, use separate blocks. An organization-only block can use regex:
transform user {
match github org regex ^(acme|acme-labs)$
action add role authp/user
}The four forms are match github id exact <id>,
match github id regex <pattern>, match github org exact <login> and
match github org regex <pattern>. Each accepts exactly one operand. Quote
patterns containing spaces or Caddy delimiters. Exact IDs are canonical positive
uint64 decimals: zero, signs, leading zeros, fractions, exponent notation and
overflow are rejected. Organization operands are login names, not display names
or numeric organization IDs. Matching is case-sensitive; regex uses Go regexp
search semantics. Anchor whole-value matches; request case folding with (?i).
Distinct conditions in one block are ANDed. One organization condition succeeds
if any eligible organization matches. Missing claims never satisfy these positive
matchers, even regex .*. Duplicate conditions for the same field, invalid
regex and malformed arguments fail shared validation without exposing operands.
Organization matching requires the existing provider-body setting:
user_org_filters .*Use narrower filters for eligible organizations. With no filter, lookup is
disabled and organization conditions cannot match. The lookup reads one page of
public membership from GitHub's organizations_url; it adds neither pagination
nor private membership discovery. Adding read:org alone does not change that
endpoint. See GitHub's list-user-organizations API
and the provider claim contract.
github_id is a lossless string derived from /user's numeric ID; metadata.id
remains numeric and sub remains github.com/<login>. Renaming an account leaves
ID matching stable. An absent ID does not match; a supplied malformed ID rejects
login. github_orgs contains filtered organization logins; existing
github.com/<org>/members groups remain available. The portal establishes trust
from the selected backend's driver, not realm names, origin, roles or groups.
Both claims are read-only to transform actions, including nested writes.
The shared compiler owns lowering and validation for Caddyfile and persisted
JSON configurations. Never implement a second GitHub parser in Caddy or rewrite
serialized matchers. Lower-level exact match github_id ... and
regex match github_orgs ... remain supported. A direct transformer factory
caller must supply trusted provider claims; arbitrary caller-created maps do
not establish authenticated GitHub identity.
match any applies without requiring token timestamps in selected AuthCrunch
v1.3.11. It is supported with portal refresh, OIDC and System API keys as well
as ordinary access-only login. The earlier Caddy compatibility restriction is
removed. Use realm matchers when policy should apply only to selected backends;
do not fabricate exp or rewrite matchers to make unconditional rules run.
TestPortalTransformMatchAnyIdentityContext and
TestPortalTransformMatchAnyEncoding check timed and untimed claims through
Caddy resolution, including quoted and runtime-resolved native JSON matchers.
The testcase_authenticate_with_match_any_refresh and
testcase_authenticate_with_match_any_system fixtures adapt and resolve.
The challenge TLS journey checks unconditional factor selection and claims in
login and refresh, successful OIDC identity revalidation, and Basic rejection
without the required proof. System API E2E checks unconditional transformed
claims and rejects password-only assertions when the policy requires TOTP.
Malformed multiline transforms still reject replacement without disturbing the
serving app. See the dependency qualification.
Inside a portal, this policy prefers an enrolled security key, then an enrolled TOTP token, then the account password:
transform user {
match realm local
require auth challenges u2f
require auth challenges totp if u2f not available
require auth challenges password if u2f and totp not available
}Rule bodies are parsed by pkg/authchal/parser:
<method> [<method>...] [if <method> [and <method>...] not available]
<method> [or <method>...] [if <method> [and <method>...] not available]Methods are password, totp, u2f, and mfa. Adjacent methods require all;
or selects the first available alternative. mfa represents an available
second factor. Conditions require the named credentials to be unavailable.
Do not mix an or choice with adjacent-method requirements. Duplicate rules,
unknown methods and email methods/conditions are rejected: the portal has no
email checkpoint. Method keywords are literal configuration, not placeholders.
The first eligible rule across matching transforms replaces backend/user
challenge selection. Credential availability comes from server-owned inventory,
never roles, amr, or transformed claims. If a matched conditional policy has
no eligible rule, authentication fails; it does not fall back to a password.
Without a matching conditional policy, stored user rules/defaults apply.
Legacy require password|mfa|totp|u2f remains additive after selection; it can
force MFA enrollment when appropriate. Replacing the backend policy can remove
the password checkpoint: a TOTP-only or U2F-only rule is a deliberate policy
choice. Use adjacent password totp when both proofs are required.
Successful tokens receive authoritative AMR evidence: password → pwd, TOTP →
otp, WebAuthn/U2F → hwk. Transform actions cannot fabricate completed
methods. Direct Basic and API-key login, portal refresh, OP sessions and OIDC
refresh reevaluate current policy and cannot bypass unmet requirements.
Request-context matchers (such as issuer/address) evaluate current request
context, including backchannel requests; use stable realm/identity selectors
unless that context dependence is intentional.
caddyfile_authn_transform_test.go covers shared parsing, custom claims,
canonical JSON, conditional selection, errors and runtime replacement.
testcase_authenticate_with_challenges supplies adapt/resolution fixtures.
TestCaddyAuthenticationChallengesE2E exercises actual verified Caddy TLS:
root/nested mounts, HTML/JSON and native clients, TOTP/U2F-only selection,
password fallback, AMR authorization, refresh/OIDC, Basic/API-key rejection, no eligible
rule, stored policies and profile edits. WebAuthn uses signed assertions and
rejects wrong origin and signature. Keep these boundaries when extending syntax.
TestPortalTransformGithubMatchers and TestPortalTransformGithubRejects
check provider syntax, quote boundaries, persisted matchers, ordinary ACL
compatibility, shared errors and reserved claims. The
testcase_authenticate_with_github_transforms adaptation fixture contains all
four forms. TestCaddyGithubTransformsE2E adapts and provisions Caddy, follows
OAuth code exchange over verified local TLS, independently verifies
the signed portal token and checks a protected route. It covers exact/regex
matches and misses, AND semantics, renamed and large IDs, missing/malformed
claims, filtered/empty/denied organization lookup and a different driver using
a realm named github. Fixed provider URLs terminate at a bounded loopback
CONNECT proxy in an isolated subprocess; no production endpoint or trust
overrides are added.
a48553d
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.