From 09ea20140ff160fdd08b11106dffe8750a16e026 Mon Sep 17 00:00:00 2001 From: mcarrolle Date: Thu, 12 Feb 2015 11:33:03 +0100 Subject: [PATCH 1/2] Implements refresh token behaviour for GoogleAuthProvider --- .../feth/play/module/pa/PlayAuthenticate.java | 47 ++++++++++------ .../providers/oauth2/OAuth2AuthProvider.java | 40 +++++++++----- .../oauth2/google/GoogleAuthProvider.java | 55 +++++++++++++++++++ code/conf/reference.conf | 3 +- code/version.sbt | 2 +- 5 files changed, 115 insertions(+), 32 deletions(-) diff --git a/code/app/com/feth/play/module/pa/PlayAuthenticate.java b/code/app/com/feth/play/module/pa/PlayAuthenticate.java index 18ffbfd4..afe20209 100644 --- a/code/app/com/feth/play/module/pa/PlayAuthenticate.java +++ b/code/app/com/feth/play/module/pa/PlayAuthenticate.java @@ -2,6 +2,7 @@ import java.util.Date; +import com.feth.play.module.pa.providers.oauth2.OAuth2AuthProvider; import play.Configuration; import play.Logger; import play.Play; @@ -175,23 +176,35 @@ public static void storeUser(final Session session, final AuthUser authUser) { } } - public static boolean isLoggedIn(final Session session) { - boolean ret = session.containsKey(USER_KEY) // user is set - && session.containsKey(PROVIDER_KEY); // provider is set - ret &= AuthProvider.Registry.hasProvider(session.get(PROVIDER_KEY)); // this - // provider - // is - // active - if (session.containsKey(EXPIRES_KEY)) { - // expiration is set - final long expires = getExpiration(session); - if (expires != AuthUser.NO_EXPIRATION) { - ret &= (new Date()).getTime() < expires; // and the session - // expires after now - } - } - return ret; - } + public static Boolean isLoggedIn(Session session) { + return isLoggedIn(getUser(session),session); + } + + /** + * Check if the specified user is logged in. If session is expired, then a refresh token will be used to update the user token + * + * @param authUser + * @param session + * @return + */ + public static Boolean isLoggedIn(AuthUser authUser, Session session) { + boolean isLogged = false; + AuthProvider provider = getProvider(session.get(PROVIDER_KEY)); + if (authUser != null && provider != null) { + if (authUser.expires() != AuthUser.NO_EXPIRATION) { + boolean isExpired = System.currentTimeMillis() > authUser.expires(); + if (isExpired) { + if(provider instanceof OAuth2AuthProvider){ + // OAuth2AuthProvider implementation can provide refresh token + isLogged = ((OAuth2AuthProvider) provider).refresh(authUser, session) != null; + } + } else { + isLogged = true; + } + } + } + return isLogged; + } public static Result logout(final Session session) { session.remove(USER_KEY); diff --git a/code/app/com/feth/play/module/pa/providers/oauth2/OAuth2AuthProvider.java b/code/app/com/feth/play/module/pa/providers/oauth2/OAuth2AuthProvider.java index 3db6c418..f3725017 100644 --- a/code/app/com/feth/play/module/pa/providers/oauth2/OAuth2AuthProvider.java +++ b/code/app/com/feth/play/module/pa/providers/oauth2/OAuth2AuthProvider.java @@ -1,32 +1,26 @@ package com.feth.play.module.pa.providers.oauth2; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.UUID; - +import com.feth.play.module.pa.PlayAuthenticate; import com.feth.play.module.pa.exceptions.*; +import com.feth.play.module.pa.providers.ext.ExternalAuthProvider; +import com.feth.play.module.pa.user.AuthUser; +import com.feth.play.module.pa.user.AuthUserIdentity; +import com.google.common.base.Strings; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; - import play.Application; import play.Configuration; import play.Logger; import play.i18n.Messages; import play.libs.ws.WS; -import play.libs.ws.WSResponse; import play.libs.ws.WSRequestHolder; -import play.mvc.Http; +import play.libs.ws.WSResponse; import play.mvc.Http.Context; import play.mvc.Http.Request; import play.mvc.Http.Session; -import com.feth.play.module.pa.PlayAuthenticate; -import com.feth.play.module.pa.providers.ext.ExternalAuthProvider; -import com.feth.play.module.pa.user.AuthUser; -import com.feth.play.module.pa.user.AuthUserIdentity; +import java.util.*; public abstract class OAuth2AuthProvider extends ExternalAuthProvider { @@ -52,6 +46,7 @@ protected List neededSettingKeys() { public static abstract class SettingKeys { public static final String AUTHORIZATION_URL = "authorizationUrl"; public static final String ACCESS_TOKEN_URL = "accessTokenUrl"; + public static final String REFRESH_TOKEN_URL = "refreshTokenUrl"; public static final String CLIENT_ID = "clientId"; public static final String CLIENT_SECRET = "clientSecret"; public static final String SCOPE = "scope"; @@ -201,6 +196,13 @@ public Object authenticate(final Context context, final Object payload) } final String code = request.getQueryString(Constants.CODE); final I info = getAccessToken(code, request); + // Store refresh token to cache + String token = info.getRefreshToken(); + Logger.debug("1-add to cache refreshToken=" + token); + if(!Strings.isNullOrEmpty(token)) { + Logger.debug("add to cache refreshToken=" + token); + PlayAuthenticate.storeInCache(context.session(), OAuth2AuthProvider.Constants.REFRESH_TOKEN, token); + } return transform(info, callbackState); } else { // no auth, yet @@ -240,4 +242,16 @@ public void afterSave(final AuthUser user, final Object identity, final Session */ protected abstract AuthUserIdentity transform(final I info, final String state) throws AuthException; + + /** + * Method used to refresh a token when the token was expired. + * + * @param authUser + * @param session + * @return a refreshed authuser or null otherwise + */ + public AuthUser refresh(AuthUser authUser, Session session) { + return null; + } + } diff --git a/code/app/com/feth/play/module/pa/providers/oauth2/google/GoogleAuthProvider.java b/code/app/com/feth/play/module/pa/providers/oauth2/google/GoogleAuthProvider.java index bb29dc75..40c8101d 100644 --- a/code/app/com/feth/play/module/pa/providers/oauth2/google/GoogleAuthProvider.java +++ b/code/app/com/feth/play/module/pa/providers/oauth2/google/GoogleAuthProvider.java @@ -1,14 +1,30 @@ package com.feth.play.module.pa.providers.oauth2.google; +import com.feth.play.module.pa.PlayAuthenticate; +import com.feth.play.module.pa.user.AuthUser; +import com.feth.play.module.pa.user.SessionAuthUser; +import com.google.common.base.Strings; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.entity.ContentType; +import org.apache.http.message.BasicNameValuePair; import play.Application; +import play.Configuration; import play.Logger; import play.libs.ws.WS; +import play.libs.ws.WSRequestHolder; import play.libs.ws.WSResponse; import com.fasterxml.jackson.databind.JsonNode; import com.feth.play.module.pa.exceptions.AccessTokenException; import com.feth.play.module.pa.exceptions.AuthException; import com.feth.play.module.pa.providers.oauth2.OAuth2AuthProvider; +import play.mvc.Http; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; public class GoogleAuthProvider extends OAuth2AuthProvider { @@ -62,4 +78,43 @@ protected GoogleAuthInfo buildInfo(final WSResponse r) } } + @Override + public AuthUser refresh(AuthUser authUser, Http.Session session) { + Configuration config = PlayAuthenticate.getConfiguration().getConfig(PROVIDER_KEY); + String url = config.getString(SettingKeys.REFRESH_TOKEN_URL); + String refreshToken = PlayAuthenticate.getFromCache(session, OAuth2AuthProvider.Constants.REFRESH_TOKEN); + if (Strings.isNullOrEmpty(refreshToken)){ + Logger.error("refresh token not found from cache"); + return null; + } + if(Strings.isNullOrEmpty(url)) { + return null; + } + final List params = new ArrayList(); + params.add(new BasicNameValuePair(OAuth2AuthProvider.Constants.REFRESH_TOKEN, refreshToken)); + params.add(new BasicNameValuePair(OAuth2AuthProvider.Constants.CLIENT_ID, config.getString(OAuth2AuthProvider.SettingKeys.CLIENT_ID))); + params.add(new BasicNameValuePair(OAuth2AuthProvider.Constants.CLIENT_SECRET, config.getString(OAuth2AuthProvider.SettingKeys.CLIENT_SECRET))); + params.add(new BasicNameValuePair(OAuth2AuthProvider.Constants.GRANT_TYPE, "refresh_token")); + + final WSRequestHolder request = WS + .url(url); + request.setContentType(ContentType.APPLICATION_FORM_URLENCODED.toString()); + WSResponse response = request.post(URLEncodedUtils.format(params, Charset.forName("UTF-8"))).get(getTimeout()); + AuthUser refreshedUser = null; + if (response == null ) { + Logger.error("[" + authUser.getId() + "] refresh token | fail | No response received for " + url); + } else if(response.getStatus() != 200) { + Logger.error("[" + authUser.getId() + "] refresh token | fail | HTTP_STATUS="+response.getStatus()+response.getBody()); + } else { + JsonNode payload = response.asJson(); + JsonNode expiresNode = payload.get(OAuth2AuthProvider.Constants.EXPIRES_IN); + if(expiresNode != null) { + long expiresDate = new Date().getTime() + expiresNode.asLong() * 1000; + refreshedUser = new SessionAuthUser(authUser.getProvider(), authUser.getId(), expiresDate); + //update value in session + PlayAuthenticate.storeUser(session, refreshedUser); + } + } + return refreshedUser; + } } diff --git a/code/conf/reference.conf b/code/conf/reference.conf index df81ee98..7f1218b8 100644 --- a/code/conf/reference.conf +++ b/code/conf/reference.conf @@ -177,11 +177,12 @@ play-authenticate { } authorizationUrl="https://accounts.google.com/o/oauth2/auth" accessTokenUrl="https://accounts.google.com/o/oauth2/token" + refreshTokenUrl="https://www.googleapis.com/oauth2/v3/token" userInfoUrl="https://www.googleapis.com/oauth2/v1/userinfo" scope="https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email" # Additional parameters - Read more about them here: https://developers.google.com/accounts/docs/OAuth2WebServer#offline - # accessType="offline" + accessType="offline" # approvalPrompt="force" # Get the credentials here: https://code.google.com/apis/console diff --git a/code/version.sbt b/code/version.sbt index 635d3807..5f2b3485 100644 --- a/code/version.sbt +++ b/code/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.6.9-SNAPSHOT" \ No newline at end of file +version in ThisBuild := "0.6.10-SNAPSHOT" \ No newline at end of file From 80fad34d15254b02ddb167ec84464245023ea8c2 Mon Sep 17 00:00:00 2001 From: mcarrolle Date: Thu, 12 Feb 2015 11:58:38 +0100 Subject: [PATCH 2/2] Remove useless debug trace --- .../play/module/pa/providers/oauth2/OAuth2AuthProvider.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/code/app/com/feth/play/module/pa/providers/oauth2/OAuth2AuthProvider.java b/code/app/com/feth/play/module/pa/providers/oauth2/OAuth2AuthProvider.java index f3725017..cc8dee88 100644 --- a/code/app/com/feth/play/module/pa/providers/oauth2/OAuth2AuthProvider.java +++ b/code/app/com/feth/play/module/pa/providers/oauth2/OAuth2AuthProvider.java @@ -198,9 +198,10 @@ public Object authenticate(final Context context, final Object payload) final I info = getAccessToken(code, request); // Store refresh token to cache String token = info.getRefreshToken(); - Logger.debug("1-add to cache refreshToken=" + token); if(!Strings.isNullOrEmpty(token)) { - Logger.debug("add to cache refreshToken=" + token); + if (Logger.isDebugEnabled()) { + Logger.debug("add to cache refreshToken=" + token); + } PlayAuthenticate.storeInCache(context.session(), OAuth2AuthProvider.Constants.REFRESH_TOKEN, token); } return transform(info, callbackState);