Secure Server-to-Server Communication with Spring Boot and OAuth 2.0

Secure Server-to-Server Communication with Spring Boot and OAuth 2.0

The OAuth 2.0 client credentials grant was created to help solve for the problems that HTTP Basic Auth had. Learn how it can allow two machines to communicate securely.

Víctor Cardona

Nice article, it works smoothly, thanks for sharing.

Виталий

Where does Spring store an accessToken received from authorization server?

Brian Demers

For a Resource Server, it isn’t stored, it is attached to each request (in the auth header).

On client side in this example, it isn’t stored (because it is a simple command line client). In the case of a web app acting as a client it would be stored in your session (by default) https://projects.spring.io/…

Виталий

How is it possible to provide resource server id when setting up client_credentials grant? Without it I get

org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException: Invalid token does not contain resource id (oauth2-resource)

Amadou Ada DIENE

Thanks for this brilliant and useful article

Richard Collette

That example assumes that a server is authenticating on behalf of the user can caching the tokens in session. But there are cases where you merely want serverA’s identity (or perhaps more accurately application A’s identity) to access server B. An example of managing tokens and refreshing them would be helpful. Okta has a form of tokenManager implemented in JavaScript. Pointing to some similar implementation for Spring would be helpful.

alltej

Thanks! It works. Do you have example that uses the signed JWT?

Brian Demers

Hey @disqus_IexpkkyGVf:disqus!

In the example above I’m using Okta. In our case the access tokens actually are signed JWTs. You can take a look at this resource server example: https://github.com/okta/sam… if you are looking for client side validation.

Brian Demers

Sorry I missed this, sounds like your configuration might be the issue. If you are still interested let me know what your application.yml looks like.

alltej

That example uses implicit grant/flow. The solution I am trying to find is using Okta as the authentication server. Implicit flow is fine in this case. After that, I need to get the authorization for this user via a hosted/custom authorization server(not third-party). Then generate a signed JWT which will be attached to each request by the front-end client.

Brian Demers

I’m not sure i’m following 100%, It sounds like you want to authorize via Okta, and then recreate and manage another token within your application? I don’t have an example for this, but I’m assuming there are a few different ways to do this with Spring Security.

alltej

@disqus_u7ZhPHjjDC:disqus Thanks for your response. Kinda using Okta for authentication and a custom API (internal/home-grown) for authorization.

Al Pacifico

I am getting the following trying to execute the command-line client (apologies for lengthy error log):
2018-10-23 04:32:01.949 INFO 16299 — [ main] c.e.c.ClientApplication : Started ClientApplication in 2.654 seconds (JVM running for 7.893)
2018-10-23 04:32:02.009 INFO 16299 — [ main] ConditionEvaluationReportLoggingListener :

Error starting ApplicationContext. To display the conditions report re-run your application with ‘debug’ enabled.
2018-10-23 04:32:02.020 ERROR 16299 — [ main] o.s.boot.SpringApplication : Application run failed

java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:795) [spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:776) [spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
at org.springframework.boot.Sp…(SpringApplication.java:315) [spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
at org.springframework.boot.Sp…(SpringApplication.java:1242) [spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
at org.springframework.boot.Sp…(SpringApplication.java:1230) [spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
at com.example.credsexampleclient.ClientApplication.main(ClientApplication.java:24) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImp…:43) ~[na:1.8.0_131]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:497) [spring-boot-maven-plugin-2.0.6.RELEASE.jar:2.0.6.RELEASE]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]
Caused by: java.lang.NullPointerException: null
at java.lang.StringBuilder.<init>(StringBuilder.java:112) ~[na:1.8.0_131]
at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.getAccessTokenUri(OAuth2AccessTokenSupport.java:162) ~[spring-security-oauth2-2.2.1.RELEASE.jar:na]
at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:137) ~[spring-security-oauth2-2.2.1.RELEASE.jar:na]
at org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider.obtainAccessToken(ClientCredentialsAccessToke…:44) ~[spring-security-oauth2-2.2.1.RELEASE.jar:na]
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:148) ~[spring-security-oauth2-2.2.1.RELEASE.jar:na]
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:121) ~[spring-security-oauth2-2.2.1.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) ~[spring-security-oauth2-2.2.1.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) ~[spring-security-oauth2-2.2.1.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105) ~[spring-security-oauth2-2.2.1.RELEASE.jar:na]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:683) ~[spring-web-5.0.10.RELEASE.jar:5.0.10.RELEASE]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128) ~[spring-security-oauth2-2.2.1.RELEASE.jar:na]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:644) ~[spring-web-5.0.10.RELEASE.jar:5.0.10.RELEASE]
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:296) ~[spring-web-5.0.10.RELEASE.jar:5.0.10.RELEASE]
at com.example.credsexamplecli…(ClientApplication.java:40) [classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:792) [spring-boot-2.0.6.RELEASE.jar:2.0.6.RELEASE]
… 11 common frames omitted

