Microservices security with Oauth2


Preface

One of the most important aspects to consider when exposing a public access API consisting of many microservices is security. Spring has some interesting features and frameworks which makes configuration of our microservices security easier. In this article I’m going to show you how to use Spring Cloud and Oauth2 to provide token access security behind API gateway.

Theory

OAuth2 standard is currently used by all the major websites that allow you to access their resources through the shared API. It is an open authorization standard allowing users to share their private resources stored in one page to another page without having to go into the service of their credentials. These are basic terms related to oauth2.

  • Resource Owner – dispose of access to the resource
  • Resource Server – server that stores the owner’s resources that can be shared using special token
  • Authorization Server – manages the allocation of keys, tokens and other temporary resource access codes. It also has to ensure that access is granted to the relevant person
  • Access Token – the key that allows access to a resource
  • Authorization Grant – grants permission for access. There are different ways to confirm access: authorization code, implicit, resource owner password credentials, and client credentials

You can read more about this standard here and in this digitalocean article. The flow of this protocol has three main steps. In the begining we authorization request is sent to Resource Owner. After response from Resource Owner we send authorization grant request to Authorization Server and receive access token. Finally, we send this access token to Resource Server and if it is valid the API serves the resource to the application.

Our solution

The picture below shows architecture of our sample. We have API Gateway (Zuul) which proxies our requests to authorization server and two instances of account microservice. Authorization server is some kind of infrastructure service which provides outh2 security mechanisms. We also have discovery service (Eureka) where all of our microservices are registered.

sec-micro

Gateway

For our sample we won’t provide any security on API gateway. It just has to proxy requests from clients to authorization server and account microservices. In the Zuul’s gateway configuration visible below we set sensitiveHeaders property on empty value to enable Authorization HTTP header forward. By default Zuul cut that header while forwarding our request to the target API which is incorrect because of the basic authorization demanded by our services behind gateway.

