forked from joscha/play-authenticate
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CookieAuthProvider for Remember Me functionality.
- Loading branch information
Showing
22 changed files
with
486 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
220 changes: 220 additions & 0 deletions
220
code/app/com/feth/play/module/pa/providers/cookie/CookieAuthProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
package com.feth.play.module.pa.providers.cookie; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.UUID; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import play.Application; | ||
import play.Play; | ||
import play.mvc.Http.Context; | ||
import play.mvc.Http.Cookie; | ||
import play.mvc.Http.Cookies; | ||
|
||
import com.feth.play.module.pa.PlayAuthenticate; | ||
import com.feth.play.module.pa.providers.AuthProvider; | ||
import com.feth.play.module.pa.user.AuthUser; | ||
|
||
public abstract class CookieAuthProvider extends AuthProvider { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(CookieAuthProvider.class); | ||
|
||
protected static final String PROVIDER_KEY = "cookie"; | ||
|
||
protected static final String SESSION_KEY_DO_NOT_REMEMBER = "pa.cookie.do_not_remember"; | ||
|
||
private static final String SETTING_KEY_COOKIE_NAME = "cookieName"; | ||
private static final String SETTING_KEY_TIMEOUT = "timeout"; | ||
private static final String SETTING_KEY_SECURE_ONLY = "secureOnly"; | ||
private static final String SETTING_KEY_PATH = "path"; | ||
|
||
@Override | ||
protected List<String> neededSettingKeys() { | ||
return Arrays.asList( | ||
SETTING_KEY_COOKIE_NAME, | ||
SETTING_KEY_TIMEOUT, | ||
SETTING_KEY_SECURE_ONLY, | ||
SETTING_KEY_PATH | ||
); | ||
} | ||
|
||
public CookieAuthProvider(final Application app) { | ||
super(app); | ||
} | ||
|
||
@Override | ||
public String getKey() { | ||
return PROVIDER_KEY; | ||
} | ||
|
||
protected static enum CheckResult { | ||
SUCCESS, | ||
INVALID_TOKEN, | ||
MISSING_SERIES, | ||
EXPIRED, | ||
ERROR | ||
} | ||
|
||
protected abstract CheckResult check(final CookieAuthUser cookieAuthUser); | ||
|
||
protected void save(final CookieAuthUser cookieAuthUser, final AuthUser loginUser) { | ||
PlayAuthenticate.getUserService().link(loginUser, cookieAuthUser); | ||
} | ||
|
||
protected abstract void renew(final CookieAuthUser cookieAuthUser, final String newToken); | ||
|
||
/** | ||
* Deletes an auth cookie. This assumes that any persisted auth cookie | ||
* with the given series (cookieAuthUser.getSeries()) is deleted. | ||
* | ||
* @param cookieAuthUser | ||
*/ | ||
protected void deleteSeries(final CookieAuthUser cookieAuthUser) { | ||
PlayAuthenticate.getUserService().unlink(cookieAuthUser); | ||
} | ||
|
||
protected void potentialTheft(final CookieAuthUser cookieAuthUser) { | ||
log.warn("Potential cookie theft: {}", cookieAuthUser.getId()); | ||
} | ||
|
||
protected String getRandom() { | ||
return UUID.randomUUID().toString(); | ||
} | ||
|
||
protected String getNewSeries() { | ||
return getRandom(); | ||
} | ||
|
||
protected String getNewToken() { | ||
return getRandom(); | ||
} | ||
|
||
protected int getTimeout() { | ||
return getConfiguration().getInt(SETTING_KEY_TIMEOUT); | ||
} | ||
|
||
public String getCookieName() { | ||
return getConfiguration().getString(SETTING_KEY_COOKIE_NAME); | ||
} | ||
|
||
protected boolean isSecureOnly() { | ||
return !Play.isDev() && getConfiguration().getBoolean(SETTING_KEY_SECURE_ONLY); | ||
} | ||
|
||
protected String getPath() { | ||
return getConfiguration().getString(SETTING_KEY_PATH); | ||
} | ||
|
||
public void remember(final Context ctx) { | ||
remember(ctx, PlayAuthenticate.getUser(ctx)); | ||
} | ||
|
||
public void remember(final Context ctx, final AuthUser authUser) { | ||
if (authUser == null) { | ||
return; | ||
} | ||
|
||
if ("true".equals(ctx.session().get(SESSION_KEY_DO_NOT_REMEMBER))) { | ||
ctx.session().remove(SESSION_KEY_DO_NOT_REMEMBER); | ||
ctx.response().discardCookie(getCookieName()); | ||
return; | ||
} | ||
|
||
CookieAuthUser cookieAuthUser = null; | ||
if (authUser instanceof CookieAuthUser) { | ||
final CookieAuthUser oldCookieAuthUser = (CookieAuthUser) authUser; | ||
cookieAuthUser = oldCookieAuthUser.renew(getNewToken()); | ||
renew(oldCookieAuthUser, cookieAuthUser.getToken()); | ||
} else { | ||
cookieAuthUser = new CookieAuthUser(getNewSeries(), getNewToken()); | ||
save(cookieAuthUser, authUser); | ||
} | ||
|
||
ctx.response().setCookie( | ||
getCookieName(), | ||
cookieAuthUser.toCookieValue(), | ||
getTimeout(), // MaxAge in seconds | ||
getPath(), // Path | ||
null, | ||
isSecureOnly(), | ||
true); //httpOnly, not javascript! | ||
} | ||
|
||
public void forget(final Context ctx) { | ||
final AuthUser authUser = PlayAuthenticate.getUser(ctx); | ||
ctx.response().discardCookie(getCookieName()); | ||
if (authUser instanceof CookieAuthUser) { | ||
deleteSeries((CookieAuthUser)authUser); | ||
} | ||
} | ||
|
||
public CookieAuthUser authenticate(final Context ctx) { | ||
return (CookieAuthUser) authenticate(ctx, null); | ||
} | ||
|
||
@Override | ||
public Object authenticate(final Context ctx, final Object payload) { | ||
final Cookies cookies = ctx.request().cookies(); | ||
final Cookie cookie = cookies.get(getCookieName()); | ||
if (cookie == null) { | ||
log.trace("authenticate() - cookie ({}) is null", getCookieName()); | ||
return null; | ||
} | ||
|
||
final CookieAuthUser cookieAuthUser = CookieAuthUser.fromCookieValue(cookie.value()); | ||
if (cookieAuthUser == null) { | ||
log.warn("Discarding marlformed cookie: {}", cookie.value()); | ||
ctx.response().discardCookie(getCookieName()); | ||
return null; | ||
} | ||
|
||
final CheckResult checkResult = check(cookieAuthUser); | ||
|
||
if (null == checkResult || | ||
CheckResult.MISSING_SERIES == checkResult || | ||
CheckResult.EXPIRED == checkResult || | ||
CheckResult.ERROR == checkResult) { | ||
|
||
log.debug("Discarding expired or missing ({}) cookie: {}", | ||
(checkResult == null)? null : checkResult.name(), cookie.value()); | ||
ctx.response().discardCookie(getCookieName()); | ||
return null; | ||
|
||
} else if (CheckResult.SUCCESS == checkResult) { | ||
|
||
log.trace("Renew cookie: {}", cookie.value()); | ||
remember(ctx, cookieAuthUser); | ||
PlayAuthenticate.storeUser(ctx.session(), cookieAuthUser); | ||
return cookieAuthUser; | ||
|
||
} else if (CheckResult.INVALID_TOKEN == checkResult) { | ||
potentialTheft(cookieAuthUser); | ||
deleteSeries(cookieAuthUser); | ||
ctx.response().discardCookie(getCookieName()); | ||
return null; | ||
|
||
} | ||
return null; | ||
} | ||
|
||
public void doNotRemember(Context ctx) { | ||
ctx.session().put(SESSION_KEY_DO_NOT_REMEMBER, "true"); | ||
} | ||
|
||
@Override | ||
public boolean isExternal() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public boolean isManaged() { | ||
return false; | ||
} | ||
|
||
public static CookieAuthProvider getCookieProvider() { | ||
return (CookieAuthProvider) PlayAuthenticate.getProvider(PROVIDER_KEY); | ||
} | ||
|
||
} |
56 changes: 56 additions & 0 deletions
56
code/app/com/feth/play/module/pa/providers/cookie/CookieAuthUser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.feth.play.module.pa.providers.cookie; | ||
|
||
import com.feth.play.module.pa.user.AuthUser; | ||
import com.feth.play.module.pa.user.TokenIdentity; | ||
|
||
public class CookieAuthUser extends AuthUser implements TokenIdentity { | ||
|
||
/** | ||
* | ||
*/ | ||
private static final long serialVersionUID = 1L; | ||
|
||
private final String series; | ||
private final String token; | ||
|
||
public CookieAuthUser(final String series, final String token) { | ||
this.series = series; | ||
this.token = token; | ||
} | ||
|
||
@Override | ||
public String getId() { | ||
return series; | ||
} | ||
|
||
@Override | ||
public String getProvider() { | ||
return CookieAuthProvider.PROVIDER_KEY; | ||
} | ||
|
||
public String getSeries() { | ||
return series; | ||
} | ||
|
||
@Override | ||
public String getToken() { | ||
return token; | ||
} | ||
|
||
public CookieAuthUser renew(final String newToken) { | ||
return new CookieAuthUser(series, newToken); | ||
} | ||
|
||
public String toCookieValue() { | ||
return String.format("%s|%s", series, token); | ||
} | ||
|
||
public static CookieAuthUser fromCookieValue(final String value) { | ||
final String[] fields = value.split("\\|"); | ||
if (fields.length != 2) { | ||
return null; | ||
} | ||
return new CookieAuthUser(fields[0], fields[1]); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.feth.play.module.pa.user; | ||
|
||
public interface TokenIdentity { | ||
|
||
public String getToken(); | ||
|
||
} |
Oops, something went wrong.