2018-10-23 04:32:02.022 INFO 16299 — [ main] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@2ad83edd: startup date [Tue Oct 23 04:32:00 PDT 2018]; root of context hierarchy
2018-10-23 04:32:02.024 INFO 16299 — [ main] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImp…:43)
at java.lang.reflect.Method.invoke (Method.java:498)
at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run (AbstractRunMojo.java:497)
at java.lang.Thread.run (Thread.java:748)
Caused by: java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.callRunner (SpringApplication.java:795)
at org.springframework.boot.SpringApplication.callRunners (SpringApplication.java:776)
at org.springframework.boot.Sp… (SpringApplication.java:315)
at org.springframework.boot.Sp… (SpringApplication.java:1242)
at org.springframework.boot.Sp… (SpringApplication.java:1230)
at com.example.credsexampleclient.ClientApplication.main (ClientApplication.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImp…:43)
at java.lang.reflect.Method.invoke (Method.java:498)
at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run (AbstractRunMojo.java:497)
at java.lang.Thread.run (Thread.java:748)
Caused by: java.lang.NullPointerException
at java.lang.StringBuilder.<init> (StringBuilder.java:112)
at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.getAccessTokenUri (OAuth2AccessTokenSupport.java:162)
at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken (OAuth2AccessTokenSupport.java:137)
at org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider.obtainAccessToken (ClientCredentialsAccessToke…:44)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal (AccessTokenProviderChain.java:148)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken (AccessTokenProviderChain.java:121)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken (OAuth2RestTemplate.java:221)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken (OAuth2RestTemplate.java:173)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest (OAuth2RestTemplate.java:105)
at org.springframework.web.client.RestTemplate.doExecute (RestTemplate.java:683)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute (OAuth2RestTemplate.java:128)
at org.springframework.web.client.RestTemplate.execute (RestTemplate.java:644)
at org.springframework.web.client.RestTemplate.getForObject (RestTemplate.java:296)
at com.example.credsexamplecli… (ClientApplication.java:40)
at org.springframework.boot.SpringApplication.callRunner (SpringApplication.java:792)
at org.springframework.boot.SpringApplication.callRunners (SpringApplication.java:776)
at org.springframework.boot.Sp… (SpringApplication.java:315)
at org.springframework.boot.Sp… (SpringApplication.java:1242)
at org.springframework.boot.Sp… (SpringApplication.java:1230)
at com.example.credsexampleclient.ClientApplication.main (ClientApplication.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImp…:43)
at java.lang.reflect.Method.invoke (Method.java:498)
at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run (AbstractRunMojo.java:497)
at java.lang.Thread.run (Thread.java:748)

Fact Simbainashe Musingarimi

Nice work. I was wondering if its possible to make an Authorization Server a Oauth2 Client as well? How can I authenticate the Authorization server when it tries to access other Resource Server applications

Brian Demers

I’m not sure I’m following. Can you describe your use case a bit more?

Fact Simbainashe Musingarimi

Thank you for your assistance. I have an Authorization Server Application and a Notification Service Application (a protected resource). The Authorization Server Application is the one that handles user registration. After registration I want to send a confirmation email by calling the the Notification Service.

Brian Demers

Sounds like a similar use case as an API gateway, were one service sits in front of others and handles the authorization. You could have forward along existing user’s access token, or you could use a client credentials grant between the two services.

Pallavi Darbhamulla

I made a slight modification to your client app where the call is made from a controller within the client app. This however redirects me to a login page and I am not sure what username/password to enter

Brian Demers

How are you injecting the client/RestTemplate? Maybe you are using one that is associated with the user (from the http request)