I have application with Spring security. It's REST api for Angular SPA. It uses session and cookie mechanism. Now I want to access this api from mobile application (Nativescript). I spent some time searching for best way to authenticate mobile app user. In the most cases oauth2 and jwt tokens are advised. So I've done reasearch on this and decided to add additional (seperate) authentication only for mobile application. So Angular app still will be using session with path api/... and mobile app will be using token mechanism with path /api/mobile/... (underneath it will be the same api but with different prefix).
I've decided to use OAuth2 and its Spring integration. I've read documentation and I'm consfused. Why they always mention about authentication provider (Google, Github, Facebook)? I don't want to force my users to login via other service. I want to allow them login with credentials they already registered with in my application. How this social login even related with oAuth authorization server? All examples they've provided use some other services.
I've also tried to add my authorization server in my existing app. I've successfully retrieved token. But now I don't understand all this relationships. There is authorization server that keeps client id and client server. But why /auth/token endpoint needs another authentication? So mobile app needs to provide 3 different credentials - user credentials, client id and secret and token endpoint credentials.
Did I miss something? I know that OAuth is only specification and Spring is implementation of it. But I'm under impression that Spring overcomplicates this. And do I need oauth at all since I have only 1 type of resource?
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer
.inMemory()
.withClient(clientId)
.secret(passwordEncoder.encode(clientSecret))
.authorizedGrantTypes("refresh_token", "authorization_code", "password", "implicit")
.scopes(scopeRead, scopeWrite)
.resourceIds(resourceIds);
}
}
Related
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
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
Our app is currently set up with OAuth OpenID connect authentication with an external (third-party) server. The requirement is to use user details service that loads the user from LDAP (along with the authorities/roles) to complete the authentication. So authentication.getPrincipal() should be returning the custom UserDetails object we use that gets generated by querying LDAP using the username obtained from the Open ID authentication.
I have tried the following:
Followed Similar Issue but it seems like in the answer it's setting up the server-side and it doesn't work either
Tried adding custom UserDetailsService in WebSecurityConfig
#Configuration
public class OAuth2Config extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.oauth2Login().and().userDetailsService(myCustomUserDetailsService());
}
}
where myCustomUserDetailsService() handles the call to LDAP and fetches the user details info, including the authorities.
I'm not too familiar with the spring oauth2 framework, correct me if I'm wrong: I'm guessing I need to implement my own user info endpoint to make a call to LDAP, and not the user info endpoint provided to me by the OpenID service?
You could add a new filter in the security filter chain right after that one. The second filter could retrieve the principal/name from authentication and query for users.
Like a two steps authentication
Not as clean as I hoped, but I registered an application listener on InteractiveAuthenticationSuccessEvent and manually updated the authentication after login (similar post's answer https://stackoverflow.com/a/10747856/11204609)
If anyone has better suggestions?
UPDATE: The identity provider agreed to include LDAP info in their claims so I can directly obtain via the token without having to do a call-back (still an on-going discussion at the moment). That being said, having a auth success callback app listener is only way I could solve the problem.
How can I get details from the OAuth2 SSO Principal into my JWT? (instance of OAuth2Authentication getDetails as OAuth2AuthenticationDetails getDecodedDetails returns null)
I have...
Angular 6 client w/ implicit login as acme client (using angular-oauth2-oidc)
Spring Boot OAuth2 Authorization Server with JWT TokenService configuration w/ 3rd party SSO to GitHub
Auth server is configured with acme as implicit and GitHub client for SSO
Auth server exposes a /login/github
Auth server exposes a /me (protected by ResourceServer config)
When I login...
Angular app redirects to Auth service login
Auth service redirects to GitHub
[User Authenticates]
GitHub redirects to Auth Service
Auth Service initiates a session and issues a token
Auth Service redirects to Angular
The browser token is a proper JWT
Now, when I communicate with Auth Service /me:
Directly, I get a Principal that contains ALL of the details from GitHub (yay)
Indirectly from the Angular application passing the token via Authorization: Bearer ... header, I get a Principal that contains bare minimum OAuth client info for acme client (ugh)
I've tried a custom TokenEnhancer, but the OAuth2Authentication instance is already the bare minimum with no details. And, when the call is initiated from Angular, it doesn't have the same session cookie as when I call it directly (I don't want to share session - I want to put the details in the JWT).
[Update #1]
I tried a custom JwtAccessTokenConverter and used it in both of the #EnableAuthorizationServer and #EnableResourceServer (secures the /me endpoint) configuration classes. However it didn't work. I still get null details from OAuth2Authentication.
final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setAccessTokenConverter(new CustomTokenConverter());
The way Spring Lemon does this is replacing the OAuth2 and OpenID connect user services (see spring security docs). See LemonOAuth2UserService and LemonOidcUserService for details. For statelessness, it passes the client a shortlived JWT token as a param to targetUrl, as you can see in its OAuth2AuthenticationSuccessHandler class. It uses some cookies mechanism for doing all this statelessly, which can be further understood by looking at its HttpCookieOAuth2AuthorizationRequestRepository and how it's configured.
Here is an article explaining this in more details: https://www.naturalprogrammer.com/blog/1681261/spring-security-5-oauth2-login-signup-stateless-restful-web-services .
I've built a REST Service using Spring Boot and Spring Security for authentication. I'm pretty new to both Spring Boot and Spring Security. I've made one authentication module in one JAR file. The front end client sends a request with username and password to the authentication module. The authentication module then authenticates the user's details against a DB. A JWT is created and then sent back to the client if the authentication is successful. The username and role is coded into the JWT. The JWT is then used to verify the user when resources are requested from the other REST Service endpoints that are built in separate JAR files. There are a few things I'm not sure about.
In Spring Security is there one authentication object created for each user so that several users can be authenticated at the same time or is one authentication done each time and only one user can be logged in?
How long is the authentication object in valid? Should I "logout"/remove the authentication successful when the JWT has been created in the authentication module or will it take care of it itself when the request is done? For the resource endpoints (not the authentication endpoint) is there a way to set authentication successful in the authentication object once I've verified the JWT? Similarly can I set the role in the authentication object once the JWT has been verified?
I've based my code on this example https://auth0.com/blog/securing-spring-boot-with-jwts/. I've split it into different JARs for authentication and verification of the JWT (I'm doing verification in resource endpoint). I've also added JDBC authentication instead of in memory authentication.
In Spring Security is there one authentication object created for each
user so that several users can be authenticated at the same time or is
one authentication done each time and only one user can be logged in?
Of course multiple users can be authenticated at the same time!
How long is the authentication object in valid? Should I
"logout"/remove the authentication successful when the JWT has been
created in the authentication module or will it take care of it itself
when the request is done?
You write your service is REST, and if you want to stay "puritan" REST you should configure the authentication to be stateless, which means that the Authentication object is removed when the request has been processed. This does not affect the validity of the JWT token, you can set an expiry of JWT token if you want.
How to make REST stateless with "Java config":
#Configuration
public static class RestHttpConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// and the rest of security config after this
For the resource endpoints (not the authentication endpoint) is there
a way to set authentication successful in the authentication object
once I've verified the JWT? Similarly can I set the role in the
authentication object once the JWT has been verified?
I use code similar to below after verification of the token:
Collection<? extends GrantedAuthority> authorities = Collections.singleton(new SimpleGrantedAuthority("ROLE_JWT"));
Authentication authentication = new PreAuthenticatedAuthenticationToken(subject, token, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
By constructing the authentication object with at least one role (authority), it is marked as "successful" (authenticated).