Microservices security with Oauth2

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 OAuth 2 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 OAuth 2.

  • 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 beginning we authorization request is sent to the 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 the 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 OAuth 2 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 the 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 is based on default Spring security configuration. Client authorization details are stored in an in-memory repository. Of course in the production mode you would like to use other implementations instead of in-memory repositories like JDBC datasource and token store. You can read more about Spring authorization mechanisms in Spring Security Reference and Spring Boot Security. Here’s a 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 the 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 the 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);
   }

}
 

Here’s security configuration for account-service.

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

Testing

We only need a web browser and REST client (for example Chrome Advanced REST client) to test our solution. Let’s start from sending authorization requests to a resource owner. We can call OAuth 2 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 the page below. Select Approve and click Authorize for requests and 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

And the final step is to call the account endpoint using an access token. We had to put it into the Authorization header as bearer token. 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. For more advanced example read this article: Part 2: Microservices security with OAuth 2

0 COMMENTS

comments user
Dhiraj Ray

There are many articles out there on this topic but this article explains it in a best way.

comments user
Sayali Shinde

What software do we need to install ? Do we need to install Eureka or Zuul ?

comments user
Piotr Mińkowski

Well, you don’t have to install it. You just run services with them

comments user
Jaime

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ú

    comments user
    Jaime

    forget it, i did it 🙂

      comments user
      Piotr Mińkowski

      Ok 🙂

      comments user
      Vetri

      can you help me.. got same problem/..

        comments user
        Piotr Mińkowski

        If you send header Authorization: Bearer it should works…

      comments user
      Vetri

      Invalid CSRF Token ‘null’ was found on the request parameter ‘_csrf’ or header ‘X-CSRF-TOKEN

comments user
Rake

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

    comments user
    Rake

    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.

      comments user
      Rake

      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
      ==========================

    comments user
    Piotr Mińkowski

    Can you paste the request which is sent to the service (with HTTP headers)?

      comments user
      Rake

      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

comments user
Rake

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.

    comments user
    Piotr Mińkowski

    Any logs from auth server?

comments user
Abul Fayes

Where did my comment go?

comments user
Abul Fayes

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

    comments user
    Piotr Mińkowski

    Have you got running MySQL database?

comments user
Abul Fayes

Would be good to have a docker-compose file for easily starting up the services

    comments user
    Piotr Mińkowski

    You’re right 🙂

comments user
Alphan ARSLAN

It is really great article thanks or sharing

comments user
Pedro

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!

    comments user
    Piotr Mińkowski

    Hi,
    Thanks. You don’t have to. You can devide seprate out resource server from authorization server

comments user
Daxol

Jak autoryzować się tym tokenem w innych mikroserwisach?

comments user
HB

Perfect 🙂

Thanks.

comments user
DEEPAK

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

comments user
nick ao

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,

    comments user
    Piotr Mińkowski

    Hello. Why do you think that gateway cannot perform authorization or authentication?

Leave a Reply