View Source AppIdentity.Plug (AppIdentity for Elixir v1.3.2)
A Plug that verifies App Identity proofs provided via one or more HTTP headers.
When multiple proof values are provided in the request, all must be
successfully verified. If any of the proof values cannot be verified,
request processing halts with 403 Forbidden. Should no proof headers be
included, the request is considered invalid.
All of the above behaviours can be modified through configuration.
Checking Results
The results of AppIdentity.Plug are stored in Plug.Conn private storage as
a map under the :app_identity key (this can be changed through the name
option), keyed by the header group name. When using the headers option,
each header is its own group. The header_groups option explicitly defines
headers that will be treated as belonging to the same group.
Header results only appear in the result map if they are present in the
request. If AppIdentity.Plug is configured for app1 and app2 headers,
but there are only values in app1, the resulting output will not include
app2.
Results Partitioning: Header Groups or Multiple Configurations?
AppIdentity.Plug provides two main ways to partition processed results:
name or header groups (either automatic grouping via headers or explicit
grouping via header_groups).
Most applications that require result partitioning will use header groups, because there's only one pool of applications defined. In this case, use the following configuration as a guide.
plug AppIdentity.Plug, finder: &MyApp.Application.get/1,
on_failure: :continue,
header_groups: %{
"application" => ["application-identity"],
"service" => ["service-identity"]
}Later in request processsing, a controller or a route-specific Phoenix
pipeline could call a require_application function which pulls from
conn.private[:app_identity] with the appropriate header group name for
verification.
If there are separate pools of applications defined, or there is a need to
have different on_failure conditions, then configure two
AppIdentity.Plugs with different names . The following example
configuration would allow application-identity headers to fail without
halting (even if omitted), but a missing or incorrect service-identity
header would cause failures immediately.
plug AppIdentity.Plug, finder: &MyApp.Application.get/1,
on_failure: :continue,
headers: ["application-identity"]
plug AppIdentity.Plug, name: :service_app_identity,
finder: {MyApp.ServiceApplication, :get},
header: ["service-identity"]Multiple Plugs Warning
If multiple AppIdentity.Plug configurations are used, different
namevalues must be specified or the later plug will overwrite the results from the earlier plug.
Configuration
AppIdentity.Plug requires configuration for app discovery and identity headers and offers further configuration. Static configuration is strongly recommended.
App Discovery
So that AppIdentity.Plug can find apps used in identity validation, at least
one of apps or finder must be supplied. If both are present, the
apps configuration is consulted before calling the finder function.
apps: A list ofAppIdentity.App.t/0orAppIdentity.App.input/0values to be used for proof validation. Duplicate apps will be ignored.plug AppIdentity.Plug, apps: [app1, app2], ...finder: A callback function conforming toAppIdentity.App.finder/0that loads anAppIdentity.App.input/0from an external source given a parsed proof. This may also be specified as a{module, function}tuple.plug AppIdentity.Plug, finder: &ApplicationModel.get/1 plug AppIdentity.Plug, finder: {ApplicationModel, :get}AppIdentity.Plug does not cache the results of the
finderfunction. Any caching should be implemented in your application.
Identity Headers
AppIdentity.Plug does not have any default headers to search for app
identity validation, requiring one of headers or header_groups to be
configured. If both are present, an exception will be raised during
configuration.
headers: A list of valid HTTP header names, which will be normalized on initialization.plug AppIdentity.Plug, headers: ["application-identity"], ...The result output uses each header name as the key for the related proof results. A configuration of
headers: ["app1", "app2"]can produce a result map like%{"app1" => [...], "app2" => [...]}.Duplicate header names will result in an error. This option must be omitted if
header_groupsis used.header_groups: A map of header group names to valid HTTP header names.When using
header_groups, there is no guaranteed order for processing groups, but the each headers within a group will be processed in the order provided.plug AppIdentity.Plug, header_groups: %{ "application" => ["application", "my-application"], "service" => ["service", "my-service"], }, ...The result output uses each header group name as the key for the related proof results from any header in that group. A configuration of
header_groups: %{"app" => ["app1", "app2"], "svc" => ["svc1"]}can produce a result map like%{"app" => [...], "svc" => [...]}.Duplicate header names across any header groups will result in an error. This option must be omitted if
headersis used.
headersorheader_groups?The correct choice between
headersandheader_groupsdepends on your application's requirements, butheaderscan be expressed asheader_groupsfor ease of changing later.That is, the following configurations are equivalent:
plug AppIdentity.Plug, headers: ["application-identity"], ... plug AppIdentity.Plug, headers_groups: %{"application-identity" => ["application-identity"]}, ...If your requirements treat each header uniquely,
headersis a useful shorthand configuration.
Callbacks
There are three configuration options that can be implemented as callbacks:
on_failure, on_success, and on_resolution.
on_failure: The behaviour of the AppIdentity.Plug when proof validation fails. If not provided, this defaults to:forbidden. When provided, it must be one of the following values::forbidden: Halt request processing and respond with a403(forbidden) status. This is the same as{:halt, :forbidden}.plug AppIdentity.Plug, on_failure: :forbidden{:halt, Plug.Conn.status()}: Halt request processing and return the specified status code. An empty body is emitted.plug AppIdentity.Plug, on_failure: {:halt, :forbidden}{:halt, Plug.Conn.status(), Plug.Conn.body()}: Halt request processing and return the specified status code. The body value is included in the response.plug AppIdentity.Plug, on_failure: {:halt, :forbidden, ["Evicted"]}:continue: Continue processing, ensuring that failure states are recorded for the application to act on at a later point. This could be used to implement a distinction between validating a proof and requiring that the proof is valid.plug AppIdentity.Plug, on_failure: :continueA 1-arity callback function (or a
{module, function}tuple) that accepts aPlug.Connand returns one of the above values.on_failurecallbacks must not modify the passedconnvalue.plug AppIdentity.Plug, on_failure: {ApplicationModel, :resolve_proof_failure} plug AppIdentity.Plug, on_failure: &ApplicationModel.resolve_proof_failure/1
on_success: A 1-arity callback function (or a{module, function}tuple) that accepts aPlug.Connwhen proof validation succeeds. Theon_successcallback may modify the passedconnvalue and must return the modifiedconnvalue.on_resolution: An 1-arity callback function (or a{module, function}tuple) that accepts aPlug.Connafter proof validation completes, regardless of sucesss or failure. If present, this will be run as the last step (afteron_failureandon_success). Becauseon_failuremay halt pipeline processing, it may be necessary to checkconn.halted. Theon_resolutioncallback may modify the passedconnvalue and must return the modifiedconnvalue.
The on_success and on_resolution callbacks are optional.
Optional Configuration
name: An atom which will be used to store theAppIdentity.Plugresults in Plug.Conn private storage. If not provided, defaults to:app_identity. Required ifAppIdentity.Plugwill be specified more than once as results are not merged.plug AppIdentity.Plug, name: :service_app, ...disallowed: A list of algorithm versions that are not allowed when processing received identity proofs. SeeAppIdentity.disallowed/0.plug AppIdentity.Plug, disallowed: [1], ...
Telemetry
When telemetry is enabled, this plug will emit [:app_identity, :plug, :start] and [:app_identity, :plug, :stop] events.