lookup Guard
lookup Guard
Defines a guard that resolves roles and attributes for an identity established by another guard.
Guards that establish identity from a credential have only what the credential asserts to offer. The lookup guard closes that gap: given an identity supplied by such a guard, it resolves roles and attributes from a configured external document.
Its contract is exactly that — given an identity, resolve roles and attributes from a configured source. Any caller-specific meaning lives in the configuration, in which endpoint is fetched and which paths are read, not in the guard.
guards:
my_lookup_guard:
type: lookup
options:
via: "${guarded['cognito0'].identity}"
endpoint: https://example.com/entitlements
interval: 300
credentials:
headers:
authorization: "Bearer token-123"
identity: application.id
roles: subscriptions
attributes:
plan_id: plan.id
tenant: application.tenantConfiguration (* required)
store
string
The name of the store used by this guard.
options*
object
The lookup specific options.
options:
via: "${guarded['cognito0'].identity}"
endpoint: https://example.com/entitlements
interval: 300
credentials:
headers:
authorization: "Bearer token-123"
identity: application.id
roles: subscriptions
attributes:
plan_id: plan.idoptions.via*
string| Pattern:^\\$\\{guarded\\['[^'${}]+'\\]\\.identity\\}$
References the guard supplying the identity, using the ${guarded['<name>'].identity} expression form.
The expression must resolve to exactly one guard. Identity strings collide across credential types trivially — nothing prevents one credential from mapping to a string that is also some peer's certificate subject — so a lookup guard fed from several sources would let whoever holds either credential assume that identity. Concatenation and multiple references are therefore rejected, making cross-type impersonation inexpressible rather than merely discouraged.
options:
via: "${guarded['cognito0'].identity}"options.endpoint*
string
URL of the JSON document to resolve identities against. The document is fetched on startup and re-fetched thereafter.
options:
endpoint: https://example.com/entitlementsThe document is an array of records. Each record is identified by the value at options.identity, which defaults to id, and the record whose identity equals the incoming identity is the one whose paths are resolved.
[
{
"id": "acme-corp",
"roles": ["consumer:completions-live", "provider:events-raw"]
}
]With the default identity and roles paths, that is the whole document — nothing further needs configuring. Records may carry any further structure, addressed by options.attributes:
[
{
"id": "acme-corp",
"roles": ["consumer:completions-live", "provider:events-raw"],
"plan": { "id": "premium" },
"application": { "tenant": "acme" }
}
]The document type is checked at fetch time. A document that is not an array is reported and the previously held document is kept, rather than being treated as empty — a shape mismatch that converged with "no record found" would fail closed silently at guarded routes and be indistinguishable from nobody being entitled to anything.
Records that are not objects, and records whose identity path resolves to nothing, are skipped: neither can ever match an incoming identity. Where two records claim the same identity the first wins, which is the ordering the array itself already implies.
options.interval
integer| Default:300
The delay, in seconds, between the end of one request to endpoint and the start of the next.
This is not a polling period. Every request carries Prefer: wait=N, and echoes If-None-Match whenever an ETag is held. An endpoint supporting both blocks until the document changes and then returns immediately, so the interval is trailing overhead; an endpoint supporting neither returns 200 immediately, and the interval is what paces the loop.
options:
interval: 60options.credentials
object
HTTP request configuration used when fetching the document.
credentials.headers
objectas map of namedstringproperties
HTTP headers sent with each request to endpoint, for example to pass an authorization token.
options:
credentials:
headers:
authorization: "Bearer token-123"options.identity
string| Default:id
Path, resolved within each record, holding the value that identifies it. The record whose value equals the incoming identity is the one used; records identified by any other path are skipped.
This names where the identity is found, not a different identity to adopt — the session identity is always the incoming identity, since that is what the record was matched on. To expose some other value from the record downstream, read it as an attribute.
options:
identity: application.idoptions.roles
string| Default:roles
Path, resolved within the matched record, to an array of role strings contributed to the authorized session. These are the roles enforced by guarded route requirements, matched exactly.
options:
roles: subscriptionsoptions.attributes
objectas map of namedstringproperties
Maps attribute names to paths resolved within the matched record. Resolved attribute values are available to downstream bindings as ${guarded['my_lookup_guard'].attributes.<name>}.
options:
attributes:
plan_id: plan.id
tenant: application.tenantMissing records
When the document holds no record for an incoming identity, the session is still authorized — the identity was established by the guard referenced by via, and this guard supplies authorization data only. The session simply holds no roles, so it fails closed at any guarded route that requires one.
This is the opposite of api-keys, where the presence of a record is the verification and its absence leaves the request unauthenticated. The two share their fetch and extraction machinery but not their meaning, which is why they remain separate guard types: whether a guard authenticates is visible in its type name rather than dependent on a field.
Pairing with an identity guard
A typical deployment names both guards on the route, so the identity guard authenticates and the lookup guard authorizes.
guards:
cognito0:
type: aws-cognito
options:
pool: arn:aws:cognito-idp:us-east-1:012345678901:userpool/us-east-1_ABC123XYZ
region: us-east-1
identity: sub
lookup0:
type: lookup
options:
via: "${guarded['cognito0'].identity}"
endpoint: https://example.com/entitlements
bindings:
north_api_server:
type: http
kind: server
routes:
- guarded:
lookup0:
- consumer:completions-live
exit: south_kafka_proxy