zuul:
  routes:
    uaa:
      path: /uaa/**
      sensitiveHeaders:
      serviceId: auth-server
    account:
      path: /account/**
      sensitiveHeaders:
      serviceId: account-service

Main class inside gateway source code is very simple. It only has to enable Zuul proxy feature and discovery client for collecting services from Eureka registry.

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class GatewayServer {

	public static void main(String[] args) {
		SpringApplication.run(GatewayServer.class, args);
	}

}

Authorization Server

Our authorization server is as simple as possible. It based on default Spring security configuration. Client authorization details are stored in an in-memory repository. Of cource in the production mode you would like to use other implementations instead of in-memory repository like JDBC datasource and token store. You can read more about Spring authorization mechanisms in Spring Security Reference and Spring Boot Security. Here’s fragment of configuration from application.yml. We provided user basic authentication data and basic security credentials for the /token endpoint: client-id and client-secret. The user credentials are the normal Spring Security user details.

security:
  user:
    name: root
    password: password
  oauth2:
    client:
      client-id: acme
      client-secret: secret

Here’s main class of our authentication server with @EnableAuthorizationServer. We also exposed one REST endpoint with user authentication details for account service and enabled Eureka registration and discovery for clients.

@SpringBootApplication
@EnableAuthorizationServer
@EnableDiscoveryClient
@EnableResourceServer
@RestController
public class AuthServer {

	public static void main(String[] args) {
		SpringApplication.run(AuthServer.class, args);
	}

	@RequestMapping("/user")
	public Principal user(Principal user) {
		return user;
	}

}

Application – account microservice

Our sample microservice has only one endpoint for @GET request which always returns the same account. In main class resource server and Eureka discovery are enabled. Service configuration is trivial. Sample application source code is available on GitHub.

@SpringBootApplication
@EnableDiscoveryClient
@EnableResourceServer
public class AccountService {

	public static void main(String[] args) {
		SpringApplication.run(AccountService.class, args);
	}

}
security:
  user:
    name: root
    password: password
  oauth2:
    resource:
      loadBalanced: true
      userInfoUri: http://localhost:9999/user

Testing

We only need web browser and REST client (for example Chrome Advanced REST client) to test our solution. Let’s start from sending authorization request to resource owner. We can call oauth2 authorize endpoint via Zuul gateway in the web browser.

http://localhost:8765/uaa/oauth/authorize?response_type=token&client_id=acme&redirect_uri=http://example.com&scope=openid&state=48532

After sending this request we should see page below. Select Approve and click Authorize for requests an access token from the authorization server. If the application identity is authenticated and the authorization grant is valid an access token to the application should be returned in the HTTP response.

oauth2

http://example.com/#access_token=b1acaa35-1ebd-4995-987d-56ee1c0619e5&token_type=bearer&state=48532&expires_in=43199

And the final step is to call account endpoint using access token. We had to put it into Authorization header as bearer token. In the sample application logging level for security operation is set to TRACE so you can easily find out what happened if something goes wrong.

call

Conclusion

To be honest I’m not very familiar with security issues in applications. So one very important thing for me is the simplicity of security solution I decided to use. In Spring Security we have almost all needed mechanisms out of the box. It also provides components which can be easily extendable for more advanced requirements. You should treat this article as a brief introduction to more advanced solutions using Spring Cloud and Spring Security projects.

34 thoughts on “Microservices security with Oauth2

  1. I get the token after login in browser, OK.
    But when i put the token in postman with “Bearer token_string”, it returns the login form, why?
    Another question, after i login in the browser i cant access via zuul to my microservices, it says Full authentication is required to access this resource, please give me an idea of what is going wrong.

    Greetings from Perú

    Like

  2. I get the token after login in browser, OK.
    But when i put the token in postman with “Bearer token_string”, it returns the login form, why?

    i am gettng same error, can you tell me what wrong i am doing

    Like

    1. Hi Can you please update whats wrong i am doing. Its not working from application also. Getting login page in response while calling account endpoint using access token.

      Like

      1. I am calling following endpoint on account-service . I am using Rest Client chrome extension to call endpoint.
        http://localhost:8765/account
        Request Type: Get
        Header passed:
        Authorization:Bearer 20cbd6ae-7b5f-47dc-860f-c047e36ab3a6
        Content-Type:application/json
        Response getting:
        ===============================
        Login page
        Login page
        Example user: user / password
        Username:
        Password:
        Back to home page
        ==========================

        Like

      1. I am calling following endpoint on account-service . I am using Rest Client chrome extension to call endpoint.
        http://localhost:8765/account

        Request Type: Get
        Header passed:
        Authorization:Bearer 20cbd6ae-7b5f-47dc-860f-c047e36ab3a6
        Content-Type:application/json

        Response getting:

        Login page

        Login page
        Example user: user / password

        Username:

        Password:

        <!– –>

        Back to home page

        Like

  3. Hi Can you please update whats wrong i am doing. Its not working from application also. Getting login page in response while calling account endpoint using access token.

    Like

  4. Getting a JDBC error in the AuthServer:
    Caused by: java.net.ConnectException: Operation timed out (Connection timed out)
    at java.net.PlainSocketImpl.socketConnect(Native Method) ~[na:1.8.0_121]
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) ~[na:1.8.0_121]
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) ~[na:1.8.0_121]
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) ~[na:1.8.0_121]
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[na:1.8.0_121]
    at java.net.Socket.connect(Socket.java:589) ~[na:1.8.0_121]
    at com.mysql.jdbc.StandardSocketFactory.connect(StandardSocketFactory.java:211) ~[mysql-connector-java-5.1.40.jar:5.1.40]
    at com.mysql.jdbc.MysqlIO.(MysqlIO.java:300) ~[mysql-connector-java-5.1.40.jar:5.1.40]
    … 39 common frames omitted

    Like

  5. Great Article!

    Could you please explain me why we have to define the authorization server as a resource (@EnableResourceServer annotation)? I have test it and works like a charm but I don’t understand why this annotation is needed. I understand its use in the microservices but not in the authorization server.

    Thanks!

    Like

  6. HI
    Thanks for the tutorial.
    I have 2 queries .
    1. You have commented the code in OAuth2Config.java file in auth package. Any reasons. Will it work .
    2. OAUTH2 is authorization framework. So if a website gives option for login with facebook account or google account like delegated the authentication to these sites then will it be still OAuth2 scenario.
    Thanks

    Like

  7. Hello Piotr Mińkowski,
    First of all, i want to thanks you about your post. It’s great.
    Could you explain me this point, please? I don’n understand why do you put login form and login confirmation in Gateway module. In fact, gateway only have one mission – forwarding the request. And the Auth-Service normaly is the responsible of Authentification and Authorization. So, i think it’s better that Auth-Service do Authentification and Authorization and that Gateway only forward the request (it shouldn’t have the login form and login confirm).
    Thanks a lot
    I’m waiting your response.
    Best,

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.