Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have a spring-boot application with 4 microservices, eureka server and a centralized API gateway.
All external traffic is coming via my API gateway to my microservices.
My API gateway (Zuul) is validating and verifying JWT token.
the JWT token is generated by one of my microservices after user login (the users microservice), the token contain the user Id and his roles/authorities.
Now I want to implement role-based security on methods that are present in microservices other than the gateway.
I have tried to use #PreAuthorize but it's not working out of the gateway (obviously in order to make it work I have to set a Spring Security authentication object in the SecurityContextHolder in my microservices and populate it with authorities).
So is there any solution to achieve this type of security?
What is the best design to set up security in microservice architecture?
Authentication at API gateway level and authorization at microservices level?
Do I need to use spring security within the microservices or just pass down the roles (append them to the request) after validating the JWT at API gateway level and for example create my own annotations and use Spring AOP to handle authorization?
In Spring5 microservices you will be able to find a base to develop a microservice architecture with several of the requisites you are looking for:
Registry server using Eureka.
Gateway server with Zuul.
Regarding to security, I have developed two different microservices:
Spring Oauth2 with Jwt
Spring Jwt multi-application security service to work with access and refresh Jwt tokens, with several customizations like: definition of the content of every one, work with JWS or JWE, etc
Most important ones are well documented using Swagger, as you can see here, and all documented APIs are accessible using an unique gateway Url.
For all classes of every microservice, Junit tests were developed.
Security
At this point, I took several decisions:
1. Is not the gateway the microservice that verifies the security.
Because use the gateway as "firewall" is a less flexible approach. I wanted to decide which microservices need security and every one should manage internally the roles can access to every endpoint. In summary, every microservice has to work with the authorization/authentication but it don't need to know how that functionality is done.
2. Specific microservice to deal with the security
As I told you, I developed 2 different ones, because I wanted to "play" with different options/approaches. The most important advantage is the encapsulation, if "tomorrow" I decide to change Jwt by any other option, I will only need to modify those ones, the microservices that use them will keep the same code (I will explain you soon how the integration was done)
Security integration example
I will explain how the security functionality was integrated between:
Pizza service easy microservice developed as part of the architecture.
Spring Jwt
1. Every application that manages user and roles, will include in the security microservice a folder similar to the next one, to define its models, repositories to get the required information, etc
2. Global endpoints of the security microservice are defined here. As you can see, they work basically with 2 Dtos:
AuthenticationInformationDto
UsernameAuthoritiesDto
The main advantage, only the security microservice knows the details about how that functionality was done, the other ones that use it will receive a well known Dtos with the required information.
3. In pizza-service, the security integration is mainly defined in the next 3 classes:
SecurityContextRepository to get authorization token from the header and send it to the SecurityManager.
SecurityManager call to security-jwt-service with the provided "authorization token" (it doesn't know if it is Jwt or any other thing) and receives a well know UsernameAuthoritiesDto (transforming it into an object of the Spring class UsernamePasswordAuthenticationToken)
WebSecurityConfiguration global security configuration.
Now you can include in your endpoints the required role based security:
Controller example
Custom PreAuthorize annotation
Final considerations
pizza-service was developed using Webflux, you can see an equivalent integration based on a MVC microservice one in order-service here (in this case I used the "other security service" but is easy to adapt it).
To improve the security and follow the "Oauth approach", the requests to security-jwt-service need to include the Basic authentication too. As you can see in SecurityManager class:
private String buildAuthorizationHeader(String username, String password) {
String auth = username + ":" + password;
byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
return "Basic " + new String(encodedAuth);
}
The table in database to store that information is the same one used to manage the security configuration of every application: security.jwt_client_details
Question is wide at the moment as the nature of the traffic to your microservices is not clear.
Assuming that all external traffic coming via your API gateway to your microservices.
You don't need to validate the JWT twice once in API gateway and then again in your internal microservice. If the JWT is invalid , the request will never reach your microservice
Then API gateway propagate the roles. In your microservice, you initialise the spring security context using the roles passed in the header. It will allow you to use #PreAuthorize etc
Assuming that external traffic can come via your API gateway as well as directly to your microservices.
Now you need to verify it in both API gateway and in your microservices
Update
I don't have knowledge about Zuul API gateway. This is just addressing the following:
I have tried to use #PreAuthorize but it's not working out of the gateway (obviously in order to make it work I have to set a Spring Security authentication object in the SecurityContextHolder in my microservices and populate it with authorities).
public class PreAuthenticatedUserRoleHeaderFilter
extends GenericFilterBean {
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String rolesString = //extract the roles
String userName = // extract the username
List<GrantedAuthority> authorities
= AuthorityUtils.commaSeparatedStringToAuthorityList(rolesString);
PreAuthenticatedAuthenticationToken authentication
= new PreAuthenticatedAuthenticationToken(
userName, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(servletRequest, servletResponse);
}
}
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true,
jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
PreAuthenticatedUserRoleHeaderFilter authFilter
= new PreAuthenticatedUserRoleHeaderFilter();
http.
antMatcher("/**")
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(authFilter,
BasicAuthenticationFilter.class)
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
If you'r API gateway is also the one who create's and sign JWT token's with private key's and to authenticate you use public key's from API gateway then you are the one who specifies structure of that JWT token and you should be able to encode roles into that JWT (it could be scope parameter for example but all possible scope's are usually accessible by all users). Then you can configure spring boot to automaticaly resolve group role from that JWT (set SecurityContextHolder role's right) and #PreAuthorize annotation can be used without any modification.
If you'r API gateway is only verifying JWT token's against 3rd party authorization server (the server which signed and structured that JWT) with public key's from this server you must implement some custom mechanism for role-based access. One that come's to my mind is to implement second level Oauth2 authentication which would be only used with request's between your microservice's and API gateway using some kind of 'inner' JWT. For example see following image:
Since you define how structure of inner JWT should look by your API gateway code you can set custom attribute's like role: (admin, user etc..). This can be resolved for example from user name, id, email which you are provided from outer JWT from 3rd party authorization server. Therefore you would need to keep some mapping inside API gateway code like:
(userId: 12563) => Admin group
(userId: 45451) => User group
Since your micro-services use JWT for authentication you can use spring boot resource server to setup authentication and configure it to resolve group's (object you mentioned inside SecurityContextHolder) automatically from your custom structured inner JWT. This way you could simply use #PreAuthorize annotation inside your micro-service's and therefore you would not have to create custom annotation's. Note that this is only supposed to solve second case i have specified in first case you are in control of JWT token already.
Role based authorization is avoided these days and scope based authorization is preferred with something like service1.read, service2.full_access scopes. You could either: move role based authorization into each service and away from identity server, convert to scope based authorization, move role based authorization job to respective service rather than relay on identity server.
You can user reference token flow and invalidate token when changes occurs in your role/rights, this will help explaining it
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 days ago.
Improve this question
Spring Boot - Angular Application:
Why does my method return "AnonymousUser" when called from one controller, but the correct user id when called from another controller?
I have a method that returns the logged-in user:
#Override
public String getLoggedUserName() {
return SecurityContextHolder.getContext().getAuthentication().getName();
}
When I call it from a controller, it returns the name of the logged-in user, but when I call it from another controller, it returns "AnonymousUser". Why is this happening?
I don't know if it can be useful as additional information but authentication is via Keycloak
I'll answer first to what I guess your actual question is: "how to be sure to have a JwtAuthenticationToken in the security-context of a #ResController?"
Configure your app as resource-server with a JWT decoder
Protect entry-points with isAuthenticated() either form security filter-chain configuration or #PreAuthorize (or equivalent) on controller class or method (with #EnableMethodSecurity somewhere in the config)
Off course, do not explicitly replace the default Authentication implementation for successful authorizations... (when providing an authentication converter for instance)
Now a complete answer to what you formulated and is way wider as the type of Authentication implementation in the security context of controller method invocation varies depending on quite a few factors.
Runtime
Let's start with "real" invocations (spring app up and running, nothing mocked):
request is not authorized, then an AnonymousAuthenticationToken is put in the security context. Depending on the security config, this could be because of missing or invalid access token (expired, wrong issuer or audience, bad signature, ...), missing basic header, etc., or because the type of authorization is not the right one (for instance a Bearer token on a route intercepted by a security filter-chain expecting basic authentication)
request is successfully authorized => default authentication depends on the security conf
resource-server with JWT decoder, then JwtAuthenticationToken is used (can be accessed verridden by configuring http.oauth2ResourceServer().jwt().jwtAuthenticationConver(...)
resource-server with access token introspection, then BearerTokenAuthentication is used ( override with http.oauth2ResourceServer().opaqueToken().authenticationConver(...))
client with oauth2Login, then OAuth2AuthenticationToken is used
etc., the list continues for non OAuth2 authentications (just have a look at Authentication type hierarchy)
Tests
Test security context can be set for tests in plain JUnit using annotations like #WithMockUser or using MockMvc (respectively WebTestClient for reactive apps) and either annotations or request post processors (respectively WebTestClient mutators).
The type of Authentication injected depends on the annotation (or mutator / post-processor) used. For instance, #WithMockUser builds a UsernamePasswordAuthenticationToken which is rarely adapted to an OAuth2 context. Mutators from SecurityMockMvcRequestPostProcessors from spring-security-test or annotations like
#WithMockJwtAuth, #WithMockBearerTokenAuthentication or #WithOAuth2Login from spring-addons are generally better suited.
Tutorials for both runtime config and tests
I wrote some available from there: https://github.com/ch4mpy/spring-addons/tree/master/samples/tutorials
I am using Spring Security along with Spring Authorization Server and experimenting with creating an auth server.
I have a basic flow allowing me to login with the pre-built login page (from a baledung guide - this is the code I'm working off ). I'm assuming this login page form comes from formLogin() like so:
http.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
//.formLogin(withDefaults());
return http.build();
I would like to not use this pre-built form as I have a need to host and run the login form front-end application completely separately. ie on a different server, domain and codebase.
Another way to ask this question could be:
How do I disable the built in form in authorization-server so I can use it with a completely separate form?
Are there any recommended ways of learning about how customise my SecurityFilterChain along these lines? Is this the correct place to look? I find the baledung article (and articles like that) helpful as a starting point, but seldom works for more practical use case. I'm confident Spring Security and the oauth2 libraries will allow me to do what I want, but not entirely clear.
After discussing this with you, I've gathered that what you're trying to do is essentially pre-authenticate the user that was authenticated through another (separately hosted) login page, actually a separate system. The idea is that the other system would redirect back with a signed JWT in a query parameter.
This really becomes more of a federated login problem at that point, which is what SAML 2.0 and OAuth 2.0 are aimed at solving. However, if you have to stick with things like a signed JWT (similar to a SAML assertion), we could model a fairly simple pre-authenticated authorization_code flow using the Spring Authorization Server.
Note: I haven't explored options for JWT Profile for OAuth 2.0 Client Authentication and Authorization Grants but it could be a viable alternative. See this issue (#59).
Additional note: There are numerous security considerations involved with the approach outlined below. What follows is a sketch of the approach. Additional considerations include CSRF protection, using Form Post Response Mode (similar to SAML 2.0) to protect the access token instead of a query parameter, aggressively expiring the access token (2 minutes or less), and others. In other words, using a federated login approach like SAML 2.0 or OAuth 2.0 will always be RECOMMENDED over this approach when possible.
You could to start with the existing Spring Authorization Server sample and evolve it from there.
Here's a variation that redirects to an external authentication provider and includes a pre-authentication mechanism on the redirect back:
#Bean
#Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
// #formatter:off
http
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("https://some-other-sso.example/login"))
);
// #formatter:on
return http.build();
}
#Bean
#Order(2)
public SecurityFilterChain standardSecurityFilterChain(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
// #formatter:on
return http.build();
}
#Bean
public JwtDecoder jwtDecoder(PublicKey publicKey) {
return NimbusJwtDecoder.withPublicKey((RSAPublicKey) publicKey).build();
}
#Bean
public BearerTokenResolver bearerTokenResolver() {
DefaultBearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
bearerTokenResolver.setAllowUriQueryParameter(true);
return bearerTokenResolver;
}
The first filter chain operates on authorization server endpoints, such as /oauth2/authorize, /oauth2/token, etc. Note that the /oauth2/authorize endpoint requires an authenticated user to function, meaning that if the endpoint is invoked, the user has to be authenticated, or else the authentication entry point is invoked, which redirects to the external provider. Also note that there must be a trusted relationship between the two parties, since we're not using OAuth for the external SSO.
When a redirect from the oauth client comes to the /oauth2/authorize?... endpoint, the request is cached by Spring Security so it can be replayed later (see controller below).
The second filter chain authenticates a user with a signed JWT. It also includes a customized BearerTokenResolver which reads the JWT from a query parameter in the URL (?access_token=...).
The PublicKey injected into the JwtDecoder would be from the external SSO provider, so you can plug that in however it makes sense to in your setup.
We can create a stub authentication endpoint that converts the signed JWT into an authenticated session on the authorization server, like this:
#Controller
public class SsoController {
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
#GetMapping("/login")
public void login(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
this.successHandler.onAuthenticationSuccess(request, response, authentication);
}
}
The .oauth2ResourceServer() DSL causes the user to be authenticated when the /login endpoint is invoked. It requires an access_token parameter (used by the BearerTokenResolver) to pre-authenticate the user by validating the signed JWT as an assertion that the user has been externally authenticated. At this point, a session is created that will authenticate all future requests by this browser.
The controller is then invoked, and simply redirects back to the real authorization endpoint using the SavedRequestAwareAuthenticationSuccessHandler, which will happily initiate the authorization_code flow.
Re your comnent: "I'm attempting to build an Authorization Server":
Coding your own Authorization Server (AS) or having to build its code yourself is highly inadvisable, since it is easy to get bogged down in plumbing or to make security mistakes.
By all means use Spring OAuth Security in your apps though. It is hard enough to get these working as desired, without taking on extra work.
SUGGESTED APPROACH
Choose a free AS and run it as a Docker Container, then connect to its endpoints from your apps.
If you need to customize logins, use a plugin model, write a small amount of code, then deploy a JAR file or two to the Docker container.
This will get you up and running very quickly. Also, since Spring Security is standards based, you are free to change your mind about providers, and defer decisions on the final one.
EXAMPLE IMPLEMENTATION
Curity, along with other good choices like Keycloak or Ory Hydra are Java based and support plugins:
Curity Community Edition
Custom Authenticator Example
I have a spring application that verifies a JWT token on the rest endpoint.
Using SecurityChain
.oAuth2ResourceServer()
.jwt()
This seems to create a JwtAuthenticationToken in the ReactiveSecurityContextHolder.
I then want to flow the input from this endpoint where the client is authenticated by checking the bearer token. And then call another rest service using a webClient. This web client needs to authenticate with grant type password with the external service using a different OAuth server and get is own bearer token.
The problem is that the web client uses the ReactiveSecurityContextHolder that contains the authenticated JWT. And tries to use this information rather than connect and authenticate my app to the rest endpoint.
I have set up the Yaml to register my client
spring:
security:
oauth2:
client:
registration:
Myapp:
client-id:
client-secret:
token-uri:
authorization-grant-type:
Then adding a filter function of
ServerOAuth2AuthorizedClientExchangeFilterFunction
But I get principalName cannot be empty as it seems to reuse the security context from verifying the caller on the rest endpoint in my application.
How should it be designed or samples to show how you can use different security contexts or get tokens differently between service to service calls?
You are correct that the design of ServerOAuth2AuthorizedClientExchangeFilterFunction is designed to be based on the currently-authorized client, which you've explained that you don't want to use in this case.
You've indicated that you want to use the client's credentials as the username and password for the Resource Owner Password Grant. However, there's nothing in Spring Security that is going to do that.
However, you can use WebClientReactivePasswordTokenResponseClient directly in order to formulate the custom request yourself.
Briefly, this would be a custom ExchangeFilterFunction that would look something like:
ClientRegistrationRespository clientRegistrations;
ReactiveOAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest>
accessTokenResponseClient = new WebClientReactivePasswordTokenResponseClient();
Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
return this.clientRegistrations.findByRegistrationId("registration-id")
.map(clientRegistration -> new OAuth2PasswordGrantRequest(
clientRegistration,
clientRegistration.getClientId(),
clientRegistration.getClientSecret())
.map(this.accessTokenResponseClient::getTokenResponse)
.map(tokenResponse -> ClientRequest.from(request)
.headers(h -> h.setBearerAuth(tokenResponse.getAccessToken().getTokenValue())
.build())
.flatMap(next::exchange);
}
(For brevity, I've removed any error handling.)
The above code takes the following steps:
Look up the appropriate client registration -- this contains the provider's endpoint as well as the client id and secret
Construct an OAuth2PasswordGrantRequest, using the client's id and secret as the resource owner's username and password
Perform the request using the WebClientReactivePasswordTokenResponseClient
Set the access token as a bearer token for the request
Continue to the next function in the chain
Note that to use Spring Security's OAuth 2.0 Client features, you will need to configure your app also as a client. That means at least changing your DSL to include .oauth2Client() in addition to .oauth2ResourceServer(). It will also mean configuring a ClientRegistrationRepository. To keep my comment focused on filter functions, I've left that detail out, but I'd be happy to help there, too, if necessary.
We are running an API on Sping Boot 2.2 and are consequently using Sping Security 5.2. In securing this API with OAuth, we are using the new features built into Spring Security (since the Spring Security OAuth project is now deprecated). We are using opaque tokens and (as indicated by the documentation) have a security config of the following form:
#Configuration
public static class OAuthWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().mvcMatchers("/path/to/api/**").hasAuthority(CUSTOM_SCOPE)
.oauth2ResourceServer().opaqueToken().introspector(opaqueTokenIntrospector());
}
}
Here opaqueTokenIntrospector() is a bean which performs the following tasks:
Send a request to the introspection endpoint to get the full token.
Also send a request to the userinfo endpoint to get additional info about the user from the IDP.
Map some of this additional info into custom spring roles and add these roles to the authenticated user.
The way this configuration is set up, each request to the API comes with two additional requests: one to the introspection endpoint and one to the userinfo endpoint. It would be better to save on some of these if a user performs successive requests to the API.
Is it possible to save the result of the opaqueTokenIntrospector() in the session of the user? This way the whole flow of the bean need only be done once per user, saving on redundant requests.
This is a common requirement when you get beyond basic APIs. Use a claims caching solution, which is an API gateway pattern:
When a token is first received do the lookups and save them into an object:
Token claims
User info claims
App specific claims
Then cache the results against the token, so that subsequent requests with the same token are fast:
Use a thread safe cache - my preference is to use an in memory one
Use the SHA256 hash of the access token as the key
Use the serialized claims as the value
This probably goes beyond Spring's default behaviour, but you can customise it.
I have a Java sample that does this - here are a couple of links, though my sample is quite a complex one:
Custom Authorizer Class
Caching Class
Java Write Up
I have a query I am struggling to come to a decent answer with and hoping by vocalizing it someone might be able to guide me or advise me in the right direction.
All our current web applications are build using Spring, which includes the security which is handled by Spring Security module of course.
We are exploring the opportunities of integrating some of our new android projects into these web applications.
After some research and guidance, all flags point towards OAuth2 implementation in Android App and using that to obtain access to relevant parts of the web application server via Restfull calls.
What I am trying to understand now is can we and should we replace our existing Spring Security implementation in our web application and replace it with Spring Oauth2 equivalent.
The overall goal would to be able to have a single security solution that we would use for both website login, app login, and any API implementations that would be exposed to other web applications.
If anyone can also provide a link to a Spring Oauth2 Java Config (not-XML) setup where a user logs in and accesses a page based on their role in a unrestful manner, would also be extremely helpful. Most examples we have found were either extremely complex, focused solely on restfull calls, or only XML configurations.
Seems you did not hear about spring-cloud-security project which extremely simplifies working with oauth2 providers. Take a look at:
http://cloud.spring.io/spring-cloud-security/
There are samples available on github ( right side of above page ). It shows how to set up in case you want to use just one oauth2 provider - check project which shows how to do that for github https://github.com/spring-cloud-samples/sso . You do most of this stuff through configuration in application.yml file + add #EnableOAuth2Sso annotation so all machinery starts.
For 'normal' interaction with your web app its pretty straighforward. If you need your app to work as an api for others then you have to maintain tokens, add them to request etc. If client of your api is also your code which you can change then you may use OAuth2RestTemplate class which take care about tokens, so you do queries to your api as it was usual/not secured endpoint.
If you need to use for example two different providers for different paths then its more complicated, you have to configure them all like:
#Bean
public OAuth2RestTemplate facebookOAuth2RestTemplate(OAuth2ClientContext clientContext) {
return new OAuth2RestTemplate(facebookOAuth2ResourceDetails(), clientContext);
}
#Bean
public OAuth2ProtectedResourceDetails facebookOAuth2ResourceDetails() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setAccessTokenUri(tokenUri);
resource.setId("id");
resource.setUserAuthorizationUri(authorizationUri);
resource.setUseCurrentUri(false);
resource.setPreEstablishedRedirectUri(redirectUri);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setTokenName("tokenname");
resource.setAuthenticationScheme(AuthenticationScheme.query);
resource.setClientAuthenticationScheme(AuthenticationScheme.form);
return resource;
}
and decide which instance of OAuth2RestTemplate to use in which case.
//update
If you want to exchange spring security core with authorizing users by some oauth2 provider you can extends OAuth2SsoConfigurerAdapter class:
#Configuration
#EnableOAuth2Sso
public class WebSecurityConfig extends OAuth2SsoConfigurerAdapter {
#Override
public void match(RequestMatchers matchers) {
matchers.antMatchers("/admin");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.and()
.logout().logoutUrl("/logout").permitAll()
.logoutSuccessUrl("/");
}
So /admin will be now protected, and user redirected to any authorization server you specify. It requires oauth configuration in application.yml.
spring:
oauth2:
client: # Sauron
clientId: app_clientid
clientSecret: app_secret
accessTokenUri: http://authorizationserver/oauth/token
userAuthorizationUri: http://authorizationserver/oauth/authorize
clientAuthenticationScheme: header
resource:
tokenInfoUri: http://authorizationserver/oauth/check_token
preferTokenInfo: false
Thats why I wrote before that its easy to use just one auhorization server, but in case you need more then its more complicated.