SDA Commons Server Auth¶
This module provides an AuthBundle
to authenticate
users based on JSON Web Tokens and an OpaBundle
to
authorize the request with help of the Open Policy Agent
.
To use the bundle, a dependency to this module has to be added:
1 |
|
Auth Bundle¶
The authentication creates a JwtPrincipal
per
request. This can be accessed from the SecurityContext
:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
To activate the authentication in an application, the bundle has to be added to the application:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
The Bundle can be configured to perform basic authorization based on whether a token is presented or not on endpoints
that are annotated with @PermitAll
with the setting .withAnnotatedAuthorization()
.
If the authorization should be handled by e.g. the OpaBundle
, the option .withExternalAuthorization()
still validates
tokens sent in the Authorization
header but also accepts requests without header to be processed and eventually
rejected in a later stage.
Configuration¶
The configuration relies on the config.yaml
of the application and the custom property where the
AuthConfig
is mapped. Usually this should be
auth
.
1 2 3 4 5 |
|
The config allows to set the leeway in seconds for validation of
exp
and
nbf
.
Multiple sources for public keys for verification of the signature can be configured. Each source may refer to a
- certificate in PEM format
- an authentication provider root URL or
- a URL providing a
JSON Web Key Set
The authentication can be disabled for use in test and development environments. Be careful to NEVER disable authentication in production.
For the authentication provider root URL and a jwks source a required issuer can be configured by the attribute requiredIssuer
.
If the attribute requiredIssuer
is set, the issuer of the token must match to the provided required issuer.
A warning will be logged if the host name of the source url does not match the host name of the required issuer.
Example config:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
The config may be filled from environment variables if the
ConfigurationSubstitutionBundle
is used:
1 2 3 4 5 |
|
In this case, the AUTH_KEYS
variable should contain a JSON array of
KeyLocation
objects:
1 2 3 4 5 6 7 8 9 10 |
|
Periodical reloading of public keys provided via JWKS¶
Public keys provided via a JSON Web Key Set (JWKS) are stored in a local key-cache. The cache updates every 5 minutes. It will also update when a JWT with unknown kid must be validated. No changes apply when the JWKS is unreachable.
To avoid acceptance of tokens signed by revoked keys, all keys not available in the JWKS are removed on update.
HTTP Client Configuration and Proxy Support¶
The client that calls the OpenID Discovery endpoint or the JWKS url, is configurable with the standard Dropwizard configuration.
Tip: There is no need to make all configuration properties available as environment variables. Seldomly used properties can always be configured using System Properties.
1 2 3 4 5 6 7 |
|
This configuration can be used to configure a proxy server if needed. Use this if all clients should use an individual proxy configuration.
In addition, the client consumes the standard proxy system properties.
Please note that a specific proxy configuration in the HttpClientConfiguration
disables the proxy system properties for the client using that configuration.
This can be helpful when all clients in an Application should use the same proxy configuration (this includes all clients that are created by the sda-commons-client-jersey
bundle).
OPA Bundle¶
Details about the authorization with Open Policy Agent are documented within the authorization concept (see Confluence). In short, Open Policy Agent acts as policy decision point and is started as sidecar to the actual service.
The OPA Bundle acts as a client to the Open Policy Agent and is hooked in as request filter handling requests after they have
been validated by the JwtAuthenticator
and before they reach the service implementation.
The OPA Bundle requires the AuthBundle
in place, so that the JWT can be verified against public keys before it is handed over to OPA.
In case the request does not contain a JWT token at all, the JWT verification in the AuthBundle
will be skipped without error and further
checks should be part of the OPA policy.
If it is still required to reject requests that do not contain a JWT token the AuthBundle
needs to be configured to perform basic authorization. This is achieved
by setting .withAnnotatedAuthorization()
on the AuhtBundle
and annotate endpoints with @PermitAll
.
The OPA bundle requests the policy decision providing the following inputs
* HTTP path as Array
* HTTP method as String
* validated JWT (if available)
* all request headers (can be disabled in the OpaBundle
builder)
Remark to HTTP request headers:
The bundle normalizes header names to lower case to simplify handling in OPA since HTTP specification defines header names as case-insensitive.
Multivalued headers are not normalized with respect to the representation as list or single string with separator char.
They are forwarded as parsed by the framework.
Security note: Please be aware while a service might only consider one value of a specific header, the OPA is able to authorize on an array of those. Consider this in your policy when you want to make sure you authorize on the same value that a service might use to evaluate the output.
These inputs can be accessed inside a policy .rego
-file in this way:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
The response consists of two parts: The overall allow
decision, and optional rules that represent constraints to limit data access
within the service. These constraints are fully service dependent and MUST be applied when querying the database or
filtering received data.
The following listing presents a sample OPA result with a positive allow decision and two constraints, the first with boolean value and second with a list of string values.
1 2 3 4 5 6 7 8 |
|
The following listing shows a corresponding model class to the example above:
1 2 3 4 5 6 7 8 9 10 |
|
The bundle creates a OpaJwtPrincipal
for each request. You can retrieve the constraint model's data from the principal by invoking OpaJwtPrincipal#getConstraintsAsEntity
.
Data from an JwtPrincipal
is
copied to the new principal if existing.
Beside the JWT, the constraints are included in this principal. The OpaJwtPrincipal
includes a
method to parse the constraints JSON string to a Java object.
The OpaJwtPrincipal
can be
injected as field using @Context
in
request scoped beans like endpoint implementations or accessed from the SecurityContext
.
1 2 3 4 5 6 7 8 9 10 11 |
|
To activate the OPA integration in an application, the bundle has to be added to the application. The Kubernetes file of the service must also be adjusted to start OPA as sidecar.
If you use the SdaPlatformBundle, there is a more convenient way to enable OPA support.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Configuration¶
The configuration relies on the config.yaml
of the application and the custom property where the
OpaConfig
is mapped. Usually this should be
opa
.
1 2 3 4 5 |
|
The config includes the connection data to the OPA sidecar and the path to the used policy endpoint.
Example config:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
The config may be filled from environment variables if the
ConfigurationSubstitutionBundle
is used:
1 2 3 4 |
|
HTTP Client Configuration and Proxy Support¶
The client that calls the Open Policy Agent is configurable with the standard Dropwizard configuration.
Tip: There is no need to make all configuration properties available as environment variables. Seldomly used properties can always be configured using System Properties.
1 2 3opa: opaClient: timeout: 500ms
This configuration can be used to configure a proxy server if needed.
Please note that this client does not consume the standard proxy system properties but needs to be configured manually! The OPA should be deployed as near to the service as possible, so we don't expect the need for universal proxy settings.
Testing¶
sda-commons-server-auth-testing
provides support for testing
applications with authentication.
Input Extensions¶
The Bundle offers the option to register extensions that send custom data to the Open Policy Agent to be accessed during policy execution.
Such extensions implement the OpaInputExtension
interface and are registered in a dedicated namespace.
The extension is called in the OpaAuthFilter
and is able to access the current RequestContext
to for example extract additional data from the request.
Overriding existing input properties (path
, jwt
, httpMethod
) is not possible.
This extension option should only be used if the normal authorization via constraints is not powerful enough. In general, a custom extension should not be necessary for most use cases.
Remark on accessing the request body in an input extension:
The extension gets the ContainerRequestContext
as input to access information about the request.
Please be aware to not access the request entity since this might break your service.
A test that shows the erroneous behavior can be found in OpaBundleBodyInputExtensionTest.java
Security note: When creating new extensions, be aware that you might access properties of a request that are not yet validated in the method interface. This is especially important if your service expects single values that might be accessible as array value to the OPA. While the service (e.g. in case of query parameters) only considers the first value, make sure to not authorize on other values in the OPA.
The following listing shows an example extension that adds a fixed boolean entry:
1 2 3 4 5 6 |
|
Register the extension during the OpaBundle
creation:
1 2 3 4 |
|
Access the additional input inside a policy .rego
-file in this way:
1 2 3 4 5 6 7 |
|