I'm considering to use OAuth2 for my application. The architecture I'm trying to implement is as follows:
I will have my own (and only this) Authorization Server
Some Resource Apps validating access to their resources using the Authorization Server
Some client apps (web, mobile) which will redirect the user to the Authorization Server for authentication and on success will consume the api's on the Resource Apps.
So far I have managed to implement this interaction between 3 basic apps (1 auth server, 1 resource server and 1 client). The thing I don't get working is the logout functionality. I have read of the "notoriously tricky problem" that Dave Syer describes in his tutorial, but in this case I really need the user to re-login after loging out. I have tried giving few seconds to the access token and the refresh token, but instead of being prompted to login again when the expiration arrives, I'm getting a NPE on the client app. I have also tried the solutions proposed in this post to remove the token from the token store, but it doesn't work. The single sign off is for me the desirable behaviour for this implementation. How can I achieve this using Spring Boot Oauth2. If it is not possible for some reason, which alternatives I could use to implement a centralized security using Spring Boot?
Thanks in advance.
After a lot of tests I have realized that this can be solved just with a redirect to the AuthServer and doing logout programmatically like this:
In the client app (WebSecurityConfigurerAdapter):
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.logout()
.logoutSuccessUrl("http://your-auth-server/exit");
}
In the authorization server:
#Controller
public class LogoutController {
#RequestMapping("/exit")
public void exit(HttpServletRequest request, HttpServletResponse response) {
// token can be revoked here if needed
new SecurityContextLogoutHandler().logout(request, null, null);
try {
//sending back to client app
response.sendRedirect(request.getHeader("referer"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
I have posted a sample app on github with a full example of this implementation.
Related
I am using Spring Security to secure an application using OAuth. I wanted to make it stateless so I have a custom authentication filter that authenticates based on a token from the client. My configuration for Spring Security is as follows:
#Override
protected void configure(HttpSecurity http) throws Exception {
// require https
http.requiresChannel().antMatchers("/**").requiresSecure().and().portMapper().http(80).mapsTo(443);
http.antMatcher("/**").authorizeRequests().anyRequest().authenticated().and()
.cors().and()
.csrf().disable()
.addFilterBefore(customOAuth2Filter(), BasicAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
As you can see, I am using SessionCreationPolicy.STATELESS which should mean that Spring Security never uses a session. However, if I login and then try to access a protected endpoint without a token(say, if I close the tab and then open a protected endpoint in a new tab, or if I just refresh the page), the application will give me the data at the endpoint instead of asking me to login again.
So it seems like a session is being used even though I am using SessionCreationPolicy.STATELESS. However, I did notice that the JSESSIONID of each request being sent does not match the JSESSIONID in the set-cookie header of the previous response. Not sure why that is or where the new JSESSIONID is coming from.
How do I make it so subsequent requests without the token will ask for login again? Any insight into what is happening here would be appreciated, thanks.
What i found while using spring oauth framework is resource server making check_token?token=T_O_K_E_N request to authorisation server, and authorisation server is just returning CheckTokenEndPoint map with authorities something like below.
{
"exp": 1511471427,
"user_name": "idvelu",
"authorities": [
"FUNCTION_GET_USERS",
"FUNCTION_AUTHORITY_1",
"FUNCTION_AUTHORITY_2",
"FUNCTION_AUTHORITY_3",
"FUNCTION_AUTHORITY_4",
"FUNCTION_AUTHORITY_5",
"FUNCTION_AUTHORITY_6",
"FUNCTION_AUTHORITY_7",
],
"client_id": "c1",
"scope": [
"read",
"write"
]
}
Just visualise this with oauth service and resource service is running in two different machines/jvm.
I think now resource server has to authorise the request against configured valid authorities in ResourceServerConfiguration::configure(HttpSecurity) with the authorities from the authorisation server.
#Override
public void configure(HttpSecurity http) throws Exception {
http.anonymous().disable().requestMatchers().antMatchers("/**").and().authorizeRequests()
.antMatchers(HttpMethod.GET, "/myproject/users").hasAnyAuthority("FUNCTION_GET_USERS")
.antMatchers(HttpMethod.POST, "/myproject/users").hasAnyAuthority("FUNCTION_POST_NEW_USER")
.anyRequest().denyAll()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
In this case authorisation server may return all the hundreds of user authorities to resource server. Instead why not authorisation server itself can take the few of the permission required for authorisation as query params check_token?token=T_O_K_E_N&authorities=FUNCTION_GET_USERS,FUNCTION_AUTHORITY_2,.. from the resource server and validate it against the user's functions through DB?
And finally my problem is; i have different services like java, node.js, NGINX... All these have to verify its authentication and authorization against one spring Authorisation server. Because of the above stated problem all my service has to implement the authorisation (resource server) part. Means comparing all the authorities of user against the API acess authorities. Java side this comparison is fine with spring resource server implementation. But all other non-java (resource) services needs authorisation/resourceServer implementation. Instead if my spring authorisation server accepts the authorities and validates then my problem is solved as single point of authorisation/comparison implementations. I just need to pass it as part of check_token.
How to implement this new check_token endpoint along with accepting the authorities?
The authorization server is responsible for authenticating the user/client (depending on the oauth type you use). When this is done, a token is given.
When a user/client presents themselves to the resource server, wanting to consume a service, they must provide the token. Now the resource server has a token and needs to validate that this token was generated by the authorization server. There are two options:
The token itself does not contain any information. The resource server calls the authorization server and asks if the token is valid. The authorization server will respond that the token is valid and gives some additional information (user/client, roles/scopes, etc).
The token does contain the necessary information (JWT tokes for example). This enables the resource server to extract the needed info without contacting the authorization server. In this case, the authorization server has signed the token and the resource server can validate the signature to be sure that it was the autorization server that has issued the token.
At the moment, you are using the first scenario. Every resource server you write, must verify tokens and extract additional info. How the verification is done depends on the authorization server.
Part2:
Your question is not clear to me. I presume you are using the OAuth2 authorization code scheme with non JWT tokens?
In that case, you have an authorization server that is only responsible for authentication, a resource server that is exposing some services and a client that consumes the resource server.
If i'm not mistaking, you have different resource servers (api's)?
You did not share your authorization server configurations, but normaly you use #EnableAuthorizationServer. This will create an endpoint /oauth/check_token. By default this endpoint is not accassible. You need to do something like this:
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception
{
oauthServer.checkTokenAccess("permitAll()"); // authenticated is better
}
You can use this endpoint to validate tokens.
All this is described in the oauth2 developer guide.
Part3:
On the authorization server, you can create an endpoint like this :
#RequestMapping("/user")
public Principal user(Principal principal) {
if(principal instanceof OAuth2Authentication) {
return (OAuth2Authentication) principal;
} else {
return principal;
}
}
You can modify it to your needs.
I am developing a website using Spring Boot 1.5.7 as back-end and Angular 2 as front-end.
I am a newbie on both technologies, and it's the very first time I try to develop a website. So I am a bit confused on many things.
I have implemented user authentication through JWT.
When the user logins through credentials, the backend verifies them and then creates a JWT and returns it to the frontend: the token is added to the header this way:
Authorization - Bearer <jwt token>
In the frontend I check out if that key is in the post response. If it is there, I add it along with the username to the localStorage.
private authUrl = 'http://localhost:8080/login';
private headers = new Headers({
'Content-Type': 'application/json',
'Accept': 'application/json'
});
constructor(private http: Http) { }
login(username: string, password: string): Observable<void> {
let loginRequest = JSON.stringify({ username: username, password: password });
return this.http.post(this.authUrl, loginRequest, { headers: this.headers })
.map((response: Response) => {
let token = response.headers.get('Authorization');
// If token is not null, empty or undefined.
if (token) {
localStorage.setItem('jwt', JSON.stringify({ username: username, token: token }));
}
});
}
When the user is logged in, everytime he accessed a protected resource, a token will be retrieved from the localStorage and sent back to the backend for validation.
The whole thing works. JWT are immune to CSRF, so I can disable that in the back-end,
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
...
but I have read (for instance, here) that there are vulnerabilities when using localStorage.
In particular the localStorage is accessible through JavaScript on the same domain, exposing to XSS attacks.
To resolve it seems I could use a JWT Cookie. As written in the link before, I could set the HttpOnly cookie flag to avoid cookies to be accessed through JavaScript.
But, with cookies I am now vulnerable to CRSF attacks.
Now, here, I have read Angular 2+ provides built-in, enabled by default, anti XSS.
So the question is. I should use localStorage and just use the embedded Angular 2 anti XSS feature, or is that not enough and then I should store JWTs on cookies to get protection against XSS attacks and then implement on top of it some kind of CRSF protection backend side using Spring Boot configuration?
Thank you
EDIT: the website is a sort of shopping cart. The user can view almost all
pages, but to pay he needs to log in.
Aniruddha Das approach is fine but client application will lost the token if user will refresh the browser because DOM will be reloaded with browser refresh and all memory data including token will be lost.
Now come back to your approach-
Local storage - CSRF attack is not possible using this approach and application will be stateless but it is prone to XSS attack. By default Angular do the output encoding to prevent the XSS attack but risk is still there with server side Angular template. To mitigate the XSS attack, you can reduce the token expiry time and encrypt it, if there is some sensitive information.
Cookie approach - HTTP cookie will mitigate the XSS attack but you have to implement the CSRF protection. You have to use the API gateway pattern to make the application stateless.
Both approach have prons/cons and you have to select depending on your application. If your application is related to financial domain then I would suggest cookie based approach.
In angular you can hold your token in service and use it when ever it required. Like pojo in java in angular you can create a angular service with getter and setter to hold the token. Provide that service to the module and it will available in all component and directives.
The token will be in memory while the application is open in the browser and will be be stored in the browser.
I would say use a observable/Subject type variable so that it will wait until the token is extracted from server and use that to do stuffs.
I have 2 apps running, one is resource server where I have the info that needs authentication to view the text. Then I have authorization server that gives tokens. Right now I can use postman or Insomnia, add the auth_url, token_url, client_id, client_secret and I get the token. I add the token to header and i get do a get request to my resource server using header, and it works just fine.
Now i have no idea how to implement redirection from my resource server directly. Like when I go to
localhost:9000/home
I'd like to get redirected to:
localhost:9001/login
where I login with my inmemory user then it redirects me back to localhost:9000/home and I see the message.
What would be the best way to implement a way for user to access information on localhost:9000/home. You go to localhost:9000/home, it goes to authorization server on localhost:9001, you log in with username and password. Approve the grant, and it puts you back to localhost:9000/home and then you can see the text, what was previously protected, because you didn't have token to access it.
ResourceServer.java
#SpringBootApplication
#RestController
#EnableResourceServer
#EnableOAuth2Client
public class SampleResourceApplication extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**").hasRole("user")
.anyRequest().authenticated();
}
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
public static void main(String[] args) {
SpringApplication.run(SampleResourceApplication.class, args);
}
#RequestMapping("/home")
public String home() {
return "this is home";
}
}
and my properties looks like:
server:
port: 900
security:
oauth2:
client:
client-id: foo
client-secret: foosecret
access-token-uri: http://localhost:9001/auth/oauth/token
user-authorization-uri: http://localhost:9001/auth/oauth/authorize
grant-type: USER
auto-approve-scopes: true
resource:
user-info-uri: http://localhost:9001/auth/user
Let's separate the agents: You have the user (i.e. you, also know as the resource owner), the authorization server, the resource server and the client (the application that access your urls, i.e. your browser).
Normally, this happens in your situation:
When your client access the resource server, it receives a 401. Depending of your implementation, you could also directly redirect the client to your AS (using a simple redirect response).
Your AS prompts you for credentials. After validating them, it issues a token for you. You can then use this token to access the RS.
What you're trying to get (if I understand correctly) is to redirect with the token automatically. To achieve this, you can simply pass the url you tried to reach (i.e. localhost:9000/home) when you redirect to your AS at the end of step 1. Your AS hten prompts the user for credentials, generate the token, stores it as a cookie (in the case of a browser), and redirects you to the url he received (localhost:9000/home).
EDIT: what's the resulting code for the redirection.
When you get to the configure, you first check if the user is authenticated. If he is, then all's fine, but if he isn't, you must catch this event and start your redirection. This can be done using the exceptionHandling method of the chaining http:
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**").hasRole("user")
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint());
}
private AuthenticationEntryPoint authenticationEntryPoint() {
return new AuthenticationEntryPoint() {
// You can use a lambda here
#Override
public void commence(HttpServletRequest aRequest, HttpServletResponse aResponse,
AuthenticationException aAuthException) throws IOException, ServletException {
aResponse.sendRedirect(MY_AS_URL + "?redirect_uri=localhost:9001/home");
}
};
}
Unfortunately, I am not familiar with the Spring framework, but hopefully this helps anyways:
OAuth is an authorization protocol. It does not handle authentication (see also: "What is the difference between authentication and authorization?" on ServerFault).
If I understand you correctly, you want users to be redirected to /login when they go to /home and aren't already logged-in. This step has nothing to do with OAuth, it must be part of your application's security / firewall setup.
Please also note that there is a difference between logging in (authenticating) on the authorization server and actually granting the application the right to access your resources (authorization). First you have to prove who you are and only then can you give access to your stuff. These are two separate steps.
For a Spring Boot based web application serving REST APIs protected by OAuth 2, how to intercept access tokens before forwarding a request to a method?
In details,
The application is enabled as a resource server by #EnableResourceServer, and its API is protected by OAuth 2 (specifically, Cloud Foundry User Account and Authentication).
Now for any request to a method with #RequestMapping, it requires an access token obtained from OAuth 2. For example,
curl -H "Authorization: Bearer [token]" [api]
To achieve fine-grained access control,
Upon a request's arrival, I would like to parse the access token, and based the content of the access token (specifically, Scope) forward or deny the request to go forward, hopefully, in a central place (perhaps kind of Inteceptor). How to achieve that?
You can add a custom filter before authentication
e.g.
#Configuration
public class CustomWebSecurityConfigurerAdapter
extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(
new CustomFilter(), BasicAuthenticationFilter.class);
}
}
http://www.baeldung.com/spring-security-custom-filter