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