This an implementation of the secure token service as specified in the token exchange working draft https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-08.
This module does not implement oAuth2 authentication flows. The module focuses on the oAuth2 token production and management process. Most oAuth2 Server implement both authentication and token management.
Authentication is the process of identifying and legitimating the subject of authorization. Means the subject on behalf of which the oAuth token is produced. An authentication server does following:
- Authenticate the user
- Ask the token manager to produce the corresponding token
- Ask the token manager to invalidate the token
Token management solely focuses on managing oAuth tokens and exchanging them like specified in the token exchange working draft (https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-08)
Overview example to show the usage of the STS components.
You can easily use this libraries in your spring boot app via maven dependency:
<dependency>
<groupId>de.adorsys.sts</groupId>
<artifactId>sts-spring</artifactId>
<version>${version}</version>
</dependency>
You can easily use features by adding following annotations to your spring @Configuration
class:
Annotation | Description |
---|---|
@EnablePOP |
Enables the Proof-Of-Possession endpoint |
@EnableResourceServerInitialization |
Enables the initialization of the resource server configuration read from the spring properties |
@EnableEncryption |
Enables the encryption service bean |
@EnableDecryption |
Enables the decryption service bean |
@EnableSecretDecryption |
Enables the secret claim decryption service bean |
@EnableKeyRotation |
Enables the key-rotation for the key-management |
@EnableServerInfo |
Enables the server-info endpoint |
@EnableTokenAuthentication |
Enables token-authentication-service bean |
@EnableSecurityContextSecretProviding |
Enables the security-context secret provider bean |
@EnableJacksonObjectMapping |
Enables jackson object mapper SPI bean |
@EnableSecretServer |
Enables the secret-server functionality in your application |
@EnableSecretServerClient |
Enables the secret-server client bean |
Provides the public keys for encryption and signature check via the /pop
endpoint.
Depends on:
- Key-Management
Resource servers are used for encryption to manage the jwks-endpoints.
You can configure the array of resources servers in your application.yml
:
sts:
resource-server-management:
resource-retriever:
http-connect-timeout: <http connect timeout for JWK set retrieval in milliseconds, default: 250>
http-read-timeout: <http read timeout for JWK set retrieval in milliseconds, default: 250>
http-size-limit: <http entity size limit for JWK set retrieval in bytes, default: 50 * 1024>
# cache settings
cache:
enabled: <(boolean, optional, default: false) defines if the secret-server client uses a internal cache for the secrets>
maximum-size: <(integer, optional, default: 1000) defines the maximum cache size>
expire-after-access: <(integer, optional, default: 10) defines the expiration time in minutes>
resource-servers:
- audience: <(text) the name of your resource server / the audience key>
jwks-url: <(text, url) the jwks-url of the resource-server, like "http://localhost:8888/pop">
You have to decide:
- Which kind of
ResourceServerRepository
you will provide as Bean.
Provides the EncryptionService
which can be used to encrypt sensitive data. The resource servers are used to get the public keys for encryption from their /pop
endpoint.
The encrypted ciphertext is created by Json Web Encryption.
Depends on:
- Resource-Server-Configuration
Provides the DecryptionService
which can be used to decrypt JWE-encrypted ciphertexts. The decryption-key has to be stored in local key-management, otherwise the decryption will fail.
Depends on:
- Key-Management
Usage of annotation @EnableSecretDecryption
provides the SecretDecryptionService
instance which is used to get the decrypted secret.
The encrypted secret is provided by a bean implementing the SecretProvider
interface.
Provided by the @EnableSecurityContextSecretProviding
annotation a bean instance of SecurityContextSecretProvider
class is provided.
This implementation reads the secret claims from the JWT token (stored in spring's security context).
The SecurityContextSecretProvider
needs the following configuration to work properly:
sts:
secret-claim-property-key: <the claim property key used to read the right claim from the claims-set, mandatory>
audience-name: <the application's audience name. Is used to read the right entry from the secrets map, mandatory)>
Manages key-pairs for encryption/decryption and key-pairs to create or check signatures. Additionally secret-keys will be managed.
You have to decide:
- Which kind of
KeyStoreRepository
you will provide as Bean.
You may enable the key-rotation feature by adding the @EnableKeyRotation
annotation to your spring boot @Configuration
class. Additionally you have to configure following properties:
sts:
keymanagement:
rotation:
check-interval: <(long) the time interval in milliseconds the key-rotation will check the keys, default: 60000>
enc-key-pairs:
min-keys: <(integer) minimal count of stored encryption key-pairs, default: 5>
enabled: <(boolean) defines if the key-rotation is enabled for encryption key-pairs, default: false>
sign-key-pairs:
min-keys: <(integer) minimal count of stored signature key-pairs, default: 5>
enabled: <(boolean) defines if the key-rotation is enabled for signature key-pairs, default: false>
secret-keys:
min-keys: <(integer) minimal count of stored secret keys, default: 5>
enabled: <(boolean) defines if the key-rotation is enabled for secret-keys, default: false>
You have to configure the properties of the key-generation in your application.yml
:
sts:
keymanagement:
keystore:
password: <(text) the key-store encryption password>
type: <(text) the key-store type, like "UBER">
name: <(text) the key-store name>
alias-prefix: <(text) the prefix of your generated key-aliases in this key-store>
keys:
enc-key-pairs:
initial-count: <(integer) initial count of generated encryption key-pairs>
algo: <(text) the key-pair algorithm, like "RSA">
sig-algo: <(text) the key-pair signature algorithm, like "SHA256withRSA">
size: <(integer) the key size, like 2048, 4096, ...>
name: <(text) the string-representation of your key-pair>
validity-interval: <(long) the interval in milliseconds the keys can be used for encryption>
legacy-interval: <(long) the interval in milliseconds the keys can be used for decryption>
sign-key-pairs:
initial-count: <(integer) initial count of generated signature key-pairs>
algo: <(text) the key-pair algorithm, like "RSA">
sig-algo: <(text) the key-pair signature algorithm, like "SHA256withRSA">
size: <(integer) the key size, like 2048, 4096, ...>
name: <(text) the string-representation of your key-pair>
validity-interval: <(long) the interval in milliseconds the keys can be used for signature creation>
legacy-interval: <(long) the interval in milliseconds the keys can be used for signature check>
secret-keys:
initial-count: <(integer) initial count of generated secret-keys>
algo: <(text) the key algorithm, like "AES">
size: <(integer) the key size, like 128, 256, ...>
validity-interval: <(long) the interval in milliseconds the keys can be used for encryption>
legacy-interval: <(long) the interval in milliseconds the keys can be used for decryption>
Provides the TokenAuthenticationService
Bean which extracts the org.springframework.security.core.Authentication
from the Bearer token. The token has to be valid, otherwise this operation will return null
.
The validation of the token will also be handled in this operation. Only authentication-servers provided by the auth-servers configuration will be accepted.
It's recommended to use this service in a request-filter (like the JWTAuthenticationFilter
in this project).
Depends on:
- Auth-Servers configuration
You have to configure the array of authservers in your application.yml
, otherwise no Bearer-token will be accepted:
sts:
authservers:
- name: <(text) custom name of your identity provider>
iss-url: <(text, url) the issuer-url of your identity-provider's token, like "https://your-idp-hostname/auth/realms/your-realm">
jwks-url: <(text, url) the jwks-endpoint url of your identity provider, like "https://your-idp-hostname/auth/realms/your-realm/protocol/openid-connect/certs">
refresh-interval-seconds: <(integer) --- unused >
Provides server and client functionality to store secrets in a separate database. Only registered clients are able to get the secrets via a token-exchange endpoint. The delivered token has to be valid and the client has to be known as a resource server, its pop-endpoint have to be accessible for the secret-server.
You can provide the secret-server functionality while using the @EnableSecretServer
annotation in your spring configuration.
This annotation enables the token-exchange endpoint with the needed services in the background. It needs a
SecretRepository
instance which is used to persist the secrets. For example you can store your secrets in a RDBMS
like postgres or mysql if you use the additional annotation @EnableJpaPersistence
in your configuration.
Alternatively you may deploy a ready-to-run secret-server provided by the sts-secret-server submodule and which
you can run via docker: the image name is adorsys/sts-secret-server
and can be found on
docker-hub.
sts:
secret-server:
secret-length: <(integer, optional) secret length in bits (not the length of the base64 encoded string!) default: 256>
endpoint: <(url as string, optional) the endpoint path of the secret-server's token-exchange endpoint. default: /secret-server/token-exchange >
encryption:
enabled: <(boolean, optional) defines if the secret encryption is enabled. default: false >
algorithm: <(string, optional) defines the encryption algorithm. default: A256GCMKW >
encryption-method: <(string, optional) defines the encryption method. default: A256GCM >
key: <(string, optional) defines the encryption key in json format. default: none >
# You need to configure the same IDP instance(s) and its pop-entpoint(s) as authserver(s) as the clients are using to authenticate.
# Here is an example:
authservers:
- name: "local keycloak"
iss-url : "http://localhost:8080/auth/realms/moped"
jwks-url: "http://localhost:8080/auth/realms/moped/protocol/openid-connect/certs"
# You need to configure the clients as resource-servers with its pop-endpoints:
# Here is an example:
resource-server-management:
resource-servers:
- audience: "moped-client"
jwks-url: "http://my-moped-application:8080/pop"
For further information consider the standalone secret-server documentation.
On client-side you can communicate with a secret-server via the SecretServerClient
bean. The interface is quite
simple: there is a String getSecret(String token)
method which delivers the decrypted and BASE64 encoded secret for
the specified. Quite easier is the usage of the SecretServerClientSecretProvider
bean. It provides the secret without
the need to pass the token as parameter. Both beans are injectable after the usage of @EnableSecretServerClient
annotation in your spring configuration.
sts:
secret-server-client:
audience: <(string, mandatory, no default) The audience name of your application which is used to communicate with the secret-server>
secret-server-uri: <(url as string, mandatory, no default) The url of the secret-server's token-exchange endpoint (Example: http://your-secret-server:8080/token/token-exchange)>
# cache settings:
cache:
enabled: <(boolean, optional, default: false) defines if the secret-server client uses a internal cache for the secrets>
maximum-size: <(integer, optional, default: 1000) defines the maximum cache size>
expire-after-access: <(integer, optional, default: 10) defines the expiration time in minutes>
You can find a standalone, fully-configurable secret-server in submodule sts-secret-server
. Please consider
the standalone secret-server documentation
for further information.
docker-compose --file build.docker-compose.yml down --remove-orphans && docker-compose --file build.docker-compose.yml up --build
docker-compose --file postgres.docker-compose.yml down --remove-orphans && docker-compose --file postgres.docker-compose.yml up --build
docker-compose --file mysql8.docker-compose.yml down --remove-orphans && docker-compose --file mysql8.docker-compose.yml up --build
docker-compose --file mysql5.docker-compose.yml down --remove-orphans && docker-compose --file mysql5.docker-compose.yml up --build
docker-compose --file mongo.docker-compose.yml down --remove-orphans && docker-compose --file mongo.docker-compose.yml up --build
docker-compose --file hub.docker-compose.yml down --remove-orphans && docker-compose --file hub.docker-compose.yml pull && docker-compose --file hub.docker-compose.yml up
Container | URL |
---|---|
keycloak | http://localhost:8080 |
STS | http://localhost:8888 |
Service Component | http://localhost:8887 |
Angular client | http://localhost:8090 |