This library serves as slim client for some XSUAA /oauth/token
token endpoints as specified here.
Furthermore it introduces a new API to support the following token flows:
- User Token Flow.
The idea behind a User Token exchange is to separate service-specific access scopes into separate tokens. For example, if Service A has scopes specific to its functionality and Service B has other scopes, the intention is that there is no single Jwt token that contains all of these scopes. As of version2.5.1
thegrant_type
=urn:ietf:params:oauth:grant-type:jwt-bearer
is used (RFC 7523). - Client Credentials Flow.
The Client Credentials (RFC 6749, section 4.4) is used by clients to obtain an access token outside of the context of a user. It is used for non interactive applications (a CLI, a batch job, or for service-2-service communication) where the token is issued to the application itself, instead of an end user for accessing resources without principal propagation. - Refresh Token Flow.
A Refresh Token (RFC 6749, section 1.5) flow allows you to obtain a new access token in case the current one becomes invalid or expires. - Password Token Flow.
The Resource owner password credentials (i.e., username and password) can be used directly as an authorization grant to obtain an access token (RFC 6749, section 1.3.3). The credentials should only be used when there is a high degree of trust between the resource owner and the client.
Note: The Authorization Code Grant Flow involves the browser and is therefore triggered by an API gateway (e.g. Application Router). The other flows, however, may need to be triggered programmatically, e.g. to exchange one token for another or refresh a token, if it is about to expire. When you create a XSUAA service instance a OAuth client gets created and you receive the client credentials (client id and secret) when you bind your application with the XSUAA service instance. Having that in place you are ready to use the token flows in your Java application.
<dependency>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>token-client</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
Instantiate XsuaaTokenFlows
with the DefaultOAuth2TokenService
which
makes use of Apache HttpClient:
XsuaaTokenFlows tokenFlows = new XsuaaTokenFlows(
new DefaultOAuth2TokenService(),
new XsuaaDefaultEndpoints(<uaa_base_url>),
new ClientCredentials(<client_id>, <client_secret>));
The DefaultOAuth2TokenService
can also be instantiated with a custom CloseableHttpClient
.
The
<uaa_base_url>
,<client_id>
and<client_secret>
are placeholders for the information you get from the XSUAA service binding.
By default, the DefaultOAuth2TokenService
caches tokens internally. The Cache can be configured by providing an
CacheConfiguration
object as constructor parameter. The cache can be disabled by using the
CacheConfiguration.CACHE_DISABLED
configuration.
❗ In order to leverage the cache it makes sense to have only one reference to the OAuth2TokenService.java
implementation or to the XsuaaTokenFlows
.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>token-client</artifactId>
<version>2.8.4</version>
</dependency>
With Spring-Web available XsuaaTokenFlows
can be instantiated with a RestTemplate
of your choice like that:
XsuaaTokenFlows tokenFlows = new XsuaaTokenFlows(
new XsuaaOAuth2TokenService(new RestTemplate()),
new XsuaaDefaultEndpoints(<uaa_base_url>),
new ClientCredentials(<client_id>, <client_secret>));
The
<uaa_base_url>
,<client_id>
and<client_secret>
are placeholders for the information you get from the XSUAA service binding. In case you leverage the spring-xsuaa library you can also useXsuaaServiceConfiguration
class.
By default, the XsuaaOAuth2TokenService
caches tokens internally. The Cache can be configured by providing an
CacheConfiguration
object as constructor parameter. The cache can be disabled by using the
CacheConfiguration.CACHE_DISABLED
configuration.
❗ In order to leverage the cache it makes sense to have only one reference to the OAuth2TokenService.java
implementation or to the XsuaaTokenFlows
.
In context of a Spring Boot application you may like to leverage auto-configuration:
<dependency>
<groupId>com.sap.cloud.security.xsuaa</groupId>
<artifactId>xsuaa-spring-boot-starter</artifactId>
<version>2.8.4</version>
</dependency>
As auto-configuration requires Spring Boot specific dependencies, it is enabled when using xsuaa-spring-boot-starter
Spring Boot Starter.
Then, xsuaa integration libraries auto-configures beans, that are required to initialize the Token Flows API.
Auto-configuration class | Description |
---|---|
XsuaaAutoConfiguration | Adds xsuaa.* properties to Spring's Environment. The properties are by default parsed from VCAP_SERVICES system environment variables and can be overwritten by properties such as xsuaa.url e.g. for testing purposes. Furthermore it exposes a XsuaaServiceConfiguration bean that can be used to access xsuaa service information. Alternatively you can access them with @Value annotation e.g. @Value("${xsuaa.url:}") String xsuaaBaseUrl . As of version 1.7.0 it creates a default RestTemplate bean that serves as Rest client that is used inside a default OAuth2TokenService to perform HTTP requests to the XSUAA server. It is recommended to overwrite this default and configuring it with the HTTP client of your choice. |
XsuaaTokenFlowAutoConfiguration | Configures a XsuaaTokenFlows bean for a given RestOperations and XsuaaServiceConfiguration bean to fetch the XSUAA service binding information. |
You can gradually replace auto-configurations as explained here.
To consume the XsuaaTokenFlows
class, you simply need to @Autowire
it like this:
@Autowired
private XsuaaTokenFlows xsuaaTokenFlows;
The XsuaaTokenFlows
provides a builder-pattern API that allows applications to easily create and execute each flow, guiding developers to only set properties that are relevant for the respective token flow.
Obtain a client credentials token:
OAuth2TokenResponse clientCredentialsToken = tokenFlows.clientCredentialsTokenFlow()
.subdomain(jwtToken.getSubdomain()) // this is optional
.disableCache(true) // optionally disables token cache for request
.execute();
In case you have a refresh token and want to obtain an access token:
OAuth2TokenResponse refreshToken = tokenFlows.refreshTokenFlow()
.refreshToken(<refresh_token>)
.subdomain(jwtToken.getSubdomain()) // this is optional
.disableCache(true) // optionally disables token cache for request
.execute();
In order to exchange a user token for another user access token:
XsuaaToken jwtToken = SpringSecurityContext.getToken();
OAuth2TokenResponse userToken = tokenFlows.userTokenFlow()
.token(jwtToken)
.subdomain(jwtToken.getSubdomain())
.disableCache(true) // optionally disables token cache for request
.attributes(additionalAttributes) // this is optional
.execute();
In order to obtain an access token for a user:
OAuth2TokenResponse clientCredentialsToken = tokenFlows.passwordTokenFlow()
.subdomain(jwtToken.getSubdomain())
.username(<your username>)
.password(<your password>)
.disableCache(true) // optionally disables token cache for request
.execute();
Make sure to read the API documentation of the XsuaaTokenFlows
API, to understand what the individual token flows' parameters are for.
For troubleshooting problems with the token service, you can set the logging level for the
com.sap.cloud.security
package to DEBUG
. Have a look at the
Logging section for more information on logging.
If you need more detailed network data in your logs, you can also enable debugging for your HTTP client.
Note that this might leak encoded tokens into your logs. Use with care!
For java applications using HttpClient
, see the
logging documentation.
For spring applications using rest template, you can set
org.springframework.web.client.RestTemplate
to log level DEBUG
.
-
This module requires the JSON-Java library. If you have classpath related issues involving JSON you should take a look at the Troubleshooting JSON class path issues document.
-
{\"error\":\"unauthorized\",\"error_description\":\"Unable to map issuer, [http://subdomain.localhost:8080/uaa/oauth/token] , to a single registered provider\"}
Token exchange is only supported within the same identity zone/tenant. That means, that you have to call the/oauth/token
endpoint of the same subdomain, that was used for the original token. This can be achieved by configuring the user token flow the following way:
tokenFlows.userTokenFlow().token(jwtToken).subdomain(jwtToken.getSubdomain());`