By far the oidc
and oauth2
plugins are the most complex of eas
. Several
factors are involved in this but this also makes the configuration more
intricate. This document is an effort to provide as much info as possible to
provide more detail into the various parts.
A key thing to understand is that the oidc
and oauth2
plugins create a
stateful session with eas
. This is why redis is generally required if using
either plugin as sessions are stored in redis.
All of this is managed via a signed cookie which is stored by the user-agent
(browser) for a configurable period of time (see cookie_expiry
option). The
various other cookie options are configurable as well
(name
, domain
, path
, etc) on a per-config_token
basis. With careful
configuration you can enable SSO scenarios amongst other options.
Generally, eas
ensures the presense of the appropriately named cookie and
ensures it was signed by eas
. This can however result in an insecure setup as
several services secured by the same eas
deployment all share the same cookie
signing key. To overcome this threat, eas
sessions are bound to a particular
audience. You can configure this audience by setting the aud
value in the/a
config_token
to a specific value. Any services sharing that same aud
will
consider sessions valid. If the aud
value is omitted in the config_token
then eas
will generate a unique hash of the complete config_token
data and
use that as the aud
value. This is a 'secure by default' approach but allows
for powerful control by operators to meet their unique/individual needs.
If you intend to secure several services with the same config_token
using
oidc
or oauth2
(SSO), then there are several recommendations:
- explicitly set the
aud
value in theconfig_token
- explicitly set the
redirect_uri
to point tohttps://eas.example.com/oauth/callback
- explicitly set the
cookie.domain
value to the generic domainexample.com
- carefully consider the
cookie.name
andcookie.path
values as appropriate - ensure unique combinations of
aud
,cookie.domain
, andcookie.name
for disperse services which should NOT share sessions
As an example, consider a scenario with 2 different config_token
s (perhaps
different oidc
providers or perhaps requiring different assertions
) which
will be used across 10 different services (5 each) each with the same TLD. The
configuration outlined below will ensure each config_token
is only used when
appropriate and will ensure different services are not conflicting with each
other for the session data. It also ensures the services are properly secured
according to operator desires.
The applicable config_token
options would be:
aud
set tocompany-basic
cookie.domain
set toexample.com
cookie.name
set to_oeas_oauth_session_basic
redirect_uri
set tohttps://eas.example.com/oauth/callback
aud
set toadmins
cookie.domain
set toexample.com
cookie.name
set to_oeas_oauth_session_admins
redirect_uri
set tohttps://eas.example.com/oauth/callback
If you wish to destroy a session with eas
you may send the user to any link
that would invoke eas
from an authentication perspective (ie: do NOT send the
user agent to eas
directly) including the following GET
parameters:
__eas_oauth_handler__=logout
redirect_uri=<url encoded redirect location>
As an example, if you are authenticating the domain https://svc.example.com
with eas
you could direct the browser to
https://svc.example.com?__eas_oauth_handler__=logout&redirect_uri=https%3A%2F%2Fgithub.com
Optionally, when invoking the logout URL eas
can revoke the tokens with the
provider. This is controlled by the features.logout.revoke_tokens_on_logout
setting in the config_token
plugin config. It implements
https://tools.ietf.org/html/rfc7009 and is available for both oauth2
and
oidc
.
- https://tools.ietf.org/html/rfc7009
- https://identityserver4.readthedocs.io/en/latest/endpoints/endsession.html
When features.logout.end_provider_session.enabled
is true
then upon logout
eas
will redirect the user to end the session with the provider.
For the complete process to work you should register https://eas.example.com/oauth/end-session-redirect
as a valid redirect_uri
with your provider. The endpoint should be directly
accessing the eas
service, and accessible to the user agent (ie: browser).
The redirect_uri
GET
parameter of the eas
logout page mentioned above
will still be the final destination of the brower but a couple detours are made
to ensure that happens. The series of redirects looks like this:
`eas` logout page requested ->
session is destroyed (with `eas`) and optionally tokens revoked ->
brower redirected to provider endpoint to destroy session ->
session is destroy with provider -> (note, typically this action would result in the provider invoking the backchannel logout (see below) for all relevant client_id's)
provider forward back to `features.logout.end_provider_session.post_logout_redirect_uri` (which should be `eas` directly exposed `/oauth/end-session-redirect` endpoint) ->
originally requested `redirect_uri` is parsed from encrypted `state` and finally browser is redirected to final destination
backchannel
logout is a feature where logouts that occur with the provider
are propogated down immediately any/all oidc client_id
's that have been
configured to support the feature implementing a form of single logout.
Typically, providers only allow you register a single backchannel
URI. As
such (this may seem obvious) proper utilization of the feature is only
available when a cliend_id
with your provider maps to a single deployment of
eas
. If you share a client_id
that spans multiple eas
deployments you
cannot effectively leverage the backchannel
logout feature.
Due to the unique approach of eas
and the various configuration possibilities
when backchannel
logouts occur the tokens
are NOT revoked with the
provider immediately but will be revoked the first time a user tries to access
a relevant eas
-secured service. Note that the eas
session is effectively
revoked immediately but if the tokens are being used downstream of eas
to
access other services, then the tokens will remain unrevoked until the user
accesses an eas
backed service which will finalize the backchannel
logout
process revoking the tokens
with the provider.
If your store (ie: redis) does not persist to disk then backchannel
support
not be effective in the case of catostrophic failure/restart of the store.
For backchannel
logout to work, the eas
must be directly exposed to the
provider. The /oauth/backchannel-logout
endpoint of eas
will be invoked by
the provider (ie: https://eas.example.com/oauth/backchannel-logout?...
).
-
Create a
backchannel_config_token
in a similar fashion as aconfig_token
is created. Seebin/generate-backchannel-config-token.js
. -
Use the
backchannel_config_token
generated above to register thebackchannel
logout URL with your provider. ie:https://eas.example.com/oauth/backchannel-logout?backchannel_config_token=<generated token>
-
In your
config_token
ensure thebackchannel
feature has been enabled. If you wish to force this value and/or set defaults you may use the env varEAS_BACKCHANNEL_LOGOUT_CONFIG
env EAS_BACKCHANNEL_LOGOUT_CONFIG syntax
default/forced values can be true/false and/or omitted
The value of `forced` **IS** the forced value, not if forcing
is enabled/disabled. If you do not wish to force a value omit
the `forced` key in your config.
Precidence is given to issuer specific keys over _fallback if present.
{
"enabled": {
"_fallback": {
"default": true,
"forced": true
},
"issuers": {
"some issuer": {
"default": true,
"forced": true
},
"https://<keycloak>/auth/realms/<my realm>": {
"default": true,
"forced": true
}
...
}
},
"ttl": {
...same syntax as 'enabled' key, value of default/forced should be # of seconds to retain logout entries in the store
}
}
# example: backchannel support enabled unconditionally
{
"enabled": {
"_fallback": {
"forced": true
}
}
}
# example: backchannel support disabled unconditionally
{
"enabled": {
"_fallback": {
"forced": false
}
}
}
# example: backchannel support enabled unconditionally for specific issuer
{
"enabled": {
"issuers": {
"myissuer": {
"forced": true
}
}
}
}
# example: backchannel support relagated to `config_token` unless the value is not explicitly set
# otherwise default to off
{
"enabled": {
"_fallback": {
"default": false
}
}
}