I'm working on this Spring Security implementation with OAuth2 and JWT:
According to the author I can access resources using token this way:
To access a resource use (you'll need a different application which has configured ResourceServer):
http localhost:8080/users 'Authorization: Bearer '$ACCESS_TOKEN
About this step:
To use the refresh token functionality:
http --form POST adminapp:password#localhost:9999/oauth/token grant_type=refresh_token refresh_token=$REFRESH_TOKEN
It's not clear for me when I need to refresh the token and how to handle this part into Angular.
When the Token expires do I need to first send request to the endpoint for refreshing the token and then to the login page?
How this case should be implemented?
At the time of authentication, two JWTs will be created - access token and refresh token. Refresh token will have longer validity. Both the tokens will be written in cookies so that they are sent in every subsequent request.
On every REST API call, the tokens will be retrieved from the HTTP header. If the access token is not expired, check the privileges of the user and allow access accordingly. If the access token is expired but the refresh token is valid, recreate new access token and refresh token with new expiry dates and sent back through Cookies
Access tokens carry the necessary information to access a resource directly. In other words, when a client passes an access token to a server managing a resource, that server can use the information contained in the token to decide whether the client is authorized or not. Access tokens usually have an expiration date and are short-lived.
Refresh tokens carry the information necessary to get a new access token. In other words, whenever an access token is required to access a specific resource, a client may use a refresh token to get a new access token issued by the authentication server. Common use cases include getting new access tokens after old ones have expired, or getting access to a new resource for the first time. Refresh tokens can also expire but are rather long-lived.
High level code
authenticate()
public ResponseEntity<OAuth2AccessToken> authenticate(HttpServletRequest request, HttpServletResponse response, Map<String, String> params) {
try {
String username = params.get("username");
String password = params.get("password");
boolean rememberMe = Boolean.valueOf(params.get("rememberMe"));
OAuth2AccessToken accessToken = authorizationClient.sendPasswordGrant(username, password);
OAuth2Cookies cookies = new OAuth2Cookies();
cookieHelper.createCookies(request, accessToken, rememberMe, cookies);
cookies.addCookiesTo(response);
if (log.isDebugEnabled()) {
log.debug("successfully authenticated user {}", params.get("username"));
}
return ResponseEntity.ok(accessToken);
} catch (HttpClientErrorException ex) {
log.error("failed to get OAuth2 tokens from UAA", ex);
throw new BadCredentialsException("Invalid credentials");
}
}
refreshToken()
Try to refresh the access token using the refresh token provided as a cookie. Note that browsers typically send multiple requests in parallel which means the access token will be expired on multiple threads. We don't want to send multiple requests to UAA though, so we need to cache results for a certain duration and synchronize threads to avoid sending multiple requests in parallel.
public HttpServletRequest refreshToken(HttpServletRequest request, HttpServletResponse response, Cookie refreshCookie) {
//check if non-remember-me session has expired
if (cookieHelper.isSessionExpired(refreshCookie)) {
log.info("session has expired due to inactivity");
logout(request, response); //logout to clear cookies in browser
return stripTokens(request); //don't include cookies downstream
}
OAuth2Cookies cookies = getCachedCookies(refreshCookie.getValue());
synchronized (cookies) {
//check if we have a result from another thread already
if (cookies.getAccessTokenCookie() == null) { //no, we are first!
//send a refresh_token grant to UAA, getting new tokens
String refreshCookieValue = OAuth2CookieHelper.getRefreshTokenValue(refreshCookie);
OAuth2AccessToken accessToken = authorizationClient.sendRefreshGrant(refreshCookieValue);
boolean rememberMe = OAuth2CookieHelper.isRememberMe(refreshCookie);
cookieHelper.createCookies(request, accessToken, rememberMe, cookies);
//add cookies to response to update browser
cookies.addCookiesTo(response);
} else {
log.debug("reusing cached refresh_token grant");
}
//replace cookies in original request with new ones
CookieCollection requestCookies = new CookieCollection(request.getCookies());
requestCookies.add(cookies.getAccessTokenCookie());
requestCookies.add(cookies.getRefreshTokenCookie());
return new CookiesHttpServletRequestWrapper(request, requestCookies.toArray());
}
}
Related
In the spring boot project, when the user logouts, we invalidate the cookie with this block of code:
//name = "Token"
//value = "expired"
//age = 0
private void setExpiredCookie(HttpServletResponse response, String name, String value, int age) {
Cookie cookie = new Cookie(name, value);
cookie.setSecure(true); //Send cookie to the server only over an encrypted HTTPS connection
cookie.setHttpOnly(true); //Preventing cross-site scripting attacks
cookie.setPath("/"); //Global cookie accessible every where
cookie.setMaxAge(age); //Deleting a cookie. I Passed the same other cookie properties when you used to set it
response.addCookie(cookie);
}
However, after logout, I tested my website with an application for catching the request and resending it through the repeater, with exact values, such as token and payload.
I resent a request, for example, to change the email address, and this request, despite logging out, is valid for 15 minutes (for the life of the original cookie).
What am I missing? Because I am properly deleting and protecting cookies.
You are just creating new cookie.
You should invalidate cookie with session id, which was given to you when you authenticated. Simply use this:
HttpSession session = httpServletRequest.getSession(false);
session.invalidate();
I would like to know what's the best and secured way to send the JWT generated in my spring boot app to my Flutter mobile app. I can send it in the response body which I know that's not a good practice in web but is it a problem when it comes to mobile?
This is how I return the JWT:
public ResponseEntity<?> signin(String username, String password) {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
String token= jwtTokenProvider.createToken(username, userRepository.findByUsername(username).getRoles());
//return new JwtResponse(token,username,userRepository.findByUsername(username).getRoles());
return ResponseEntity.ok(new AuthToken(token));
} catch (AuthenticationException e) {
throw new CustomException("Invalid username/password supplied", HttpStatus.UNPROCESSABLE_ENTITY);
}
}
IT doesn't matter how you send your JWT token, in the Header or in the body portion. it can easily be seen and copied by others through some app on the client-side.
The steps you can take to secure your data is
firstly You need to check if the Token is tampered with by the client, every time you receive a JWT token from the client
Secondly, you should add a created-at field where you send a timestamp of when the Token was created. This will prevent someone to pose as the owner of the token forever after this is stolen, as this Token will be invalid after some time.
and you can send your token simply in the body part of the response after taking those security measures. Of course, there are many more best practices to follow but these are a good starting point.
Hi I am using Spring MVC 4.3 version. We have using Spring CSRF functionality. springSecurityFilterChain defined in web.xml will make sure to go through CSRFFilter class. As per my knowledge Spring stores csrf token in session.
I have created a controller method which accepts HTTP GET call. This is the first method in my application. First time when I am trying to access the token from session I am getting null. I tried below possibilities only for request.getAttribute("_csrf") call is returning null. Why am I getting null from session call? Is it okay to use request attributes?
HttpSession session = objHttpRequest.getSession(false);
HttpSessionCsrfTokenRepository sessionToken = neWHttpSessionCsrfTokenRepository();
System.out.println("HttpSessionCsrfTokenRepository token = " + sessionToken.loadToken(objHttpRequest)); // Returned **null**
CsrfToken token = (CsrfToken) session.getAttribute("org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN");
System.out.println(">>>>> session token "+ token); // Returned **null**
token = (CsrfToken)objHttpRequest.getAttribute("_csrf");
System.out.println(">>>>> request token "+ token); // Returned token value
Be very careful using session.
The session attribute is set as stated. But it is for the duration of the user interaction. So you don't have any indication on the server side where this came from. So the "bad-guy" code can simply ride on your session and the check will think it is ok.
The token must be part of the form data submitted or as part of the header.
After successful authentication via a form post sign-in, I need to be able to use the same session token within the response to do another post to a protected route, but this time using XMLHttpRequest.
How would I get the session token, considering that the successful authentication response has already passed.
The session token is stored in a laravel_session cookie, assuming default Laravel settings (see config/session.php).
You can read the cookie in javascript using document.cookie. For example:
function readCookie(name)
{
var matches = document.cookie.match('(^|; )'+name+'=([^;]*)');
if (matches) {
return decodeURIComponent(matches[2]);
}
return null;
}
var token = readCookie('laravel_session');
I am building an ASP.NET Web API. I am using YouTube API to upload videos on YouTube. I have managed to implement the OAuth with refresh token flow. After generating a refresh token I am using the following code for all the subsequent calls to YouTube API.
var token = new TokenResponse { RefreshToken = REFRESH_TOKEN };
var credentials = new UserCredential(new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets { ClientId = CLIENT_ID, ClientSecret = CLIENT_SECRET }
}), "user", token);
YouTubeService service = new YouTubeService((new BaseClientService.Initializer()
{
HttpClientInitializer = credentials
}));
I want to know when this refresh token will expire and how I would regenerate this refresh token without any user input/interaction so that end user does not see a Google account selection screen (in my case I see two accounts, a gmail one and a YouTube channel's one).
Also, if I have one refresh token generated, then I do not get a refresh token in response if I try to initiate the OAuth process again by using https://accounts.google.com/o/oauth2/auth again. Can I only have one refresh token at a time?
There are examples, also in the documentation and other similar questions here. However, maybe not in your programming language.
The refreshToken should be saved by you and reused. It is valid until it gets revoked by the user himself. Getting a refreshToken requiers user interaction.
Once you have obtained the refreshToken use it in another request to obtain an accessToken. The accessToken is needed in YouTube API requests for some data access. An accesToken expires after 1 hour (3600 seconds), although this period could be changed in the future.