├── LICENSE ├── README.md ├── demo ├── README.md ├── integrated │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── google │ │ │ ├── account │ │ │ ├── User.java │ │ │ ├── UserManager.java │ │ │ ├── filter │ │ │ │ ├── CookieParserFilter.java │ │ │ │ ├── FilterUtil.java │ │ │ │ ├── HasSessionFilter.java │ │ │ │ ├── NoSessionFilter.java │ │ │ │ ├── SessionInfo.java │ │ │ │ └── SessionUtil.java │ │ │ └── internal │ │ │ │ ├── UserImpl.java │ │ │ │ ├── servlet │ │ │ │ ├── LoginServlet.java │ │ │ │ ├── LogoutServlet.java │ │ │ │ ├── SetBigAccountChangeTimeServlet.java │ │ │ │ └── SignupServlet.java │ │ │ │ └── storage │ │ │ │ ├── UserRecord.java │ │ │ │ └── UserStore.java │ │ │ ├── config │ │ │ └── UrlConfig.java │ │ │ ├── util │ │ │ ├── EmailValidator.java │ │ │ ├── EncryptionUtil.java │ │ │ └── JspUtil.java │ │ │ └── websession │ │ │ └── MyAppContextListener.java │ │ ├── resources │ │ └── META-INF │ │ │ ├── jdoconfig.xml │ │ │ └── persistence.xml │ │ └── webapp │ │ ├── WEB-INF │ │ ├── appengine-web.xml │ │ ├── logging.properties │ │ └── web.xml │ │ ├── account │ │ ├── css │ │ │ └── ui.css │ │ ├── image │ │ │ ├── dl_button_left.jpg │ │ │ ├── dl_button_middle.jpg │ │ │ ├── dl_button_right.jpg │ │ │ ├── nophoto.png │ │ │ ├── user1.jpg │ │ │ ├── user2.jpg │ │ │ ├── user3.jpg │ │ │ └── user4.jpg │ │ └── page │ │ │ ├── home.jsp │ │ │ ├── login.jsp │ │ │ └── signup.jsp │ │ ├── favicon.ico │ │ ├── index.jsp │ │ ├── js │ │ ├── indexed-db.js │ │ ├── install.js │ │ └── settings.js │ │ └── sw.js └── original │ ├── pom.xml │ └── src │ └── main │ ├── java │ └── com │ │ └── google │ │ ├── account │ │ ├── User.java │ │ ├── UserManager.java │ │ ├── filter │ │ │ ├── CookieParserFilter.java │ │ │ ├── FilterUtil.java │ │ │ ├── HasSessionFilter.java │ │ │ ├── NoSessionFilter.java │ │ │ ├── SessionInfo.java │ │ │ └── SessionUtil.java │ │ └── internal │ │ │ ├── UserImpl.java │ │ │ ├── servlet │ │ │ ├── LoginServlet.java │ │ │ ├── LogoutServlet.java │ │ │ └── SignupServlet.java │ │ │ └── storage │ │ │ ├── UserRecord.java │ │ │ └── UserStore.java │ │ ├── config │ │ └── UrlConfig.java │ │ └── util │ │ ├── EmailValidator.java │ │ ├── EncryptionUtil.java │ │ └── JspUtil.java │ ├── resources │ └── META-INF │ │ ├── jdoconfig.xml │ │ └── persistence.xml │ └── webapp │ ├── WEB-INF │ ├── appengine-web.xml │ ├── logging.properties │ └── web.xml │ ├── account │ ├── css │ │ └── ui.css │ ├── image │ │ ├── dl_button_left.jpg │ │ ├── dl_button_middle.jpg │ │ ├── dl_button_right.jpg │ │ ├── nophoto.png │ │ ├── user1.jpg │ │ ├── user2.jpg │ │ ├── user3.jpg │ │ └── user4.jpg │ └── page │ │ ├── home.jsp │ │ ├── login.jsp │ │ └── signup.jsp │ ├── favicon.ico │ └── index.jsp └── library ├── js ├── indexed-db.js ├── install.js ├── settings.js └── sw.js ├── pom.xml └── src └── main └── java └── com └── google └── dosidos ├── AppContextListener.java ├── LaunchHook.java ├── Settings.java ├── TokenEndpoint.java ├── TwoTokenHandler.java ├── TwoTokenManager.java ├── data ├── Lat.java ├── OpaqueLat.java ├── SessionState.java ├── TokenRequest.java └── TokenResponse.java └── util ├── CookieUtil.java └── EncryptionUtil.java /README.md: -------------------------------------------------------------------------------- 1 | ##Two-Token Based Web Session Management 2 | 3 | See blog (URL to be added) for more information. 4 | See the [live demo app](https://ws-codelab.appspot.com). 5 | 6 | To deploy your own version web app: 7 | 8 | 1. Download Apache Maven version 3.1 or greater and then install it. 9 | 10 | 2. Follow this [tutorial](https://cloud.google.com/appengine/docs/java/gettingstarted/creating) to create a Google App Engine project, and replace the project id in the `` tag in `demo/integrated/src/main/webapp/WEB-INF/appengine-web.xml`. 11 | 12 | 3. Upload the app to App Engine by running `mvn appengine:update` under `demo/integrated` folder. 13 | 14 | 4. You can verify the different behaviors in Chrome and FireFox: 15 | * Long Lived Session: 16 | * On FireFox, the session will time out soon (every 1 minute, as we set in SessionUtil.SESSION_LIFETIME). 17 | * On Chrome, the session is long lived. And in developer console, you can verify the session cookie value changes every minute. 18 | * Big Account Change: 19 | * On FireFox, no effect for the big account change. 20 | * On Chrome, the session will be terminated by big account change. 21 | 22 | 23 | __This is not an official Google product.__ 24 | 25 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | Two-Token Based Web Session Management 2 | === 3 | 4 | See blog (URL to be added) for more information. 5 | See the [live demo app](https://ws-codelab.appspot.com). 6 | 7 | To deploy your own version web app: 8 | 9 | 1. Download Apache Maven version 3.1 or greater and then install it. 10 | 11 | 2. Follow this [tutorial](https://cloud.google.com/appengine/docs/java/gettingstarted/creating) to create a Google App Engine project, and replace the project id in the tag in src/main/webapp/WEB-INF/appengine-web.xml. 12 | 13 | 3. Upload the app to App Engine by running: mvn appengine:update. 14 | 15 | 4. You can verify the different behaviors in Chrome and FireFox: 16 | * Long Lived Session: 17 | * On FireFox, the session will time out soon (every 1 minute, as we set in SessionUtil.SESSION_LIFETIME). 18 | * On Chrome, the session is long lived. And in developer console, you can verify the session cookie value changes every minute. 19 | * Big Account Change: 20 | * On FireFox, no effect for the big account change. 21 | * On Chrome, the session will be terminated by big account change. 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /demo/integrated/pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 20 | 4.0.0 21 | war 22 | 1.0-SNAPSHOT 23 | com.google.websession 24 | demo-webapp 25 | 26 | 27 | 3.1.0 28 | 29 | 30 | 31 | 1.9.30 32 | 33 | 34 | 35 | 36 | 37 | javax.transaction 38 | jta 39 | 1.1 40 | 41 | 42 | com.google.appengine 43 | appengine-api-1.0-sdk 44 | ${appengine.sdk.version} 45 | 46 | 47 | javax.jdo 48 | jdo-api 49 | [3.0, 4.0) 50 | 51 | 52 | com.google.appengine.orm 53 | datanucleus-appengine 54 | 2.1.2 55 | 56 | 57 | org.datanucleus 58 | datanucleus-core 59 | 3.1.3 60 | runtime 61 | 62 | 63 | org.datanucleus 64 | datanucleus-api-jdo 65 | 3.1.3 66 | 67 | 68 | javax.servlet 69 | servlet-api 70 | 2.5 71 | jar 72 | 73 | 74 | javax.servlet 75 | javax.servlet-api 76 | 3.0.1 77 | 78 | 79 | com.google.guava 80 | guava 81 | 18.0 82 | 83 | 84 | com.google.code.gson 85 | gson 86 | 2.5 87 | 88 | 89 | org.json 90 | json 91 | 20131018 92 | 93 | 94 | com.google 95 | dosidos-lib 96 | 1.0 97 | 98 | 99 | 100 | 101 | ${project.build.directory}/${project.build.finalName}/WEB-INF/classes 102 | 103 | 104 | org.apache.maven.plugins 105 | 3.3 106 | maven-compiler-plugin 107 | 108 | 1.7 109 | 1.7 110 | 111 | 112 | 113 | com.google.appengine 114 | appengine-maven-plugin 115 | ${appengine.sdk.version} 116 | 117 | 118 | org.datanucleus 119 | maven-datanucleus-plugin 120 | 3.1.3 121 | 122 | true 123 | 124 | 125 | 126 | process-classes 127 | 128 | enhance 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/User.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account; 18 | 19 | import java.io.Serializable; 20 | 21 | public interface User extends Serializable { 22 | 23 | long getUid(); 24 | 25 | String getDisplayName(); 26 | 27 | String getEmail(); 28 | 29 | String getPhotoUrl(); 30 | 31 | boolean isTosAccepted(); 32 | 33 | long getLastBigChangeTime(); 34 | } 35 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/UserManager.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account; 18 | 19 | import com.google.account.internal.storage.UserStore; 20 | 21 | public class UserManager { 22 | 23 | public static User getUser(String email) { 24 | return UserStore.getUserByEmail(email); 25 | } 26 | 27 | public static User getUser(long userId) { 28 | return UserStore.getUserById(userId); 29 | } 30 | 31 | public static void setBigAccountChangeTime(User user) { 32 | UserStore.setBigAccountChangeTime(user.getUid()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/filter/CookieParserFilter.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.filter; 18 | 19 | import java.io.IOException; 20 | 21 | import javax.servlet.Filter; 22 | import javax.servlet.FilterChain; 23 | import javax.servlet.FilterConfig; 24 | import javax.servlet.ServletException; 25 | import javax.servlet.ServletRequest; 26 | import javax.servlet.ServletResponse; 27 | import javax.servlet.http.HttpServletRequest; 28 | import javax.servlet.http.HttpServletResponse; 29 | 30 | import com.google.account.User; 31 | import com.google.account.UserManager; 32 | 33 | public class CookieParserFilter implements Filter { 34 | private static final String REQUEST_USER = "user"; 35 | private static final String REQUEST_SESSION_INFO = "session_info"; 36 | 37 | @Override 38 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 39 | throws IOException, ServletException { 40 | SessionInfo sessionInfo = SessionUtil.getSessionInfo( 41 | (HttpServletRequest) request); 42 | if (sessionInfo != null && sessionInfo.getExpiresAt() > System.currentTimeMillis()) { 43 | User user = UserManager.getUser(sessionInfo.getUserId()); 44 | if (user != null) { 45 | request.setAttribute(REQUEST_USER, user); 46 | request.setAttribute(REQUEST_SESSION_INFO, sessionInfo); 47 | } 48 | String requestUri = ((HttpServletRequest) request).getRequestURI(); 49 | if (!requestUri.contains("logout")) { 50 | FilterUtil.refreshSessionCookie( 51 | (HttpServletRequest) request, (HttpServletResponse) response); 52 | } 53 | } 54 | chain.doFilter(request, response); 55 | } 56 | 57 | @Override 58 | public void destroy() { 59 | } 60 | 61 | @Override 62 | public void init(FilterConfig config) throws ServletException { 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/filter/FilterUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.filter; 18 | 19 | import javax.servlet.ServletRequest; 20 | import javax.servlet.http.HttpServletRequest; 21 | import javax.servlet.http.HttpServletResponse; 22 | 23 | import com.google.account.User; 24 | 25 | public class FilterUtil { 26 | private static final String REQUEST_USER = "user"; 27 | private static final String REQUEST_SESSION_INFO = "session_info"; 28 | 29 | public static User getSessionUser(ServletRequest request) { 30 | return (User) request.getAttribute(REQUEST_USER); 31 | } 32 | 33 | public static SessionInfo getSessionInfo(ServletRequest request) { 34 | return (SessionInfo) request.getAttribute(REQUEST_SESSION_INFO); 35 | } 36 | 37 | public static boolean hasSessionUser(HttpServletRequest request) { 38 | return getSessionUser(request) != null; 39 | } 40 | 41 | public static void setSessionCookie( 42 | HttpServletRequest request, 43 | HttpServletResponse response, 44 | SessionInfo session) { 45 | SessionUtil.setSessionCookie(response, request.getServerName(), session); 46 | } 47 | 48 | public static void clearSessionCookie( 49 | HttpServletRequest request, HttpServletResponse response) { 50 | SessionUtil.clearSessionCookie(response, request.getServerName()); 51 | } 52 | 53 | public static void refreshSessionCookie( 54 | HttpServletRequest request, HttpServletResponse response) { 55 | SessionUtil.refreshSessionCookie(request, response, request.getServerName()); 56 | } 57 | 58 | public static void setSessionInfoInRequestAttributeAfterLogin( 59 | ServletRequest request, User user, SessionInfo sessionInfo) { 60 | request.setAttribute(REQUEST_USER, user); 61 | request.setAttribute(REQUEST_SESSION_INFO, sessionInfo); 62 | } 63 | 64 | public static void clearSessionInfoInAttributeAfterLogout(ServletRequest request) { 65 | request.removeAttribute(REQUEST_USER); 66 | request.removeAttribute(REQUEST_SESSION_INFO); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/filter/HasSessionFilter.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.filter; 18 | 19 | import java.io.IOException; 20 | 21 | import javax.servlet.Filter; 22 | import javax.servlet.FilterChain; 23 | import javax.servlet.FilterConfig; 24 | import javax.servlet.ServletException; 25 | import javax.servlet.ServletRequest; 26 | import javax.servlet.ServletResponse; 27 | import javax.servlet.http.HttpServletRequest; 28 | import javax.servlet.http.HttpServletResponse; 29 | 30 | import com.google.config.UrlConfig; 31 | 32 | public class HasSessionFilter implements Filter { 33 | 34 | @Override 35 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 36 | throws IOException, ServletException { 37 | if (!FilterUtil.hasSessionUser((HttpServletRequest) request)) { 38 | ((HttpServletResponse) response).sendRedirect(UrlConfig.LOGIN_PAGE); 39 | } else { 40 | chain.doFilter(request, response); 41 | } 42 | } 43 | 44 | @Override 45 | public void destroy() { 46 | } 47 | 48 | @Override 49 | public void init(FilterConfig config) throws ServletException { 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/filter/NoSessionFilter.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.filter; 18 | 19 | import java.io.IOException; 20 | 21 | import javax.servlet.Filter; 22 | import javax.servlet.FilterChain; 23 | import javax.servlet.FilterConfig; 24 | import javax.servlet.ServletException; 25 | import javax.servlet.ServletRequest; 26 | import javax.servlet.ServletResponse; 27 | import javax.servlet.http.HttpServletRequest; 28 | import javax.servlet.http.HttpServletResponse; 29 | 30 | import com.google.config.UrlConfig; 31 | 32 | public class NoSessionFilter implements Filter { 33 | 34 | @Override 35 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 36 | throws IOException, ServletException { 37 | if (FilterUtil.hasSessionUser((HttpServletRequest) request)) { 38 | ((HttpServletResponse) response).sendRedirect(UrlConfig.HOME_PAGE); 39 | } else { 40 | chain.doFilter(request, response); 41 | } 42 | } 43 | 44 | @Override 45 | public void destroy() { 46 | } 47 | 48 | @Override 49 | public void init(FilterConfig config) throws ServletException { 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/filter/SessionInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.filter; 18 | 19 | import java.io.Serializable; 20 | 21 | public class SessionInfo implements Serializable { 22 | private static final long serialVersionUID = 1L; 23 | private long nonce; 24 | private long userId; 25 | private long expiresAt; 26 | 27 | 28 | public SessionInfo() { 29 | this(0, 0, 0); 30 | } 31 | 32 | public SessionInfo(long userId, long expiresAt, long nonce) { 33 | this.userId = userId; 34 | this.expiresAt = expiresAt; 35 | this.nonce = nonce; 36 | } 37 | 38 | public long getUserId() { 39 | return userId; 40 | } 41 | 42 | public void setUserId(long userId) { 43 | this.userId = userId; 44 | } 45 | 46 | public long getExpiresAt() { 47 | return expiresAt; 48 | } 49 | 50 | public void setExpiresAt(long expiresAt) { 51 | this.expiresAt = expiresAt; 52 | } 53 | 54 | public long getNonce() { 55 | return nonce; 56 | } 57 | 58 | public void setNonce(long nonce) { 59 | this.nonce = nonce; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/filter/SessionUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.filter; 18 | 19 | import javax.servlet.http.Cookie; 20 | import javax.servlet.http.HttpServletRequest; 21 | import javax.servlet.http.HttpServletResponse; 22 | 23 | import com.google.common.base.Strings; 24 | import com.google.gson.Gson; 25 | import com.google.util.EncryptionUtil; 26 | 27 | /** 28 | * Utility methods to handle the cookie. 29 | */ 30 | public class SessionUtil { 31 | private static final String SESSION_COOKIE_NAME = "SID"; 32 | private static final Gson GSON = new Gson(); 33 | public static final int SESSION_LIFETIME = 1 * 60; // in seconds 34 | 35 | public static void setSessionCookie( 36 | HttpServletResponse response, String domain, SessionInfo session) { 37 | String value = EncryptionUtil.encrypt(GSON.toJson(session)); 38 | setSessionCookie( 39 | response, SESSION_COOKIE_NAME, domain, value, SESSION_LIFETIME); 40 | } 41 | 42 | public static void setSessionCookie(HttpServletResponse response, String cookieName, 43 | String domain, String cookieValue, int maxAge) { 44 | Cookie cookie = new Cookie(cookieName, cookieValue); 45 | cookie.setDomain(domain); 46 | maxAge = maxAge > 0 ? maxAge : 0; 47 | cookie.setMaxAge(maxAge); 48 | cookie.setPath("/"); 49 | response.addCookie(cookie); 50 | } 51 | 52 | public static void clearSessionCookie(HttpServletResponse response, String domain) { 53 | clearSessionCookie(response, SESSION_COOKIE_NAME, domain); 54 | } 55 | 56 | public static void clearSessionCookie(HttpServletResponse response, String cookieName, 57 | String domain) { 58 | Cookie cookie = new Cookie(cookieName, ""); 59 | cookie.setMaxAge(0); 60 | cookie.setDomain(domain); 61 | cookie.setPath("/"); 62 | response.addCookie(cookie); 63 | } 64 | 65 | public static void refreshSessionCookie( 66 | HttpServletRequest request, 67 | HttpServletResponse response, 68 | String domain) { 69 | refreshSessionCookie( 70 | request, response, SESSION_COOKIE_NAME, domain, SESSION_LIFETIME); 71 | } 72 | 73 | public static void refreshSessionCookie( 74 | HttpServletRequest request, 75 | HttpServletResponse response, 76 | String cookieName, 77 | String domain, 78 | int maxAge) { 79 | Cookie cookie = getSessionCookie(request, cookieName); 80 | if (cookie != null) { 81 | cookie.setMaxAge(maxAge); 82 | cookie.setDomain(domain); 83 | cookie.setPath("/"); 84 | response.addCookie(cookie); 85 | } 86 | } 87 | 88 | public static SessionInfo getSessionInfo(HttpServletRequest request) { 89 | Cookie cookie = getSessionCookie(request, SESSION_COOKIE_NAME); 90 | if (cookie == null || Strings.isNullOrEmpty(cookie.getValue())) { 91 | return null; 92 | } 93 | return GSON.fromJson( 94 | EncryptionUtil.decrypt(cookie.getValue()), SessionInfo.class); 95 | } 96 | 97 | public static String getSessionCookieValue( 98 | HttpServletRequest request, String cookieName) { 99 | Cookie cookie = getSessionCookie(request, cookieName); 100 | if (cookie != null) { 101 | return cookie.getValue(); 102 | } 103 | return null; 104 | } 105 | 106 | private static Cookie getSessionCookie( 107 | HttpServletRequest request, String cookieName) { 108 | Cookie[] cookies = request.getCookies(); 109 | if (cookies != null) { 110 | for (int i = 0; i < cookies.length; i++) { 111 | if (cookieName.equals(cookies[i].getName())) { 112 | return cookies[i]; 113 | } 114 | } 115 | } 116 | return null; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/internal/UserImpl.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.internal; 18 | 19 | import java.io.Serializable; 20 | 21 | import com.google.account.User; 22 | 23 | public class UserImpl implements User, Serializable { 24 | private static final long serialVersionUID = 1L; 25 | 26 | private long uid; 27 | private String displayName; 28 | private String email; 29 | private String photoUrl; 30 | private boolean tosAccepted; 31 | private long lastBigChangeTime; 32 | 33 | 34 | @Override 35 | public long getUid() { 36 | return uid; 37 | } 38 | 39 | public void setUid(long uid) { 40 | this.uid = uid; 41 | } 42 | 43 | @Override 44 | public String getDisplayName() { 45 | return displayName; 46 | } 47 | 48 | public void setDisplayName(String displayName) { 49 | this.displayName = displayName; 50 | } 51 | 52 | @Override 53 | public String getEmail() { 54 | return email; 55 | } 56 | 57 | public void setEmail(String email) { 58 | this.email = email; 59 | } 60 | 61 | @Override 62 | public String getPhotoUrl() { 63 | return photoUrl; 64 | } 65 | 66 | public void setPhotoUrl(String photoUrl) { 67 | this.photoUrl = photoUrl; 68 | } 69 | 70 | @Override 71 | public boolean isTosAccepted() { 72 | return tosAccepted; 73 | } 74 | 75 | public void setTosAccepted(boolean tosAccepted) { 76 | this.tosAccepted = tosAccepted; 77 | } 78 | 79 | @Override 80 | public long getLastBigChangeTime() { 81 | return lastBigChangeTime; 82 | } 83 | 84 | public void setLastBigChangeTime(long lastBigChangeTime) { 85 | this.lastBigChangeTime = lastBigChangeTime; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/internal/servlet/LoginServlet.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.internal.servlet; 18 | 19 | import java.io.IOException; 20 | import java.util.Random; 21 | 22 | import javax.servlet.RequestDispatcher; 23 | import javax.servlet.ServletException; 24 | import javax.servlet.http.HttpServlet; 25 | import javax.servlet.http.HttpServletRequest; 26 | import javax.servlet.http.HttpServletResponse; 27 | 28 | import com.google.account.filter.FilterUtil; 29 | import com.google.account.filter.SessionInfo; 30 | import com.google.account.filter.SessionUtil; 31 | import com.google.account.internal.UserImpl; 32 | import com.google.account.internal.storage.UserStore; 33 | import com.google.config.UrlConfig; 34 | import com.google.dosidos.AppContextListener; 35 | import com.google.dosidos.TwoTokenManager; 36 | import com.google.dosidos.data.SessionState; 37 | 38 | public class LoginServlet extends HttpServlet { 39 | private static final long serialVersionUID = 1L; 40 | private static Random random = new Random(); 41 | 42 | private TwoTokenManager getTwoTokenManager() { 43 | return (TwoTokenManager) getServletContext().getAttribute( 44 | AppContextListener.ATTR_TWO_TOKEN_MANAGER); 45 | } 46 | 47 | @Override 48 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, 49 | IOException { 50 | doPost(req, resp); 51 | } 52 | 53 | @Override 54 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, 55 | IOException { 56 | String email = req.getParameter("email"); 57 | String password = req.getParameter("password"); 58 | 59 | if (UserStore.login(email, password)) { 60 | UserImpl user = UserStore.getUserByEmail(email); 61 | long expiresAt = System.currentTimeMillis() + SessionUtil.SESSION_LIFETIME * 1000; 62 | SessionInfo sessionInfo = new SessionInfo(user.getUid(), expiresAt, random.nextLong()); 63 | FilterUtil.setSessionCookie(req, resp, sessionInfo); 64 | FilterUtil.setSessionInfoInRequestAttributeAfterLogin(req, user, sessionInfo); 65 | 66 | String uid = String.valueOf(user.getUid()); 67 | TwoTokenManager twoTokenManager = getTwoTokenManager(); 68 | if (twoTokenManager.isEnabled(req.getHeader("User-Agent"), uid)) { 69 | SessionState sessionState = twoTokenManager.generateSessionStateOnLogin( 70 | uid, req.getServerName(), SessionUtil.SESSION_LIFETIME); 71 | // This will trigger the JSP page to install service worker 72 | req.setAttribute("login_session_state", sessionState); 73 | } 74 | 75 | req.getRequestDispatcher(UrlConfig.HOME_PAGE).forward(req, resp); 76 | } else { 77 | req.setAttribute("error", "WRONG_PASSWORD"); 78 | RequestDispatcher dispatcher = getServletContext().getRequestDispatcher( 79 | UrlConfig.LOGIN_PAGE); 80 | dispatcher.forward(req, resp); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/internal/servlet/LogoutServlet.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.internal.servlet; 18 | 19 | import java.io.IOException; 20 | 21 | import javax.servlet.ServletException; 22 | import javax.servlet.http.HttpServlet; 23 | import javax.servlet.http.HttpServletRequest; 24 | import javax.servlet.http.HttpServletResponse; 25 | 26 | import com.google.account.filter.FilterUtil; 27 | import com.google.account.filter.SessionInfo; 28 | import com.google.config.UrlConfig; 29 | 30 | public class LogoutServlet extends HttpServlet { 31 | private static final long serialVersionUID = 1L; 32 | 33 | @Override 34 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, 35 | IOException { 36 | SessionInfo sessionInfo = FilterUtil.getSessionInfo(req); 37 | if (sessionInfo != null) { 38 | FilterUtil.clearSessionInfoInAttributeAfterLogout(req); 39 | } 40 | FilterUtil.clearSessionCookie(req, resp); 41 | resp.sendRedirect(UrlConfig.LOGIN_PAGE); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/internal/servlet/SetBigAccountChangeTimeServlet.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.internal.servlet; 18 | 19 | import java.io.IOException; 20 | 21 | import javax.servlet.ServletException; 22 | import javax.servlet.http.HttpServlet; 23 | import javax.servlet.http.HttpServletRequest; 24 | import javax.servlet.http.HttpServletResponse; 25 | 26 | import com.google.account.User; 27 | import com.google.account.UserManager; 28 | import com.google.account.filter.FilterUtil; 29 | import com.google.config.UrlConfig; 30 | 31 | public class SetBigAccountChangeTimeServlet extends HttpServlet { 32 | private static final long serialVersionUID = 1L; 33 | 34 | @Override 35 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, 36 | IOException { 37 | User user = FilterUtil.getSessionUser(req); 38 | UserManager.setBigAccountChangeTime(user); 39 | resp.sendRedirect(UrlConfig.HOME_PAGE); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/internal/servlet/SignupServlet.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.internal.servlet; 18 | 19 | import java.io.IOException; 20 | import java.util.List; 21 | import java.util.Random; 22 | 23 | import javax.servlet.RequestDispatcher; 24 | import javax.servlet.ServletException; 25 | import javax.servlet.http.HttpServlet; 26 | import javax.servlet.http.HttpServletRequest; 27 | import javax.servlet.http.HttpServletResponse; 28 | 29 | import com.google.account.User; 30 | import com.google.account.filter.FilterUtil; 31 | import com.google.account.filter.SessionInfo; 32 | import com.google.account.filter.SessionUtil; 33 | import com.google.account.internal.storage.UserStore; 34 | import com.google.account.internal.storage.UserStore.SignupError; 35 | import com.google.common.collect.Lists; 36 | import com.google.config.UrlConfig; 37 | import com.google.dosidos.AppContextListener; 38 | import com.google.dosidos.TwoTokenManager; 39 | import com.google.dosidos.data.SessionState; 40 | 41 | public class SignupServlet extends HttpServlet { 42 | private static final long serialVersionUID = 1L; 43 | private static Random random = new Random(); 44 | 45 | private TwoTokenManager getTwoTokenManager() { 46 | return (TwoTokenManager) getServletContext().getAttribute( 47 | AppContextListener.ATTR_TWO_TOKEN_MANAGER); 48 | } 49 | 50 | @Override 51 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, 52 | IOException { 53 | List errors = Lists.newArrayList(); 54 | @SuppressWarnings("unchecked") 55 | User user = UserStore.signup(req.getParameterMap(), req.getRemoteAddr(), errors); 56 | if (user != null) { 57 | long expiresAt = System.currentTimeMillis() + SessionUtil.SESSION_LIFETIME * 1000L; 58 | SessionInfo sessionInfo = new SessionInfo(user.getUid(), expiresAt, random.nextLong()); 59 | FilterUtil.setSessionCookie(req, resp, sessionInfo); 60 | FilterUtil.setSessionInfoInRequestAttributeAfterLogin(req, user, sessionInfo); 61 | 62 | String uid = String.valueOf(user.getUid()); 63 | TwoTokenManager twoTokenManager = getTwoTokenManager(); 64 | if (twoTokenManager.isEnabled(req.getHeader("User-Agent"), uid)) { 65 | SessionState sessionState = twoTokenManager.generateSessionStateOnLogin( 66 | uid, req.getServerName(), SessionUtil.SESSION_LIFETIME); 67 | // This will trigger the JSP page to install service worker 68 | req.setAttribute("login_session_state", sessionState); 69 | } 70 | req.getRequestDispatcher(UrlConfig.HOME_PAGE).forward(req, resp); 71 | } else { 72 | req.setAttribute("errors", errors); 73 | RequestDispatcher dispatcher = 74 | getServletContext().getRequestDispatcher(UrlConfig.SIGNUP_PAGE); 75 | dispatcher.forward(req, resp); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/account/internal/storage/UserRecord.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.internal.storage; 18 | 19 | import javax.jdo.annotations.IdGeneratorStrategy; 20 | import javax.jdo.annotations.IdentityType; 21 | import javax.jdo.annotations.PersistenceCapable; 22 | import javax.jdo.annotations.Persistent; 23 | import javax.jdo.annotations.PrimaryKey; 24 | 25 | /** 26 | * The persistence layer representation for a user. 27 | */ 28 | @PersistenceCapable(identityType = IdentityType.APPLICATION) 29 | class UserRecord { 30 | @PrimaryKey 31 | @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) 32 | private Long id; 33 | 34 | @Persistent 35 | private String email; 36 | 37 | @Persistent 38 | private String displayName; 39 | 40 | @Persistent 41 | private String password; 42 | 43 | @Persistent 44 | private String photoUrl; 45 | 46 | @Persistent 47 | private boolean tosAccepted; 48 | 49 | @Persistent 50 | private long lastBigChangeTime; 51 | 52 | public long getId() { 53 | return id; 54 | } 55 | 56 | public String getDisplayName() { 57 | return displayName; 58 | } 59 | 60 | public void setDisplayName(String displayName) { 61 | this.displayName = displayName; 62 | } 63 | 64 | public String getEmail() { 65 | return email; 66 | } 67 | 68 | public void setEmail(String email) { 69 | this.email = email; 70 | } 71 | 72 | public String getPassword() { 73 | return password; 74 | } 75 | 76 | public void setPassword(String password) { 77 | this.password = password; 78 | } 79 | 80 | public String getPhotoUrl() { 81 | return photoUrl; 82 | } 83 | 84 | public void setPhotoUrl(String photoUrl) { 85 | this.photoUrl = photoUrl; 86 | } 87 | 88 | public boolean isTosAccepted() { 89 | return tosAccepted; 90 | } 91 | 92 | public void setTosAccepted(boolean tosAccepted) { 93 | this.tosAccepted = tosAccepted; 94 | } 95 | 96 | public long getLastBigChangeTime() { 97 | return lastBigChangeTime; 98 | } 99 | 100 | public void setLastBigChangeTime(long lastBigChangeTime) { 101 | this.lastBigChangeTime = lastBigChangeTime; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/config/UrlConfig.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.config; 18 | 19 | public class UrlConfig { 20 | 21 | public static final String SIGNUP_ACTION = "/account/action/signup"; 22 | public static final String SIGNUP_PAGE = "/account/page/signup.jsp"; 23 | public static final String LOGIN_ACTION = "/account/action/login"; 24 | public static final String LOGIN_PAGE = "/account/page/login.jsp"; 25 | public static final String LOGOUT_ACTION = "/account/action/logout"; 26 | public static final String HOME_PAGE = "/account/page/home.jsp"; 27 | public static final String BIG_CHAGNE_ACTION = "/account/action/big"; 28 | } 29 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/util/EmailValidator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.util; 18 | 19 | import java.util.regex.Pattern; 20 | 21 | public class EmailValidator { 22 | private static final Pattern VALID_EMAIL_PATTERN = Pattern.compile( 23 | "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE); 24 | 25 | public static boolean isValid(String email) { 26 | return email != null && VALID_EMAIL_PATTERN.matcher(email).matches(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/util/EncryptionUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.util; 18 | 19 | import java.io.UnsupportedEncodingException; 20 | import java.security.InvalidKeyException; 21 | import java.security.NoSuchAlgorithmException; 22 | import java.security.SecureRandom; 23 | 24 | import javax.crypto.BadPaddingException; 25 | import javax.crypto.Cipher; 26 | import javax.crypto.IllegalBlockSizeException; 27 | import javax.crypto.NoSuchPaddingException; 28 | import javax.crypto.spec.SecretKeySpec; 29 | import javax.xml.bind.DatatypeConverter; 30 | 31 | import com.google.common.io.BaseEncoding; 32 | 33 | public class EncryptionUtil { 34 | private static final String KEY = "80a003040c0807d00f01075090123456"; 35 | 36 | public static String encrypt(String plainText) { 37 | try { 38 | Cipher cipher = Cipher.getInstance("AES"); 39 | cipher.init(Cipher.ENCRYPT_MODE, getAesKey()); 40 | byte[] salt = new byte[8]; 41 | SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); 42 | random.nextBytes(salt); 43 | cipher.update(salt); 44 | byte[] encrypted = cipher.doFinal(plainText.getBytes("ISO-8859-1")); 45 | return BaseEncoding.base64Url().encode(encrypted); 46 | } catch (InvalidKeyException e) { 47 | throw new RuntimeException(e); 48 | } catch (NoSuchAlgorithmException e) { 49 | throw new RuntimeException(e); 50 | } catch (NoSuchPaddingException e) { 51 | throw new RuntimeException(e); 52 | } catch (IllegalBlockSizeException e) { 53 | throw new RuntimeException(e); 54 | } catch (BadPaddingException e) { 55 | throw new RuntimeException(e); 56 | } catch (UnsupportedEncodingException e) { 57 | throw new RuntimeException(e); 58 | } 59 | } 60 | 61 | public static String decrypt(String encryptedText) { 62 | try { 63 | byte[] encrypted = BaseEncoding.base64Url().decode(encryptedText); 64 | Cipher cipher = Cipher.getInstance("AES"); 65 | cipher.init(Cipher.DECRYPT_MODE, getAesKey()); 66 | byte[] plain = cipher.doFinal(encrypted); 67 | if (plain == null || plain.length <= 8) { 68 | throw new RuntimeException("wrong encrypted text."); 69 | } 70 | byte[] data = new byte[plain.length - 8]; 71 | System.arraycopy(plain, 8, data, 0, data.length); 72 | return new String(data, "ISO-8859-1"); 73 | } catch (InvalidKeyException e) { 74 | throw new RuntimeException(e); 75 | } catch (NoSuchAlgorithmException e) { 76 | throw new RuntimeException(e); 77 | } catch (NoSuchPaddingException e) { 78 | throw new RuntimeException(e); 79 | } catch (IllegalBlockSizeException e) { 80 | throw new RuntimeException(e); 81 | } catch (BadPaddingException e) { 82 | throw new RuntimeException(e); 83 | } catch (UnsupportedEncodingException e) { 84 | throw new RuntimeException(e); 85 | } 86 | } 87 | 88 | private static SecretKeySpec getAesKey() { 89 | final byte[] symKeyData = DatatypeConverter.parseHexBinary(KEY); 90 | return new SecretKeySpec(symKeyData, "AES"); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/util/JspUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.util; 18 | 19 | import javax.servlet.http.HttpServletRequest; 20 | 21 | import com.google.common.base.Strings; 22 | 23 | public class JspUtil { 24 | public static boolean isNullOrEmpty(String value) { 25 | return Strings.isNullOrEmpty(value); 26 | } 27 | 28 | public static String nullToEmpty(String value) { 29 | return Strings.isNullOrEmpty(value) ? "" : value; 30 | } 31 | 32 | public static String nullSafeGetParameter(HttpServletRequest request, String parameter) { 33 | return nullToEmpty(request.getParameter(parameter)); 34 | } 35 | 36 | public static String nullSafeGetAttribute(HttpServletRequest request, String parameter) { 37 | return nullToEmpty((String) request.getAttribute(parameter)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /demo/integrated/src/main/java/com/google/websession/MyAppContextListener.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.websession; 18 | 19 | import java.util.Random; 20 | 21 | import javax.servlet.http.HttpServletRequest; 22 | import javax.servlet.http.HttpServletResponse; 23 | 24 | import com.google.account.User; 25 | import com.google.account.UserManager; 26 | import com.google.account.filter.FilterUtil; 27 | import com.google.account.filter.SessionInfo; 28 | import com.google.account.filter.SessionUtil; 29 | import com.google.common.base.Preconditions; 30 | import com.google.dosidos.AppContextListener; 31 | import com.google.dosidos.LaunchHook; 32 | import com.google.dosidos.Settings; 33 | import com.google.dosidos.TwoTokenHandler; 34 | import com.google.dosidos.TwoTokenManager; 35 | 36 | public class MyAppContextListener extends AppContextListener { 37 | 38 | @Override 39 | public TwoTokenManager constructTwoTokenManager() { 40 | Settings settings = new Settings.Builder() 41 | .latLifetime(900) 42 | .aesKey("80a003040c0807d00f01075090123456") 43 | .build(); 44 | 45 | return new TwoTokenManager( 46 | settings, new MyTwoTokenHandler(), new MyLaunchHook()); 47 | } 48 | 49 | private static class MyTwoTokenHandler implements TwoTokenHandler { 50 | private static Random random = new Random(); 51 | 52 | @Override 53 | public boolean hasBigAccountChangeSince(String userId, long issueAt) { 54 | Preconditions.checkArgument(issueAt > 0); 55 | 56 | long id; 57 | try { 58 | id = Long.parseLong(userId); 59 | } catch (NumberFormatException e) { 60 | throw new IllegalArgumentException("Invalid user id."); 61 | } 62 | User user = UserManager.getUser(id); 63 | if (user == null) { 64 | throw new IllegalArgumentException("Invalid user id."); 65 | } 66 | 67 | return user.getLastBigChangeTime() > issueAt; 68 | } 69 | 70 | @Override 71 | public long issueNewSessionCookie(HttpServletRequest req, HttpServletResponse resp, 72 | String userId) { 73 | User user = UserManager.getUser(Long.parseLong(userId)); 74 | long expiresAt = System.currentTimeMillis() + SessionUtil.SESSION_LIFETIME * 1000L; 75 | SessionInfo sessionInfo = new SessionInfo(user.getUid(), expiresAt, random.nextLong()); 76 | FilterUtil.setSessionCookie(req, resp, sessionInfo); 77 | FilterUtil.setSessionInfoInRequestAttributeAfterLogin(req, user, sessionInfo); 78 | return SessionUtil.SESSION_LIFETIME; 79 | } 80 | 81 | @Override 82 | public void clearSessionCookie(HttpServletRequest req, HttpServletResponse resp) { 83 | FilterUtil.clearSessionCookie(req, resp); 84 | } 85 | } 86 | 87 | private static class MyLaunchHook implements LaunchHook { 88 | 89 | @Override 90 | public boolean isEnabledForUserAgent(String userAgent) { 91 | // Enabled for Chrome & FireFox browser 92 | return userAgent.indexOf("Chrome") != -1 || userAgent.indexOf("Firefox") != -1; 93 | } 94 | 95 | @Override 96 | public boolean isEnabledForUser(String userId) { 97 | return true; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /demo/integrated/src/main/resources/META-INF/jdoconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /demo/integrated/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | org.datanucleus.api.jpa.PersistenceProviderImpl 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/WEB-INF/appengine-web.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | ws-codelab 20 | 1 21 | 22 | 25 | true 26 | 27 | 28 | 29 | 30 | 31 | 32 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/WEB-INF/logging.properties: -------------------------------------------------------------------------------- 1 | # A default java.util.logging configuration. 2 | # (All App Engine logging is through java.util.logging by default). 3 | # 4 | # To use this configuration, copy it into your application's WEB-INF 5 | # folder and add the following to your appengine-web.xml: 6 | # 7 | # 8 | # 9 | # 10 | # 11 | 12 | # Set the default logging level for all loggers to WARNING 13 | .level = WARNING 14 | -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 23 | 24 | js 25 | application/x-javascript 26 | 27 | 28 | css 29 | text/css 30 | 31 | 32 | 33 | SignupServlet 34 | com.google.account.internal.servlet.SignupServlet 35 | 36 | 37 | LoginServlet 38 | com.google.account.internal.servlet.LoginServlet 39 | 40 | 41 | LogoutServlet 42 | com.google.account.internal.servlet.LogoutServlet 43 | 44 | 45 | TokenEndpoint 46 | com.google.dosidos.TokenEndpoint 47 | 48 | 49 | SetBigAccountChangeTimeServlet 50 | com.google.account.internal.servlet.SetBigAccountChangeTimeServlet 51 | 52 | 53 | 54 | SignupServlet 55 | /account/action/signup 56 | 57 | 58 | LoginServlet 59 | /account/action/login 60 | 61 | 62 | LogoutServlet 63 | /account/action/logout 64 | 65 | 66 | TokenEndpoint 67 | /token 68 | 69 | 70 | SetBigAccountChangeTimeServlet 71 | /account/action/big 72 | 73 | 74 | 75 | CookieParserFilter 76 | com.google.account.filter.CookieParserFilter 77 | 78 | 79 | HasSessionFilter 80 | com.google.account.filter.HasSessionFilter 81 | 82 | 83 | NoSessionFilter 84 | com.google.account.filter.NoSessionFilter 85 | 86 | 87 | 88 | CookieParserFilter 89 | /account/action/* 90 | /account/page/* 91 | 92 | 93 | 94 | NoSessionFilter 95 | /account/page/login.jsp 96 | /account/page/signup.jsp 97 | /account/action/login 98 | /account/action/signup 99 | 100 | 101 | 102 | HasSessionFilter 103 | /account/action/big 104 | /account/page/home.jsp 105 | 106 | 107 | 108 | com.google.websession.MyAppContextListener 109 | 110 | 111 | 112 | index.jsp 113 | 114 | 115 | 116 | 117 | all 118 | /* 119 | 120 | 121 | CONFIDENTIAL 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/account/css/ui.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | input { 18 | font-size:83%; 19 | } 20 | a { 21 | color:#00c; 22 | } 23 | #login-box { 24 | background-color: #FFFFFF; 25 | border: 4px solid #E8EEFA; 26 | width: 300px; 27 | } 28 | 29 | #login-box-inner { 30 | background-color: #E8EEFA; 31 | border: 1px solid #E8EEFA; 32 | font-family: arial,sans-serif; 33 | font-size: 12px; 34 | font-weight: bold; 35 | margin: 3px; 36 | padding: 10px 20px; 37 | text-align: left; 38 | } 39 | #login-box-inner .error { 40 | color: red; 41 | } 42 | 43 | #login-box-inner .input-box { 44 | width: 240px; 45 | font-size: 14px; 46 | margin-bottom: 4px; 47 | margin-top: 2px; 48 | } 49 | 50 | #login-box-inner .input-button { 51 | cursor: pointer; 52 | font-family: arial,sans-serif; 53 | font-size: 12px; 54 | padding: 0 6px; 55 | margin-bottom: 12px; 56 | margin-top: 5px; 57 | position: relative; 58 | } 59 | #login-box-inner .new-account { 60 | color: #0000CC; 61 | font-size: 11px; 62 | cursor: pointer; 63 | font-size: 12px; 64 | margin-top: 6px; 65 | text-decoration: underline; 66 | } 67 | 68 | #reg_box { 69 | background-color: #FFFFFF; 70 | border: 4px solid #E8EEFA; 71 | width: 480px; 72 | } 73 | 74 | #reg_area { 75 | margin: 4px; 76 | padding: 10px; 77 | background-color: #E8EEFA 78 | } 79 | 80 | #reg_box input { 81 | font-size: 16px; 82 | margin-top: 2px; 83 | padding: 6px; 84 | width: 280px; 85 | } 86 | #reg_box select { 87 | font-size: 13px; 88 | margin-top: 2px; 89 | padding: 6px; 90 | height: 30px; 91 | } 92 | #reg_box td.label { 93 | color: #1D2A5B; 94 | font-size: 13px; 95 | padding-right: 3px; 96 | text-align: right; 97 | width: 105px; 98 | } 99 | .widget-button td.widget-button-right { 100 | background: url("../image/dl_button_right.jpg") repeat scroll 0 0 transparent; 101 | height: 36px; 102 | margin-right: 0; 103 | width: 9px; 104 | } 105 | .widget-button td.widget-button-middle { 106 | background: url("../image/dl_button_middle.jpg") repeat scroll 0 0 transparent; 107 | cursor: pointer; 108 | margin-left: 0; 109 | margin-right: 0; 110 | padding-left: 5px; 111 | padding-right: 5px; 112 | } 113 | .widget-button td.widget-button-left { 114 | background: url("../image/dl_button_left.jpg") repeat scroll 0 0 transparent; 115 | height: 36px; 116 | margin-left: 0; 117 | width: 9px; 118 | } 119 | 120 | .widget-button td.widget-button-middle a.widget-button-link, 121 | .widget-button td.widget-button-middle a.widget-button-link a { 122 | color: #fff; 123 | font-size: 12px; 124 | font-weight: bold; 125 | } 126 | 127 | .widget-error { 128 | background-color: yellow; 129 | color: red; 130 | font-weight: bold; 131 | } 132 | -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/account/image/dl_button_left.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/integrated/src/main/webapp/account/image/dl_button_left.jpg -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/account/image/dl_button_middle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/integrated/src/main/webapp/account/image/dl_button_middle.jpg -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/account/image/dl_button_right.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/integrated/src/main/webapp/account/image/dl_button_right.jpg -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/account/image/nophoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/integrated/src/main/webapp/account/image/nophoto.png -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/account/image/user1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/integrated/src/main/webapp/account/image/user1.jpg -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/account/image/user2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/integrated/src/main/webapp/account/image/user2.jpg -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/account/image/user3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/integrated/src/main/webapp/account/image/user3.jpg -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/account/image/user4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/integrated/src/main/webapp/account/image/user4.jpg -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/account/page/home.jsp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | <%@ page language="java" contentType="text/html; charset=iso-8859-1" %> 18 | <%@ page import="com.google.config.UrlConfig" %> 19 | <%@ page import="com.google.util.JspUtil" %> 20 | <%@ page import="com.google.account.filter.FilterUtil" %> 21 | <%@ page import="com.google.account.User" %> 22 | <%@ page import="com.google.dosidos.data.SessionState" %> 23 | <%@ page import="java.text.DateFormat" %> 24 | <%@ page import="java.util.Date" %> 25 | <% 26 | User user = FilterUtil.getSessionUser(request); 27 | 28 | String lastBigAccountChangeTime = ""; 29 | long time = user.getLastBigChangeTime(); 30 | if (time > 0) { 31 | DateFormat df = DateFormat.getDateTimeInstance(); 32 | lastBigAccountChangeTime = df.format(new Date(time)); 33 | } 34 | 35 | SessionState sessionState = (SessionState) request.getAttribute("login_session_state"); 36 | request.removeAttribute("login_session_state"); 37 | %> 38 | 39 | 40 | Home Page 41 | 42 | 43 | 44 | 45 | 46 | 53 | 54 | 55 |
56 |
57 |
58 | Welcome to <%= request.getServerName() %>, <%= user.getDisplayName() %>! 59 |
60 |
61 | Below are your profile information: 62 |
63 | 64 | 65 | 66 | 67 | 70 | 71 | 72 | 73 | 76 | 77 | 78 | 79 | 82 | 83 | 84 | 85 | 88 | 89 | 90 |
68 | 69 |
74 | 75 |
80 | 81 |
86 | 87 |
91 |

92 | 105 | 108 | 111 |
93 | 94 | 95 | 96 | 97 | 100 | 101 | 102 | 103 |
98 | Refresh Page 99 |
104 |
106 | Or Big Change 107 | 109 | Or Logout 110 |
112 |

113 |
114 | 115 |
116 | <% if (sessionState != null) { %> 117 | 124 | <% } %> 125 | 126 | 127 | -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/account/page/login.jsp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | <%@ page language="java" contentType="text/html; charset=iso-8859-1" %> 18 | <%@ page import="com.google.util.JspUtil" %> 19 | <%@ page import="com.google.config.UrlConfig" %> 20 | <% 21 | String error = JspUtil.nullSafeGetAttribute(request, "error"); 22 | String errorMessage = null; 23 | boolean showError = true; 24 | if ("WRONG_PASSWORD".equals(error)) { 25 | errorMessage = "Wrong email and password combination."; 26 | } else if (!JspUtil.isNullOrEmpty(error)) { 27 | errorMessage = "Unknown error: " + error; 28 | } else { 29 | showError = false; 30 | } 31 | 32 | String email = JspUtil.nullSafeGetParameter(request, "email"); 33 | String app = JspUtil.nullSafeGetParameter(request, "app"); 34 | String continueUrl = JspUtil.nullSafeGetParameter(request, "continueUrl"); 35 | %> 36 | 37 | 38 | Sign In 39 | 40 | 41 | 42 | 43 |

Log in to <%= request.getServerName() %>

44 |
45 |
46 |
47 | 48 | <% if (showError) { %> 49 |
<%= errorMessage %>
50 | <% } %> 51 |

52 | User
53 | 54 |

55 |

Password

56 |

57 |

58 |
59 |
60 |
61 | 62 | 63 | -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/account/page/signup.jsp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | <%@ page language="java" contentType="text/html; charset=iso-8859-1" %> 18 | <%@ page import="com.google.account.internal.storage.UserStore.SignupError" %> 19 | <%@ page import="com.google.config.UrlConfig" %> 20 | <%@ page import="com.google.util.JspUtil" %> 21 | <%@ page import="java.util.Collections" %> 22 | <%@ page import="java.util.List" %> 23 | <% 24 | List errors = (List) request.getAttribute("errors"); 25 | if (errors == null) { 26 | errors = Collections.emptyList(); 27 | } 28 | String email = JspUtil.nullSafeGetParameter(request, "email"); 29 | String displayName = JspUtil.nullSafeGetParameter(request, "displayName"); 30 | String photoUrl = JspUtil.nullSafeGetParameter(request, "photoUrl"); 31 | %> 32 | 33 | 34 | Sign Up 35 | 36 | 37 | 38 | 39 |
40 |
41 |
42 |
43 | Welcome to <%= request.getServerName() %>! 44 |
45 |
46 | Please provide follow information to create your account. 47 |
48 | 49 | 50 | 51 | 52 | 60 | 61 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 81 | 87 | 88 | 89 | 90 | 96 | 97 | 98 |
53 | 54 | <% if (errors.contains(SignupError.INVALID_EMAIL)) { %> 55 |
Invalid email address.
56 | <% } else if (errors.contains(SignupError.EMAIL_REGISTERED)) { %> 57 |
Email already registered.
58 | <% } %> 59 |
64 | 65 | <% if (errors.contains(SignupError.EMPTY_DISPLAY_NAME)) { %> 66 |
Display Name cannot be empty.
67 | <% } %> 68 |
73 | 74 | 75 | 76 | 77 |
82 | 83 | <% if (errors.contains(SignupError.INVALID_PASSWORD)) { %> 84 |
The password length cannot be less than 3.
85 | <% } %> 86 |
91 | 92 | <% if (errors.contains(SignupError.CONFIRM_MISMATCH)) { %> 93 |
Your passwords do not match.
94 | <% } %> 95 |
99 |

100 | 113 | 116 |
101 | 102 | 103 | 104 | 105 | 108 | 109 | 110 | 111 |
106 | Create Account 107 |
112 |
114 | Or Cancel 115 |
117 |

118 |
119 |
120 |
121 | 122 | 123 | -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/integrated/src/main/webapp/favicon.ico -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | <%@ page language="java" contentType="text/html; charset=iso-8859-1" %> 18 | <%@ page import="com.google.config.UrlConfig" %> 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/js/indexed-db.js: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | /** 18 | * @fileoverview Utility methods to manage session state in the Storage layer. 19 | */ 20 | 21 | /** 22 | * Namespace for two-token based web session management JavaScript codes. 23 | */ 24 | var dosidos = dosidos || {}; 25 | 26 | /** 27 | * Namespace for storage layer. 28 | */ 29 | dosidos.store = dosidos.store || {}; 30 | 31 | /** 32 | * Indexed DB store name. 33 | */ 34 | dosidos.store.INDEXED_DB_STORE_ = 'two_token_web_session'; 35 | 36 | /** 37 | * Initializes the underline storage. Currently indexedDB is used. 38 | * The readXXX()/saveXXX() methods can be called only after this method invoked. 39 | */ 40 | dosidos.store.init_ = function() { 41 | return new Promise(function(resolve, reject) { 42 | if (dosidos.store.db_) { 43 | // Already initialized. 44 | resolve(); 45 | } else { 46 | var request = indexedDB.open(dosidos.store.INDEXED_DB_STORE_); 47 | request.onsuccess = function(event) { 48 | dosidos.store.db_ = event.target.result; 49 | // Generic error handler 50 | dosidos.store.db_.onerror = function(event) { 51 | console.log("IndexedDB error: " + event.target.errorCode); 52 | }; 53 | console.log("IndexedDB successfully initialized."); 54 | resolve(); 55 | }; 56 | request.onupgradeneeded = function(event) { 57 | var db = event.target.result; 58 | db.createObjectStore(dosidos.store.INDEXED_DB_STORE_); 59 | }; 60 | request.onerror = function(event) { 61 | console.log("Failed to initilaize IndexedDB."); 62 | reject(event); 63 | }; 64 | } 65 | }); 66 | }; 67 | 68 | 69 | /** 70 | * Reads value from indexed DB. 71 | */ 72 | dosidos.store.read_ = function(key) { 73 | return new Promise(function(resolve, reject) { 74 | var store = dosidos.store.db_ 75 | .transaction(dosidos.store.INDEXED_DB_STORE_, 'readwrite') 76 | .objectStore(dosidos.store.INDEXED_DB_STORE_); 77 | var request = store.get(key); 78 | request.onsuccess = function(event) { 79 | resolve(request.result); 80 | }; 81 | request.onerror = function(event) { 82 | console.log("Failed to read IndexedDB."); 83 | reject(event); 84 | }; 85 | }); 86 | }; 87 | 88 | /** 89 | * Saves value into indexed DB. If value is null or undefined, the corresponding 90 | * key will be removed. 91 | */ 92 | dosidos.store.save_ = function(key, value) { 93 | return new Promise(function(resolve, reject) { 94 | var store = dosidos.store.db_ 95 | .transaction(dosidos.store.INDEXED_DB_STORE_, 'readwrite') 96 | .objectStore(dosidos.store.INDEXED_DB_STORE_); 97 | if (value === undefined || value === null) { 98 | store['delete'](key); 99 | } else { 100 | store.put(value, key); 101 | } 102 | store.transaction.oncomplete = function() { 103 | resolve(); 104 | } 105 | }); 106 | }; 107 | 108 | /* 109 | * Data structure for two-token web session status information. 110 | * @typedef {{ 111 | * active: (boolean|undefined), 112 | * lat: (string|undefined), 113 | * satExpires: (number|undefined) 114 | * }} 115 | */ 116 | dosidos.SessionState; 117 | 118 | /** 119 | * The indexedDb key name for the session status. 120 | */ 121 | dosidos.store.KEY_SESSION_STATE_ = 'two_token_session_state'; 122 | 123 | /** 124 | * Reads next token refresh time. 125 | */ 126 | dosidos.store.readSessionState = function() { 127 | return dosidos.store.init_() 128 | .then(function() { 129 | return dosidos.store.read_(dosidos.store.KEY_SESSION_STATE_); 130 | }) 131 | .then(function(value) { 132 | return (value && JSON.parse(value)) || {}; 133 | }); 134 | }; 135 | 136 | dosidos.store.saveSessionState = function(json) { 137 | return dosidos.store.init_() 138 | .then(function() { 139 | var str = json && JSON.stringify(json); 140 | return dosidos.store.save_(dosidos.store.KEY_SESSION_STATE_, str); 141 | }); 142 | }; 143 | 144 | dosidos.store.changeSatExpires = function(satExpires) { 145 | satExpires = satExpires > 0 ? satExpires : 0; 146 | return dosidos.store.readSessionState() 147 | .then(function(sessionState) { 148 | if (sessionState && sessionState['enabled']) { 149 | sessionState['satExpires'] = satExpires; 150 | return dosidos.store.saveSessionState(sessionState); 151 | } 152 | }); 153 | }; 154 | 155 | dosidos.store.clear = function() { 156 | return dosidos.store.saveSessionState(undefined); 157 | }; 158 | 159 | dosidos.store.suspend = function() { 160 | return dosidos.store.readSessionState().then(function(sessionState) { 161 | if (sessionState) { 162 | sessionState['enabled'] = false; 163 | return dosidos.store.saveSessionState(sessionStatee); 164 | } 165 | }); 166 | }; 167 | 168 | dosidos.store.restart = function(opt_sessionState) { 169 | return new Promise(function(resolve, reject) { 170 | if (opt_sessionState && opt_sessionState['lat']) { 171 | // Has new lat, just overwrite old sessionState. 172 | var sessionState = { 173 | 'enabled': true, 174 | 'lat': opt_sessionState['lat'], 175 | 'satExpires': opt_sessionState['satExpires'] 176 | }; 177 | return dosidos.store.saveSessionState(sessionState); 178 | } 179 | 180 | dosidos.store.readSessionState().then(function(sessionState) { 181 | if (sessionState && sessionState['lat']) { 182 | sessionState['enabled'] = true; 183 | if (opt_sessionState && opt_sessionState['satExpires']) { 184 | sessionState['satExpires'] = opt_sessionState['satExpires']; 185 | } 186 | return dosidos.store.saveSessionState(sessionState); 187 | } 188 | }); 189 | }); 190 | }; 191 | -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/js/install.js: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | /** 18 | * @fileoverview Utility method to install service worker in post login page. 19 | */ 20 | 21 | /** 22 | * Installs/Reinstalls the service worker. 23 | * @param swUrl The URL of the service worker. 24 | * @param swScope The Scope of the service worker. 25 | * @param lat The long live auth token. 26 | * @param satLifetime The life time of the short lived token. 27 | */ 28 | dosidos.installServiceWorker = function(swUrl, swScope, lat, satLifetime) { 29 | if (!('serviceWorker' in navigator)) { 30 | console.log('Browser doesn\'t support service worker'); 31 | return; 32 | } 33 | 34 | navigator.serviceWorker.register(swUrl, {scope: swScope}) 35 | .then(function(registration) { 36 | console.log('Service Worker is successfully registrated.'); 37 | var satExpires = Date.now() + satLifetime * 1000 - 38 | dosidos.settings.TIME_BUFFER_MS; 39 | var sessionState = { 40 | 'lat': lat, 41 | 'satExpires': satExpires 42 | }; 43 | dosidos.store.restart(sessionState).then(function() { 44 | console.log('Service Worker is restarted.'); 45 | }); 46 | }).catch(function(err) { 47 | console.log('ServiceWorker registration failed: ' + err); 48 | }); 49 | }; 50 | -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/js/settings.js: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | /** 18 | * @fileoverview The token configuration parameters. 19 | */ 20 | 21 | /** 22 | * Name space for configuration parameters. 23 | */ 24 | dosidos.settings = dosidos.settings || {}; 25 | 26 | /** 27 | * The URL of the token end point. 28 | */ 29 | dosidos.settings.TOKEN_ENDPOINT_URL = '/token'; 30 | 31 | /** 32 | * The HTTP header name for the LAT when accessing token end point. 33 | */ 34 | dosidos.settings.HTTP_HEADER_LAT = 'X-LAT'; 35 | 36 | /** 37 | * Sets a shorter life time intentionally to allow time differences between 38 | * the server and the user agent. In milliseconds. 39 | */ 40 | dosidos.settings.TIME_BUFFER_MS = 10 * 1000; //5 * 60 * 1000; 41 | 42 | -------------------------------------------------------------------------------- /demo/integrated/src/main/webapp/sw.js: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | /** 18 | * @fileoverview The session management service worker. 19 | */ 20 | 21 | importScripts('/js/indexed-db.js'); 22 | importScripts('/js/settings.js'); 23 | 24 | dosidos.refreshSat = function(lat) { 25 | 26 | // To mitigate XSRF, LAT need to be sent in the HTTP header. 27 | var myHeaders = new Headers(); 28 | myHeaders.set(dosidos.settings.HTTP_HEADER_LAT, lat); 29 | var options = { 30 | method: 'GET', 31 | headers: myHeaders, 32 | credentials: 'include' 33 | }; 34 | 35 | var url = dosidos.settings.TOKEN_ENDPOINT_URL + '?action=REFRESH_BY_LAT'; 36 | return fetch(url, options) 37 | .then(function(data) { 38 | return data.json(); 39 | }).then(function(response) { 40 | console.log('Token response returned: ' 41 | + response && JSON.stringify(response)); 42 | if (!response || response['result'] == 'ERROR') { 43 | if (response && response['error']) { 44 | console.log('Error token response received: ' + response['error']); 45 | } 46 | } else if (response['result'] == 'END') { 47 | console.log('Token refreshing stopped.'); 48 | return dosidos.store.clear(); 49 | } else if (response['result'] == 'REFRESHED') { 50 | var satExpires = Date.now() + response['satLifetime'] * 1000 - 51 | dosidos.settings.TIME_BUFFER_MS; 52 | console.log('Next token refresh in ' 53 | + ((satExpires - Date.now()) / 1000) + ' seconds.'); 54 | return dosidos.store.changeSatExpires(satExpires); 55 | } else { 56 | console.log('Unknow token response type.'); 57 | } 58 | }); 59 | }; 60 | 61 | 62 | /** 63 | * Checks if SAT is expired or not, for an authenticated web session. 64 | */ 65 | dosidos.check = function() { 66 | return dosidos.store.readSessionState().then(function (state) { 67 | if (state && state['enabled'] && state['satExpires'] <= Date.now()) { 68 | return dosidos.refreshSat(state['lat']); 69 | } 70 | }); 71 | }; 72 | 73 | /** 74 | * Adds listener to intercept all web requests. 75 | */ 76 | self.addEventListener('fetch', function(event) { 77 | var req = event.request; 78 | var doFetch = function() { 79 | return fetch(req); 80 | }; 81 | event.respondWith(dosidos.check().then(doFetch, doFetch)); 82 | }); 83 | 84 | self.addEventListener('install', function(event) { 85 | // Bypass the waiting lifecycle stage, 86 | // just in case there's an older version of this SW registration. 87 | event.waitUntil(self.skipWaiting()); 88 | }); 89 | 90 | self.addEventListener('activate', function(event) { 91 | // Take control of all pages under this SW's scope immediately, 92 | // instead of waiting for reload/navigation. 93 | event.waitUntil(self.clients.claim()); 94 | }); 95 | -------------------------------------------------------------------------------- /demo/original/pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 20 | 4.0.0 21 | war 22 | 1.0-SNAPSHOT 23 | com.google.websession 24 | demo-webapp 25 | 26 | 27 | 3.1.0 28 | 29 | 30 | 31 | 1.9.30 32 | 33 | 34 | 35 | 36 | 37 | javax.transaction 38 | jta 39 | 1.1 40 | 41 | 42 | com.google.appengine 43 | appengine-api-1.0-sdk 44 | ${appengine.sdk.version} 45 | 46 | 47 | javax.jdo 48 | jdo-api 49 | [3.0, 4.0) 50 | 51 | 52 | com.google.appengine.orm 53 | datanucleus-appengine 54 | 2.1.2 55 | 56 | 57 | org.datanucleus 58 | datanucleus-core 59 | 3.1.3 60 | runtime 61 | 62 | 63 | org.datanucleus 64 | datanucleus-api-jdo 65 | 3.1.3 66 | 67 | 68 | javax.servlet 69 | servlet-api 70 | 2.5 71 | jar 72 | 73 | 74 | javax.servlet 75 | javax.servlet-api 76 | 3.0.1 77 | 78 | 79 | com.google.guava 80 | guava 81 | 18.0 82 | 83 | 84 | com.google.code.gson 85 | gson 86 | 2.5 87 | 88 | 89 | org.json 90 | json 91 | 20131018 92 | 93 | 94 | 95 | 96 | ${project.build.directory}/${project.build.finalName}/WEB-INF/classes 97 | 98 | 99 | org.apache.maven.plugins 100 | 3.3 101 | maven-compiler-plugin 102 | 103 | 1.7 104 | 1.7 105 | 106 | 107 | 108 | com.google.appengine 109 | appengine-maven-plugin 110 | ${appengine.sdk.version} 111 | 112 | 113 | org.datanucleus 114 | maven-datanucleus-plugin 115 | 3.1.3 116 | 117 | true 118 | 119 | 120 | 121 | process-classes 122 | 123 | enhance 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/User.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account; 18 | 19 | import java.io.Serializable; 20 | 21 | public interface User extends Serializable { 22 | 23 | long getUid(); 24 | 25 | String getDisplayName(); 26 | 27 | String getEmail(); 28 | 29 | String getPhotoUrl(); 30 | 31 | boolean isTosAccepted(); 32 | } 33 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/UserManager.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account; 18 | 19 | import com.google.account.internal.storage.UserStore; 20 | 21 | public class UserManager { 22 | 23 | public static User getUser(String email) { 24 | return UserStore.getUserByEmail(email); 25 | } 26 | 27 | public static User getUser(long userId) { 28 | return UserStore.getUserById(userId); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/filter/CookieParserFilter.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.filter; 18 | 19 | import java.io.IOException; 20 | 21 | import javax.servlet.Filter; 22 | import javax.servlet.FilterChain; 23 | import javax.servlet.FilterConfig; 24 | import javax.servlet.ServletException; 25 | import javax.servlet.ServletRequest; 26 | import javax.servlet.ServletResponse; 27 | import javax.servlet.http.HttpServletRequest; 28 | import javax.servlet.http.HttpServletResponse; 29 | 30 | import com.google.account.User; 31 | import com.google.account.UserManager; 32 | 33 | public class CookieParserFilter implements Filter { 34 | private static final String REQUEST_USER = "user"; 35 | private static final String REQUEST_SESSION_INFO = "session_info"; 36 | 37 | @Override 38 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 39 | throws IOException, ServletException { 40 | SessionInfo sessionInfo = SessionUtil.getSessionInfo( 41 | (HttpServletRequest) request); 42 | if (sessionInfo != null && sessionInfo.getExpiresAt() > System.currentTimeMillis()) { 43 | User user = UserManager.getUser(sessionInfo.getUserId()); 44 | if (user != null) { 45 | request.setAttribute(REQUEST_USER, user); 46 | request.setAttribute(REQUEST_SESSION_INFO, sessionInfo); 47 | } 48 | String requestUri = ((HttpServletRequest) request).getRequestURI(); 49 | if (!requestUri.contains("logout")) { 50 | FilterUtil.refreshSessionCookie( 51 | (HttpServletRequest) request, (HttpServletResponse) response); 52 | } 53 | } 54 | chain.doFilter(request, response); 55 | } 56 | 57 | @Override 58 | public void destroy() { 59 | } 60 | 61 | @Override 62 | public void init(FilterConfig config) throws ServletException { 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/filter/FilterUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.filter; 18 | 19 | import javax.servlet.ServletRequest; 20 | import javax.servlet.http.HttpServletRequest; 21 | import javax.servlet.http.HttpServletResponse; 22 | 23 | import com.google.account.User; 24 | 25 | public class FilterUtil { 26 | private static final String REQUEST_USER = "user"; 27 | private static final String REQUEST_SESSION_INFO = "session_info"; 28 | 29 | public static User getSessionUser(ServletRequest request) { 30 | return (User) request.getAttribute(REQUEST_USER); 31 | } 32 | 33 | public static SessionInfo getSessionInfo(ServletRequest request) { 34 | return (SessionInfo) request.getAttribute(REQUEST_SESSION_INFO); 35 | } 36 | 37 | public static boolean hasSessionUser(HttpServletRequest request) { 38 | return getSessionUser(request) != null; 39 | } 40 | 41 | public static void setSessionCookie( 42 | HttpServletRequest request, 43 | HttpServletResponse response, 44 | SessionInfo session) { 45 | SessionUtil.setSessionCookie(response, request.getServerName(), session); 46 | } 47 | 48 | public static void clearSessionCookie( 49 | HttpServletRequest request, HttpServletResponse response) { 50 | SessionUtil.clearSessionCookie(response, request.getServerName()); 51 | } 52 | 53 | public static void refreshSessionCookie( 54 | HttpServletRequest request, HttpServletResponse response) { 55 | SessionUtil.refreshSessionCookie(request, response, request.getServerName()); 56 | } 57 | 58 | public static void setSessionInfoInRequestAttributeAfterLogin( 59 | ServletRequest request, User user, SessionInfo sessionInfo) { 60 | request.setAttribute(REQUEST_USER, user); 61 | request.setAttribute(REQUEST_SESSION_INFO, sessionInfo); 62 | } 63 | 64 | public static void clearSessionInfoInAttributeAfterLogout(ServletRequest request) { 65 | request.removeAttribute(REQUEST_USER); 66 | request.removeAttribute(REQUEST_SESSION_INFO); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/filter/HasSessionFilter.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.filter; 18 | 19 | import java.io.IOException; 20 | 21 | import javax.servlet.Filter; 22 | import javax.servlet.FilterChain; 23 | import javax.servlet.FilterConfig; 24 | import javax.servlet.ServletException; 25 | import javax.servlet.ServletRequest; 26 | import javax.servlet.ServletResponse; 27 | import javax.servlet.http.HttpServletRequest; 28 | import javax.servlet.http.HttpServletResponse; 29 | 30 | import com.google.config.UrlConfig; 31 | 32 | public class HasSessionFilter implements Filter { 33 | 34 | @Override 35 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 36 | throws IOException, ServletException { 37 | if (!FilterUtil.hasSessionUser((HttpServletRequest) request)) { 38 | ((HttpServletResponse) response).sendRedirect(UrlConfig.LOGIN_PAGE); 39 | } else { 40 | chain.doFilter(request, response); 41 | } 42 | } 43 | 44 | @Override 45 | public void destroy() { 46 | } 47 | 48 | @Override 49 | public void init(FilterConfig config) throws ServletException { 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/filter/NoSessionFilter.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.filter; 18 | 19 | import java.io.IOException; 20 | 21 | import javax.servlet.Filter; 22 | import javax.servlet.FilterChain; 23 | import javax.servlet.FilterConfig; 24 | import javax.servlet.ServletException; 25 | import javax.servlet.ServletRequest; 26 | import javax.servlet.ServletResponse; 27 | import javax.servlet.http.HttpServletRequest; 28 | import javax.servlet.http.HttpServletResponse; 29 | 30 | import com.google.config.UrlConfig; 31 | 32 | public class NoSessionFilter implements Filter { 33 | 34 | @Override 35 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 36 | throws IOException, ServletException { 37 | if (FilterUtil.hasSessionUser((HttpServletRequest) request)) { 38 | ((HttpServletResponse) response).sendRedirect(UrlConfig.HOME_PAGE); 39 | } else { 40 | chain.doFilter(request, response); 41 | } 42 | } 43 | 44 | @Override 45 | public void destroy() { 46 | } 47 | 48 | @Override 49 | public void init(FilterConfig config) throws ServletException { 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/filter/SessionInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.filter; 18 | 19 | import java.io.Serializable; 20 | 21 | public class SessionInfo implements Serializable { 22 | private static final long serialVersionUID = 1L; 23 | private long nonce; 24 | private long userId; 25 | private long expiresAt; 26 | 27 | 28 | public SessionInfo() { 29 | this(0, 0, 0); 30 | } 31 | 32 | public SessionInfo(long userId, long expiresAt, long nonce) { 33 | this.userId = userId; 34 | this.expiresAt = expiresAt; 35 | this.nonce = nonce; 36 | } 37 | 38 | public long getUserId() { 39 | return userId; 40 | } 41 | 42 | public void setUserId(long userId) { 43 | this.userId = userId; 44 | } 45 | 46 | public long getExpiresAt() { 47 | return expiresAt; 48 | } 49 | 50 | public void setExpiresAt(long expiresAt) { 51 | this.expiresAt = expiresAt; 52 | } 53 | 54 | public long getNonce() { 55 | return nonce; 56 | } 57 | 58 | public void setNonce(long nonce) { 59 | this.nonce = nonce; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/filter/SessionUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.filter; 18 | 19 | import javax.servlet.http.Cookie; 20 | import javax.servlet.http.HttpServletRequest; 21 | import javax.servlet.http.HttpServletResponse; 22 | 23 | import com.google.common.base.Strings; 24 | import com.google.gson.Gson; 25 | import com.google.util.EncryptionUtil; 26 | 27 | /** 28 | * Utility methods to handle the cookie. 29 | */ 30 | public class SessionUtil { 31 | private static final String SESSION_COOKIE_NAME = "SID"; 32 | private static final Gson GSON = new Gson(); 33 | public static final int SESSION_LIFETIME = 1 * 60; // in seconds 34 | 35 | public static void setSessionCookie( 36 | HttpServletResponse response, String domain, SessionInfo session) { 37 | String value = EncryptionUtil.encrypt(GSON.toJson(session)); 38 | setSessionCookie( 39 | response, SESSION_COOKIE_NAME, domain, value, SESSION_LIFETIME); 40 | } 41 | 42 | public static void setSessionCookie(HttpServletResponse response, String cookieName, 43 | String domain, String cookieValue, int maxAge) { 44 | Cookie cookie = new Cookie(cookieName, cookieValue); 45 | cookie.setDomain(domain); 46 | maxAge = maxAge > 0 ? maxAge : 0; 47 | cookie.setMaxAge(maxAge); 48 | cookie.setPath("/"); 49 | response.addCookie(cookie); 50 | } 51 | 52 | public static void clearSessionCookie(HttpServletResponse response, String domain) { 53 | clearSessionCookie(response, SESSION_COOKIE_NAME, domain); 54 | } 55 | 56 | public static void clearSessionCookie(HttpServletResponse response, String cookieName, 57 | String domain) { 58 | Cookie cookie = new Cookie(cookieName, ""); 59 | cookie.setMaxAge(0); 60 | cookie.setDomain(domain); 61 | cookie.setPath("/"); 62 | response.addCookie(cookie); 63 | } 64 | 65 | public static void refreshSessionCookie( 66 | HttpServletRequest request, 67 | HttpServletResponse response, 68 | String domain) { 69 | refreshSessionCookie( 70 | request, response, SESSION_COOKIE_NAME, domain, SESSION_LIFETIME); 71 | } 72 | 73 | public static void refreshSessionCookie( 74 | HttpServletRequest request, 75 | HttpServletResponse response, 76 | String cookieName, 77 | String domain, 78 | int maxAge) { 79 | Cookie cookie = getSessionCookie(request, cookieName); 80 | if (cookie != null) { 81 | cookie.setMaxAge(maxAge); 82 | cookie.setDomain(domain); 83 | cookie.setPath("/"); 84 | response.addCookie(cookie); 85 | } 86 | } 87 | 88 | public static SessionInfo getSessionInfo(HttpServletRequest request) { 89 | Cookie cookie = getSessionCookie(request, SESSION_COOKIE_NAME); 90 | if (cookie == null || Strings.isNullOrEmpty(cookie.getValue())) { 91 | return null; 92 | } 93 | return GSON.fromJson( 94 | EncryptionUtil.decrypt(cookie.getValue()), SessionInfo.class); 95 | } 96 | 97 | public static String getSessionCookieValue( 98 | HttpServletRequest request, String cookieName) { 99 | Cookie cookie = getSessionCookie(request, cookieName); 100 | if (cookie != null) { 101 | return cookie.getValue(); 102 | } 103 | return null; 104 | } 105 | 106 | private static Cookie getSessionCookie( 107 | HttpServletRequest request, String cookieName) { 108 | Cookie[] cookies = request.getCookies(); 109 | if (cookies != null) { 110 | for (int i = 0; i < cookies.length; i++) { 111 | if (cookieName.equals(cookies[i].getName())) { 112 | return cookies[i]; 113 | } 114 | } 115 | } 116 | return null; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/internal/UserImpl.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.internal; 18 | 19 | import java.io.Serializable; 20 | 21 | import com.google.account.User; 22 | 23 | public class UserImpl implements User, Serializable { 24 | private static final long serialVersionUID = 1L; 25 | 26 | private long uid; 27 | private String displayName; 28 | private String email; 29 | private String photoUrl; 30 | private boolean tosAccepted; 31 | 32 | @Override 33 | public long getUid() { 34 | return uid; 35 | } 36 | 37 | public void setUid(long uid) { 38 | this.uid = uid; 39 | } 40 | 41 | @Override 42 | public String getDisplayName() { 43 | return displayName; 44 | } 45 | 46 | public void setDisplayName(String displayName) { 47 | this.displayName = displayName; 48 | } 49 | 50 | @Override 51 | public String getEmail() { 52 | return email; 53 | } 54 | 55 | public void setEmail(String email) { 56 | this.email = email; 57 | } 58 | 59 | @Override 60 | public String getPhotoUrl() { 61 | return photoUrl; 62 | } 63 | 64 | public void setPhotoUrl(String photoUrl) { 65 | this.photoUrl = photoUrl; 66 | } 67 | 68 | @Override 69 | public boolean isTosAccepted() { 70 | return tosAccepted; 71 | } 72 | 73 | public void setTosAccepted(boolean tosAccepted) { 74 | this.tosAccepted = tosAccepted; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/internal/servlet/LoginServlet.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.internal.servlet; 18 | 19 | import java.io.IOException; 20 | import java.util.Random; 21 | 22 | import javax.servlet.RequestDispatcher; 23 | import javax.servlet.ServletException; 24 | import javax.servlet.http.HttpServlet; 25 | import javax.servlet.http.HttpServletRequest; 26 | import javax.servlet.http.HttpServletResponse; 27 | 28 | import com.google.account.filter.FilterUtil; 29 | import com.google.account.filter.SessionInfo; 30 | import com.google.account.filter.SessionUtil; 31 | import com.google.account.internal.UserImpl; 32 | import com.google.account.internal.storage.UserStore; 33 | import com.google.config.UrlConfig; 34 | 35 | public class LoginServlet extends HttpServlet { 36 | private static final long serialVersionUID = 1L; 37 | private static Random random = new Random(); 38 | 39 | @Override 40 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, 41 | IOException { 42 | doPost(req, resp); 43 | } 44 | 45 | @Override 46 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, 47 | IOException { 48 | String email = req.getParameter("email"); 49 | String password = req.getParameter("password"); 50 | 51 | if (UserStore.login(email, password)) { 52 | UserImpl user = UserStore.getUserByEmail(email); 53 | long expiresAt = System.currentTimeMillis() + SessionUtil.SESSION_LIFETIME * 1000; 54 | SessionInfo sessionInfo = new SessionInfo(user.getUid(), expiresAt, random.nextLong()); 55 | FilterUtil.setSessionCookie(req, resp, sessionInfo); 56 | FilterUtil.setSessionInfoInRequestAttributeAfterLogin(req, user, sessionInfo); 57 | req.getRequestDispatcher(UrlConfig.HOME_PAGE).forward(req, resp); 58 | } else { 59 | req.setAttribute("error", "WRONG_PASSWORD"); 60 | RequestDispatcher dispatcher = getServletContext().getRequestDispatcher( 61 | UrlConfig.LOGIN_PAGE); 62 | dispatcher.forward(req, resp); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/internal/servlet/LogoutServlet.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.internal.servlet; 18 | 19 | import java.io.IOException; 20 | 21 | import javax.servlet.ServletException; 22 | import javax.servlet.http.HttpServlet; 23 | import javax.servlet.http.HttpServletRequest; 24 | import javax.servlet.http.HttpServletResponse; 25 | 26 | import com.google.account.filter.FilterUtil; 27 | import com.google.account.filter.SessionInfo; 28 | import com.google.config.UrlConfig; 29 | 30 | public class LogoutServlet extends HttpServlet { 31 | private static final long serialVersionUID = 1L; 32 | 33 | @Override 34 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, 35 | IOException { 36 | SessionInfo sessionInfo = FilterUtil.getSessionInfo(req); 37 | if (sessionInfo != null) { 38 | FilterUtil.clearSessionInfoInAttributeAfterLogout(req); 39 | } 40 | FilterUtil.clearSessionCookie(req, resp); 41 | resp.sendRedirect(UrlConfig.LOGIN_PAGE); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/internal/servlet/SignupServlet.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.internal.servlet; 18 | 19 | import java.io.IOException; 20 | import java.util.List; 21 | import java.util.Random; 22 | 23 | import javax.servlet.RequestDispatcher; 24 | import javax.servlet.ServletException; 25 | import javax.servlet.http.HttpServlet; 26 | import javax.servlet.http.HttpServletRequest; 27 | import javax.servlet.http.HttpServletResponse; 28 | 29 | import com.google.account.User; 30 | import com.google.account.filter.FilterUtil; 31 | import com.google.account.filter.SessionInfo; 32 | import com.google.account.filter.SessionUtil; 33 | import com.google.account.internal.storage.UserStore; 34 | import com.google.account.internal.storage.UserStore.SignupError; 35 | import com.google.common.collect.Lists; 36 | import com.google.config.UrlConfig; 37 | 38 | public class SignupServlet extends HttpServlet { 39 | private static final long serialVersionUID = 1L; 40 | private static Random random = new Random(); 41 | 42 | @Override 43 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, 44 | IOException { 45 | List errors = Lists.newArrayList(); 46 | @SuppressWarnings("unchecked") 47 | User user = UserStore.signup(req.getParameterMap(), req.getRemoteAddr(), errors); 48 | if (user != null) { 49 | long expiresAt = System.currentTimeMillis() + SessionUtil.SESSION_LIFETIME * 1000L; 50 | SessionInfo sessionInfo = new SessionInfo(user.getUid(), expiresAt, random.nextLong()); 51 | FilterUtil.setSessionCookie(req, resp, sessionInfo); 52 | FilterUtil.setSessionInfoInRequestAttributeAfterLogin(req, user, sessionInfo); 53 | req.getRequestDispatcher(UrlConfig.HOME_PAGE).forward(req, resp); 54 | } else { 55 | req.setAttribute("errors", errors); 56 | RequestDispatcher dispatcher = 57 | getServletContext().getRequestDispatcher(UrlConfig.SIGNUP_PAGE); 58 | dispatcher.forward(req, resp); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/account/internal/storage/UserRecord.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.account.internal.storage; 18 | 19 | import javax.jdo.annotations.IdGeneratorStrategy; 20 | import javax.jdo.annotations.IdentityType; 21 | import javax.jdo.annotations.PersistenceCapable; 22 | import javax.jdo.annotations.Persistent; 23 | import javax.jdo.annotations.PrimaryKey; 24 | 25 | /** 26 | * The persistence layer representation for a user. 27 | */ 28 | @PersistenceCapable(identityType = IdentityType.APPLICATION) 29 | class UserRecord { 30 | @PrimaryKey 31 | @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) 32 | private Long id; 33 | 34 | @Persistent 35 | private String email; 36 | 37 | @Persistent 38 | private String displayName; 39 | 40 | @Persistent 41 | private String password; 42 | 43 | @Persistent 44 | private String photoUrl; 45 | 46 | @Persistent 47 | private boolean tosAccepted; 48 | 49 | public long getId() { 50 | return id; 51 | } 52 | 53 | public String getDisplayName() { 54 | return displayName; 55 | } 56 | 57 | public void setDisplayName(String displayName) { 58 | this.displayName = displayName; 59 | } 60 | 61 | public String getEmail() { 62 | return email; 63 | } 64 | 65 | public void setEmail(String email) { 66 | this.email = email; 67 | } 68 | 69 | public String getPassword() { 70 | return password; 71 | } 72 | 73 | public void setPassword(String password) { 74 | this.password = password; 75 | } 76 | 77 | public String getPhotoUrl() { 78 | return photoUrl; 79 | } 80 | 81 | public void setPhotoUrl(String photoUrl) { 82 | this.photoUrl = photoUrl; 83 | } 84 | 85 | public boolean isTosAccepted() { 86 | return tosAccepted; 87 | } 88 | 89 | public void setTosAccepted(boolean tosAccepted) { 90 | this.tosAccepted = tosAccepted; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/config/UrlConfig.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.config; 18 | 19 | public class UrlConfig { 20 | 21 | public static final String SIGNUP_ACTION = "/account/action/signup"; 22 | public static final String SIGNUP_PAGE = "/account/page/signup.jsp"; 23 | public static final String LOGIN_ACTION = "/account/action/login"; 24 | public static final String LOGIN_PAGE = "/account/page/login.jsp"; 25 | public static final String LOGOUT_ACTION = "/account/action/logout"; 26 | public static final String HOME_PAGE = "/account/page/home.jsp"; 27 | } 28 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/util/EmailValidator.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.util; 18 | 19 | import java.util.regex.Pattern; 20 | 21 | public class EmailValidator { 22 | private static final Pattern VALID_EMAIL_PATTERN = Pattern.compile( 23 | "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE); 24 | 25 | public static boolean isValid(String email) { 26 | return email != null && VALID_EMAIL_PATTERN.matcher(email).matches(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/util/EncryptionUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.util; 18 | 19 | import java.io.UnsupportedEncodingException; 20 | import java.security.InvalidKeyException; 21 | import java.security.NoSuchAlgorithmException; 22 | import java.security.SecureRandom; 23 | 24 | import javax.crypto.BadPaddingException; 25 | import javax.crypto.Cipher; 26 | import javax.crypto.IllegalBlockSizeException; 27 | import javax.crypto.NoSuchPaddingException; 28 | import javax.crypto.spec.SecretKeySpec; 29 | import javax.xml.bind.DatatypeConverter; 30 | 31 | import com.google.common.io.BaseEncoding; 32 | 33 | public class EncryptionUtil { 34 | private static final String KEY = "80a003040c0807d00f01075090123456"; 35 | 36 | public static String encrypt(String plainText) { 37 | try { 38 | Cipher cipher = Cipher.getInstance("AES"); 39 | cipher.init(Cipher.ENCRYPT_MODE, getAesKey()); 40 | byte[] salt = new byte[8]; 41 | SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); 42 | random.nextBytes(salt); 43 | cipher.update(salt); 44 | byte[] encrypted = cipher.doFinal(plainText.getBytes("ISO-8859-1")); 45 | return BaseEncoding.base64Url().encode(encrypted); 46 | } catch (InvalidKeyException e) { 47 | throw new RuntimeException(e); 48 | } catch (NoSuchAlgorithmException e) { 49 | throw new RuntimeException(e); 50 | } catch (NoSuchPaddingException e) { 51 | throw new RuntimeException(e); 52 | } catch (IllegalBlockSizeException e) { 53 | throw new RuntimeException(e); 54 | } catch (BadPaddingException e) { 55 | throw new RuntimeException(e); 56 | } catch (UnsupportedEncodingException e) { 57 | throw new RuntimeException(e); 58 | } 59 | } 60 | 61 | public static String decrypt(String encryptedText) { 62 | try { 63 | byte[] encrypted = BaseEncoding.base64Url().decode(encryptedText); 64 | Cipher cipher = Cipher.getInstance("AES"); 65 | cipher.init(Cipher.DECRYPT_MODE, getAesKey()); 66 | byte[] plain = cipher.doFinal(encrypted); 67 | if (plain == null || plain.length <= 8) { 68 | throw new RuntimeException("wrong encrypted text."); 69 | } 70 | byte[] data = new byte[plain.length - 8]; 71 | System.arraycopy(plain, 8, data, 0, data.length); 72 | return new String(data, "ISO-8859-1"); 73 | } catch (InvalidKeyException e) { 74 | throw new RuntimeException(e); 75 | } catch (NoSuchAlgorithmException e) { 76 | throw new RuntimeException(e); 77 | } catch (NoSuchPaddingException e) { 78 | throw new RuntimeException(e); 79 | } catch (IllegalBlockSizeException e) { 80 | throw new RuntimeException(e); 81 | } catch (BadPaddingException e) { 82 | throw new RuntimeException(e); 83 | } catch (UnsupportedEncodingException e) { 84 | throw new RuntimeException(e); 85 | } 86 | } 87 | 88 | private static SecretKeySpec getAesKey() { 89 | final byte[] symKeyData = DatatypeConverter.parseHexBinary(KEY); 90 | return new SecretKeySpec(symKeyData, "AES"); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /demo/original/src/main/java/com/google/util/JspUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.util; 18 | 19 | import javax.servlet.http.HttpServletRequest; 20 | 21 | import com.google.common.base.Strings; 22 | 23 | public class JspUtil { 24 | public static boolean isNullOrEmpty(String value) { 25 | return Strings.isNullOrEmpty(value); 26 | } 27 | 28 | public static String nullToEmpty(String value) { 29 | return Strings.isNullOrEmpty(value) ? "" : value; 30 | } 31 | 32 | public static String nullSafeGetParameter(HttpServletRequest request, String parameter) { 33 | return nullToEmpty(request.getParameter(parameter)); 34 | } 35 | 36 | public static String nullSafeGetAttribute(HttpServletRequest request, String parameter) { 37 | return nullToEmpty((String) request.getAttribute(parameter)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /demo/original/src/main/resources/META-INF/jdoconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /demo/original/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | org.datanucleus.api.jpa.PersistenceProviderImpl 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/original/src/main/webapp/WEB-INF/appengine-web.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | ws-codelab 20 | 1 21 | 22 | 25 | true 26 | 27 | 28 | 29 | 30 | 31 | 32 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /demo/original/src/main/webapp/WEB-INF/logging.properties: -------------------------------------------------------------------------------- 1 | # A default java.util.logging configuration. 2 | # (All App Engine logging is through java.util.logging by default). 3 | # 4 | # To use this configuration, copy it into your application's WEB-INF 5 | # folder and add the following to your appengine-web.xml: 6 | # 7 | # 8 | # 9 | # 10 | # 11 | 12 | # Set the default logging level for all loggers to WARNING 13 | .level = WARNING 14 | -------------------------------------------------------------------------------- /demo/original/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 23 | 24 | js 25 | application/x-javascript 26 | 27 | 28 | css 29 | text/css 30 | 31 | 32 | 33 | SignupServlet 34 | com.google.account.internal.servlet.SignupServlet 35 | 36 | 37 | LoginServlet 38 | com.google.account.internal.servlet.LoginServlet 39 | 40 | 41 | LogoutServlet 42 | com.google.account.internal.servlet.LogoutServlet 43 | 44 | 45 | 46 | SignupServlet 47 | /account/action/signup 48 | 49 | 50 | LoginServlet 51 | /account/action/login 52 | 53 | 54 | LogoutServlet 55 | /account/action/logout 56 | 57 | 58 | 59 | CookieParserFilter 60 | com.google.account.filter.CookieParserFilter 61 | 62 | 63 | HasSessionFilter 64 | com.google.account.filter.HasSessionFilter 65 | 66 | 67 | NoSessionFilter 68 | com.google.account.filter.NoSessionFilter 69 | 70 | 71 | 72 | CookieParserFilter 73 | /account/action/* 74 | /account/page/* 75 | 76 | 77 | 78 | NoSessionFilter 79 | /account/page/login.jsp 80 | /account/page/signup.jsp 81 | /account/action/login 82 | /account/action/signup 83 | 84 | 85 | 86 | HasSessionFilter 87 | /account/page/home.jsp 88 | 89 | 90 | 91 | index.jsp 92 | 93 | 94 | 95 | 96 | all 97 | /* 98 | 99 | 100 | CONFIDENTIAL 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /demo/original/src/main/webapp/account/css/ui.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | input { 18 | font-size:83%; 19 | } 20 | a { 21 | color:#00c; 22 | } 23 | #login-box { 24 | background-color: #FFFFFF; 25 | border: 4px solid #E8EEFA; 26 | width: 300px; 27 | } 28 | 29 | #login-box-inner { 30 | background-color: #E8EEFA; 31 | border: 1px solid #E8EEFA; 32 | font-family: arial,sans-serif; 33 | font-size: 12px; 34 | font-weight: bold; 35 | margin: 3px; 36 | padding: 10px 20px; 37 | text-align: left; 38 | } 39 | #login-box-inner .error { 40 | color: red; 41 | } 42 | 43 | #login-box-inner .input-box { 44 | width: 240px; 45 | font-size: 14px; 46 | margin-bottom: 4px; 47 | margin-top: 2px; 48 | } 49 | 50 | #login-box-inner .input-button { 51 | cursor: pointer; 52 | font-family: arial,sans-serif; 53 | font-size: 12px; 54 | padding: 0 6px; 55 | margin-bottom: 12px; 56 | margin-top: 5px; 57 | position: relative; 58 | } 59 | #login-box-inner .new-account { 60 | color: #0000CC; 61 | font-size: 11px; 62 | cursor: pointer; 63 | font-size: 12px; 64 | margin-top: 6px; 65 | text-decoration: underline; 66 | } 67 | 68 | #reg_box { 69 | background-color: #FFFFFF; 70 | border: 4px solid #E8EEFA; 71 | width: 480px; 72 | } 73 | 74 | #reg_area { 75 | margin: 4px; 76 | padding: 10px; 77 | background-color: #E8EEFA 78 | } 79 | 80 | #reg_box input { 81 | font-size: 16px; 82 | margin-top: 2px; 83 | padding: 6px; 84 | width: 280px; 85 | } 86 | #reg_box select { 87 | font-size: 13px; 88 | margin-top: 2px; 89 | padding: 6px; 90 | height: 30px; 91 | } 92 | #reg_box td.label { 93 | color: #1D2A5B; 94 | font-size: 13px; 95 | padding-right: 3px; 96 | text-align: right; 97 | width: 105px; 98 | } 99 | .widget-button td.widget-button-right { 100 | background: url("../image/dl_button_right.jpg") repeat scroll 0 0 transparent; 101 | height: 36px; 102 | margin-right: 0; 103 | width: 9px; 104 | } 105 | .widget-button td.widget-button-middle { 106 | background: url("../image/dl_button_middle.jpg") repeat scroll 0 0 transparent; 107 | cursor: pointer; 108 | margin-left: 0; 109 | margin-right: 0; 110 | padding-left: 5px; 111 | padding-right: 5px; 112 | } 113 | .widget-button td.widget-button-left { 114 | background: url("../image/dl_button_left.jpg") repeat scroll 0 0 transparent; 115 | height: 36px; 116 | margin-left: 0; 117 | width: 9px; 118 | } 119 | 120 | .widget-button td.widget-button-middle a.widget-button-link, 121 | .widget-button td.widget-button-middle a.widget-button-link a { 122 | color: #fff; 123 | font-size: 12px; 124 | font-weight: bold; 125 | } 126 | 127 | .widget-error { 128 | background-color: yellow; 129 | color: red; 130 | font-weight: bold; 131 | } 132 | -------------------------------------------------------------------------------- /demo/original/src/main/webapp/account/image/dl_button_left.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/original/src/main/webapp/account/image/dl_button_left.jpg -------------------------------------------------------------------------------- /demo/original/src/main/webapp/account/image/dl_button_middle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/original/src/main/webapp/account/image/dl_button_middle.jpg -------------------------------------------------------------------------------- /demo/original/src/main/webapp/account/image/dl_button_right.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/original/src/main/webapp/account/image/dl_button_right.jpg -------------------------------------------------------------------------------- /demo/original/src/main/webapp/account/image/nophoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/original/src/main/webapp/account/image/nophoto.png -------------------------------------------------------------------------------- /demo/original/src/main/webapp/account/image/user1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/original/src/main/webapp/account/image/user1.jpg -------------------------------------------------------------------------------- /demo/original/src/main/webapp/account/image/user2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/original/src/main/webapp/account/image/user2.jpg -------------------------------------------------------------------------------- /demo/original/src/main/webapp/account/image/user3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/original/src/main/webapp/account/image/user3.jpg -------------------------------------------------------------------------------- /demo/original/src/main/webapp/account/image/user4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/original/src/main/webapp/account/image/user4.jpg -------------------------------------------------------------------------------- /demo/original/src/main/webapp/account/page/home.jsp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | <%@ page language="java" contentType="text/html; charset=iso-8859-1" %> 18 | <%@ page import="com.google.config.UrlConfig" %> 19 | <%@ page import="com.google.util.JspUtil" %> 20 | <%@ page import="com.google.account.filter.FilterUtil" %> 21 | <%@ page import="com.google.account.User" %> 22 | <% 23 | User user = FilterUtil.getSessionUser(request); 24 | %> 25 | 26 | 27 | Home Page 28 | 29 | 30 | 31 | 32 |
33 |
34 |
35 | Welcome to <%= request.getServerName() %>, <%= user.getDisplayName() %>! 36 |
37 |
38 | Below are your profile information: 39 |
40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | 56 | 59 | 60 | 61 |
45 | 46 |
51 | 52 |
57 | 58 |
62 |

63 | 76 | 79 |
64 | 65 | 66 | 67 | 68 | 71 | 72 | 73 | 74 |
69 | Refresh Page 70 |
75 |
77 | Or Logout 78 |
80 |

81 |
82 | 83 |
84 | 85 | 86 | -------------------------------------------------------------------------------- /demo/original/src/main/webapp/account/page/login.jsp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | <%@ page language="java" contentType="text/html; charset=iso-8859-1" %> 18 | <%@ page import="com.google.util.JspUtil" %> 19 | <%@ page import="com.google.config.UrlConfig" %> 20 | <% 21 | String error = JspUtil.nullSafeGetAttribute(request, "error"); 22 | String errorMessage = null; 23 | boolean showError = true; 24 | if ("WRONG_PASSWORD".equals(error)) { 25 | errorMessage = "Wrong email and password combination."; 26 | } else if (!JspUtil.isNullOrEmpty(error)) { 27 | errorMessage = "Unknown error: " + error; 28 | } else { 29 | showError = false; 30 | } 31 | 32 | String email = JspUtil.nullSafeGetParameter(request, "email"); 33 | String app = JspUtil.nullSafeGetParameter(request, "app"); 34 | String continueUrl = JspUtil.nullSafeGetParameter(request, "continueUrl"); 35 | %> 36 | 37 | 38 | Sign In 39 | 40 | 41 | 42 | 43 |

Log in to <%= request.getServerName() %>

44 |
45 |
46 |
47 | 48 | <% if (showError) { %> 49 |
<%= errorMessage %>
50 | <% } %> 51 |

52 | User
53 | 54 |

55 |

Password

56 |

57 |

58 |
59 |
60 |
61 | 62 | 63 | -------------------------------------------------------------------------------- /demo/original/src/main/webapp/account/page/signup.jsp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | <%@ page language="java" contentType="text/html; charset=iso-8859-1" %> 18 | <%@ page import="com.google.account.internal.storage.UserStore.SignupError" %> 19 | <%@ page import="com.google.config.UrlConfig" %> 20 | <%@ page import="com.google.util.JspUtil" %> 21 | <%@ page import="java.util.Collections" %> 22 | <%@ page import="java.util.List" %> 23 | <% 24 | List errors = (List) request.getAttribute("errors"); 25 | if (errors == null) { 26 | errors = Collections.emptyList(); 27 | } 28 | String email = JspUtil.nullSafeGetParameter(request, "email"); 29 | String displayName = JspUtil.nullSafeGetParameter(request, "displayName"); 30 | String photoUrl = JspUtil.nullSafeGetParameter(request, "photoUrl"); 31 | %> 32 | 33 | 34 | Sign Up 35 | 36 | 37 | 38 | 39 |
40 |
41 |
42 |
43 | Welcome to <%= request.getServerName() %>! 44 |
45 |
46 | Please provide follow information to create your account. 47 |
48 | 49 | 50 | 51 | 52 | 60 | 61 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 81 | 87 | 88 | 89 | 90 | 96 | 97 | 98 |
53 | 54 | <% if (errors.contains(SignupError.INVALID_EMAIL)) { %> 55 |
Invalid email address.
56 | <% } else if (errors.contains(SignupError.EMAIL_REGISTERED)) { %> 57 |
Email already registered.
58 | <% } %> 59 |
64 | 65 | <% if (errors.contains(SignupError.EMPTY_DISPLAY_NAME)) { %> 66 |
Display Name cannot be empty.
67 | <% } %> 68 |
73 | 74 | 75 | 76 | 77 |
82 | 83 | <% if (errors.contains(SignupError.INVALID_PASSWORD)) { %> 84 |
The password length cannot be less than 3.
85 | <% } %> 86 |
91 | 92 | <% if (errors.contains(SignupError.CONFIRM_MISMATCH)) { %> 93 |
Your passwords do not match.
94 | <% } %> 95 |
99 |

100 | 113 | 116 |
101 | 102 | 103 | 104 | 105 | 108 | 109 | 110 | 111 |
106 | Create Account 107 |
112 |
114 | Or Cancel 115 |
117 |

118 |
119 |
120 |
121 | 122 | 123 | -------------------------------------------------------------------------------- /demo/original/src/main/webapp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/two-token-sw/4bc4bf28e3fb758723ad273a2b5475fb1338face/demo/original/src/main/webapp/favicon.ico -------------------------------------------------------------------------------- /demo/original/src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | <%@ page language="java" contentType="text/html; charset=iso-8859-1" %> 18 | <%@ page import="com.google.config.UrlConfig" %> 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /library/js/indexed-db.js: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | /** 18 | * @fileoverview Utility methods to manage session state in the Storage layer. 19 | */ 20 | 21 | /** 22 | * Namespace for two-token based web session management JavaScript codes. 23 | */ 24 | var dosidos = dosidos || {}; 25 | 26 | /** 27 | * Namespace for storage layer. 28 | */ 29 | dosidos.store = dosidos.store || {}; 30 | 31 | /** 32 | * Indexed DB store name. 33 | */ 34 | dosidos.store.INDEXED_DB_STORE_ = 'two_token_web_session'; 35 | 36 | /** 37 | * Initializes the underline storage. Currently indexedDB is used. 38 | * The readXXX()/saveXXX() methods can be called only after this method invoked. 39 | */ 40 | dosidos.store.init_ = function() { 41 | return new Promise(function(resolve, reject) { 42 | if (dosidos.store.db_) { 43 | // Already initialized. 44 | resolve(); 45 | } else { 46 | var request = indexedDB.open(dosidos.store.INDEXED_DB_STORE_); 47 | request.onsuccess = function(event) { 48 | dosidos.store.db_ = event.target.result; 49 | // Generic error handler 50 | dosidos.store.db_.onerror = function(event) { 51 | console.log("IndexedDB error: " + event.target.errorCode); 52 | }; 53 | console.log("IndexedDB successfully initialized."); 54 | resolve(); 55 | }; 56 | request.onupgradeneeded = function(event) { 57 | var db = event.target.result; 58 | db.createObjectStore(dosidos.store.INDEXED_DB_STORE_); 59 | }; 60 | request.onerror = function(event) { 61 | console.log("Failed to initilaize IndexedDB."); 62 | reject(event); 63 | }; 64 | } 65 | }); 66 | }; 67 | 68 | 69 | /** 70 | * Reads value from indexed DB. 71 | */ 72 | dosidos.store.read_ = function(key) { 73 | return new Promise(function(resolve, reject) { 74 | var store = dosidos.store.db_ 75 | .transaction(dosidos.store.INDEXED_DB_STORE_, 'readwrite') 76 | .objectStore(dosidos.store.INDEXED_DB_STORE_); 77 | var request = store.get(key); 78 | request.onsuccess = function(event) { 79 | resolve(request.result); 80 | }; 81 | request.onerror = function(event) { 82 | console.log("Failed to read IndexedDB."); 83 | reject(event); 84 | }; 85 | }); 86 | }; 87 | 88 | /** 89 | * Saves value into indexed DB. If value is null or undefined, the corresponding 90 | * key will be removed. 91 | */ 92 | dosidos.store.save_ = function(key, value) { 93 | return new Promise(function(resolve, reject) { 94 | var store = dosidos.store.db_ 95 | .transaction(dosidos.store.INDEXED_DB_STORE_, 'readwrite') 96 | .objectStore(dosidos.store.INDEXED_DB_STORE_); 97 | if (value === undefined || value === null) { 98 | store['delete'](key); 99 | } else { 100 | store.put(value, key); 101 | } 102 | store.transaction.oncomplete = function() { 103 | resolve(); 104 | } 105 | }); 106 | }; 107 | 108 | /* 109 | * Data structure for two-token web session status information. 110 | * @typedef {{ 111 | * active: (boolean|undefined), 112 | * lat: (string|undefined), 113 | * satExpires: (number|undefined) 114 | * }} 115 | */ 116 | dosidos.SessionState; 117 | 118 | /** 119 | * The indexedDb key name for the session status. 120 | */ 121 | dosidos.store.KEY_SESSION_STATE_ = 'two_token_session_state'; 122 | 123 | /** 124 | * Reads next token refresh time. 125 | */ 126 | dosidos.store.readSessionState = function() { 127 | return dosidos.store.init_() 128 | .then(function() { 129 | return dosidos.store.read_(dosidos.store.KEY_SESSION_STATE_); 130 | }) 131 | .then(function(value) { 132 | return (value && JSON.parse(value)) || {}; 133 | }); 134 | }; 135 | 136 | dosidos.store.saveSessionState = function(json) { 137 | return dosidos.store.init_() 138 | .then(function() { 139 | var str = json && JSON.stringify(json); 140 | return dosidos.store.save_(dosidos.store.KEY_SESSION_STATE_, str); 141 | }); 142 | }; 143 | 144 | dosidos.store.changeSatExpires = function(satExpires) { 145 | satExpires = satExpires > 0 ? satExpires : 0; 146 | return dosidos.store.readSessionState() 147 | .then(function(sessionState) { 148 | if (sessionState && sessionState['enabled']) { 149 | sessionState['satExpires'] = satExpires; 150 | return dosidos.store.saveSessionState(sessionState); 151 | } 152 | }); 153 | }; 154 | 155 | dosidos.store.clear = function() { 156 | return dosidos.store.saveSessionState(undefined); 157 | }; 158 | 159 | dosidos.store.suspend = function() { 160 | return dosidos.store.readSessionState().then(function(sessionState) { 161 | if (sessionState) { 162 | sessionState['enabled'] = false; 163 | return dosidos.store.saveSessionState(sessionStatee); 164 | } 165 | }); 166 | }; 167 | 168 | dosidos.store.restart = function(opt_sessionState) { 169 | return new Promise(function(resolve, reject) { 170 | if (opt_sessionState && opt_sessionState['lat']) { 171 | // Has new lat, just overwrite old sessionState. 172 | var sessionState = { 173 | 'enabled': true, 174 | 'lat': opt_sessionState['lat'], 175 | 'satExpires': opt_sessionState['satExpires'] 176 | }; 177 | return dosidos.store.saveSessionState(sessionState); 178 | } 179 | 180 | dosidos.store.readSessionState().then(function(sessionState) { 181 | if (sessionState && sessionState['lat']) { 182 | sessionState['enabled'] = true; 183 | if (opt_sessionState && opt_sessionState['satExpires']) { 184 | sessionState['satExpires'] = opt_sessionState['satExpires']; 185 | } 186 | return dosidos.store.saveSessionState(sessionState); 187 | } 188 | }); 189 | }); 190 | }; 191 | -------------------------------------------------------------------------------- /library/js/install.js: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | /** 18 | * @fileoverview Utility method to install service worker in post login page. 19 | */ 20 | 21 | /** 22 | * Installs/Reinstalls the service worker. 23 | * @param swUrl The URL of the service worker. 24 | * @param swScope The Scope of the service worker. 25 | * @param lat The long live auth token. 26 | * @param satLifetime The life time of the short lived token. 27 | */ 28 | dosidos.installServiceWorker = function(swUrl, swScope, lat, satLifetime) { 29 | if (!('serviceWorker' in navigator)) { 30 | console.log('Browser doesn\'t support service worker'); 31 | return; 32 | } 33 | 34 | navigator.serviceWorker.register(swUrl, {scope: swScope}) 35 | .then(function(registration) { 36 | console.log('Service Worker is successfully registrated.'); 37 | var satExpires = Date.now() + satLifetime * 1000 - 38 | dosidos.settings.TIME_BUFFER_MS; 39 | var sessionState = { 40 | 'lat': lat, 41 | 'satExpires': satExpires 42 | }; 43 | dosidos.store.restart(sessionState).then(function() { 44 | console.log('Service Worker is restarted.'); 45 | }); 46 | }).catch(function(err) { 47 | console.log('ServiceWorker registration failed: ' + err); 48 | }); 49 | }; 50 | -------------------------------------------------------------------------------- /library/js/settings.js: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | /** 18 | * @fileoverview The token configuration parameters. 19 | */ 20 | 21 | /** 22 | * Name space for configuration parameters. 23 | */ 24 | dosidos.settings = dosidos.settings || {}; 25 | 26 | /** 27 | * The URL of the token end point. 28 | */ 29 | dosidos.settings.TOKEN_ENDPOINT_URL = '/token'; 30 | 31 | /** 32 | * The HTTP header name for the LAT when accessing token end point. 33 | */ 34 | dosidos.settings.HTTP_HEADER_LAT = 'X-LAT'; 35 | 36 | /** 37 | * Sets a shorter life time intentionally to allow time differences between 38 | * the server and the user agent. In milliseconds. 39 | */ 40 | dosidos.settings.TIME_BUFFER_MS = 10 * 1000; //5 * 60 * 1000; 41 | 42 | -------------------------------------------------------------------------------- /library/js/sw.js: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | /** 18 | * @fileoverview The session management service worker. 19 | */ 20 | 21 | importScripts('/js/indexed-db.js'); 22 | importScripts('/js/settings.js'); 23 | 24 | dosidos.refreshSat = function(lat) { 25 | 26 | // To mitigate XSRF, LAT need to be sent in the HTTP header. 27 | var myHeaders = new Headers(); 28 | myHeaders.set(dosidos.settings.HTTP_HEADER_LAT, lat); 29 | var options = { 30 | method: 'GET', 31 | headers: myHeaders, 32 | credentials: 'include' 33 | }; 34 | 35 | var url = dosidos.settings.TOKEN_ENDPOINT_URL + '?action=REFRESH_BY_LAT'; 36 | return fetch(url, options) 37 | .then(function(data) { 38 | return data.json(); 39 | }).then(function(response) { 40 | console.log('Token response returned: ' 41 | + response && JSON.stringify(response)); 42 | if (!response || response['result'] == 'ERROR') { 43 | if (response && response['error']) { 44 | console.log('Error token response received: ' + response['error']); 45 | } 46 | } else if (response['result'] == 'END') { 47 | console.log('Token refreshing stopped.'); 48 | return dosidos.store.clear(); 49 | } else if (response['result'] == 'REFRESHED') { 50 | var satExpires = Date.now() + response['satLifetime'] * 1000 - 51 | dosidos.settings.TIME_BUFFER_MS; 52 | console.log('Next token refresh in ' 53 | + ((satExpires - Date.now()) / 1000) + ' seconds.'); 54 | return dosidos.store.changeSatExpires(satExpires); 55 | } else { 56 | console.log('Unknow token response type.'); 57 | } 58 | }); 59 | }; 60 | 61 | 62 | /** 63 | * Checks if SAT is expired or not, for an authenticated web session. 64 | */ 65 | dosidos.check = function() { 66 | return dosidos.store.readSessionState().then(function (state) { 67 | if (state && state['enabled'] && state['satExpires'] <= Date.now()) { 68 | return dosidos.refreshSat(state['lat']); 69 | } 70 | }); 71 | }; 72 | 73 | /** 74 | * Adds listener to intercept all web requests. 75 | */ 76 | self.addEventListener('fetch', function(event) { 77 | var req = event.request; 78 | var doFetch = function() { 79 | return fetch(req); 80 | }; 81 | event.respondWith(dosidos.check().then(doFetch, doFetch)); 82 | }); 83 | 84 | self.addEventListener('install', function(event) { 85 | // Bypass the waiting lifecycle stage, 86 | // just in case there's an older version of this SW registration. 87 | event.waitUntil(self.skipWaiting()); 88 | }); 89 | 90 | self.addEventListener('activate', function(event) { 91 | // Take control of all pages under this SW's scope immediately, 92 | // instead of waiting for reload/navigation. 93 | event.waitUntil(self.clients.claim()); 94 | }); 95 | -------------------------------------------------------------------------------- /library/pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 4.0.0 20 | 21 | com.google 22 | dosidos-lib 23 | 1.0 24 | jar 25 | 26 | Two Token based Web Session Management Library 27 | 28 | 29 | 30 | javax.servlet 31 | servlet-api 32 | jar 33 | 2.5 34 | 35 | 36 | javax.servlet 37 | javax.servlet-api 38 | 3.0.1 39 | 40 | 41 | com.google.guava 42 | guava 43 | 18.0 44 | 45 | 46 | com.google.code.gson 47 | gson 48 | 2.5 49 | 50 | 51 | org.json 52 | json 53 | 20131018 54 | 55 | 56 | com.google.code.findbugs 57 | jsr305 58 | 3.0.0 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.apache.maven.plugins 66 | maven-install-plugin 67 | 2.5.1 68 | 69 | 70 | install-jar-lib 71 | install 72 | 73 | install-file 74 | 75 | 76 | com.google 77 | dosidos-lib 78 | 1.0 79 | jar 80 | ${basedir}/target/dosidos-lib-1.0.jar 81 | true 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/AppContextListener.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos; 18 | 19 | import javax.servlet.ServletContext; 20 | import javax.servlet.ServletContextEvent; 21 | import javax.servlet.ServletContextListener; 22 | import javax.servlet.annotation.WebListener; 23 | 24 | /** 25 | * Initializes {@code TwoTokenManager} when the web application starts. 26 | */ 27 | @WebListener 28 | public abstract class AppContextListener implements ServletContextListener { 29 | public static final String ATTR_TWO_TOKEN_MANAGER = "TwoTokenManager"; 30 | 31 | @Override 32 | public void contextDestroyed(ServletContextEvent event) { 33 | } 34 | 35 | @Override 36 | public void contextInitialized(ServletContextEvent event) { 37 | ServletContext ctx = event.getServletContext(); 38 | ctx.setAttribute(ATTR_TWO_TOKEN_MANAGER, constructTwoTokenManager()); 39 | } 40 | 41 | /** 42 | * Constructs an application-specific TwoTokenManager instance. 43 | */ 44 | public abstract TwoTokenManager constructTwoTokenManager(); 45 | } 46 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/LaunchHook.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos; 18 | 19 | import javax.annotation.Nullable; 20 | 21 | /** 22 | * A hook to decide if two-token model should be enabled. 23 | */ 24 | public interface LaunchHook { 25 | 26 | /** 27 | * Decides if two-token model should be enabled for current user agent. 28 | * 29 | * @param userAgent 30 | * Current user agent string. 31 | * @return {@code true} if two-token model should be enabled; Otherwise 32 | * {@code false}. 33 | */ 34 | boolean isEnabledForUserAgent(String userAgent); 35 | 36 | /** 37 | * Decides if two-token model should be enabled for current user. 38 | * 39 | * @param userId 40 | * The id of logged-in user. Use {@code null} if no user logs in. 41 | * @return {@code true} if two-token model should be enabled; Otherwise 42 | * {@code false}. 43 | */ 44 | boolean isEnabledForUser(@Nullable String userId); 45 | } 46 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/Settings.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos; 18 | 19 | import com.google.common.base.Strings; 20 | 21 | /** 22 | * Holds the configuration information. 23 | */ 24 | public class Settings { 25 | private final static String DEFAULT_LAT_HEADER_NAME = "X-LAT"; 26 | 27 | private final String aesKey; 28 | private final String latHeaderName; 29 | private final long latLifetime; 30 | 31 | private Settings( 32 | String latHeaderName, 33 | long latLifetime, 34 | String aesKey) { 35 | this.latHeaderName = latHeaderName; 36 | this.latLifetime = latLifetime; 37 | this.aesKey = aesKey; 38 | } 39 | 40 | public String getLatHeaderName() { 41 | return latHeaderName; 42 | } 43 | 44 | public long getLatLifetime() { 45 | return latLifetime; 46 | } 47 | 48 | public String getAesKey() { 49 | return aesKey; 50 | } 51 | 52 | public static class Builder { 53 | private String aesKey; 54 | private String latHeaderName; 55 | private long latLifetime; 56 | 57 | public Builder aesKey(String aesKey) { 58 | this.aesKey = aesKey; 59 | return this; 60 | } 61 | 62 | public Builder latHeaderName(String latHeaderName) { 63 | this.latHeaderName = latHeaderName; 64 | return this; 65 | } 66 | 67 | public Builder latLifetime(long latLifetime) { 68 | this.latLifetime = latLifetime; 69 | return this; 70 | } 71 | 72 | public Settings build() { 73 | if (Strings.isNullOrEmpty(aesKey)) { 74 | throw new IllegalArgumentException("AES Key cannot be empty."); 75 | } 76 | if (latLifetime <= 0) { 77 | throw new IllegalArgumentException("LAT lifetime must be postive."); 78 | } 79 | if (Strings.isNullOrEmpty(latHeaderName)) { 80 | latHeaderName = DEFAULT_LAT_HEADER_NAME; 81 | } 82 | return new Settings( 83 | latHeaderName, 84 | latLifetime, 85 | aesKey); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/TokenEndpoint.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos; 18 | 19 | import java.io.IOException; 20 | import java.io.PrintWriter; 21 | import java.util.logging.Logger; 22 | 23 | import javax.servlet.ServletException; 24 | import javax.servlet.annotation.WebServlet; 25 | import javax.servlet.http.HttpServlet; 26 | import javax.servlet.http.HttpServletRequest; 27 | import javax.servlet.http.HttpServletResponse; 28 | 29 | import com.google.common.base.Strings; 30 | import com.google.dosidos.data.OpaqueLat; 31 | import com.google.dosidos.data.TokenRequest; 32 | import com.google.dosidos.data.TokenResponse; 33 | import com.google.dosidos.data.TokenRequest.Action; 34 | import com.google.gson.Gson; 35 | 36 | @WebServlet 37 | public class TokenEndpoint extends HttpServlet { 38 | private static final long serialVersionUID = 1L; 39 | private static final Gson GSON = new Gson(); 40 | private static final Logger log = Logger.getLogger( 41 | TokenEndpoint.class.getName()); 42 | 43 | private TwoTokenManager getTwoTokenManager() { 44 | return (TwoTokenManager) getServletContext().getAttribute( 45 | AppContextListener.ATTR_TWO_TOKEN_MANAGER); 46 | } 47 | 48 | @Override 49 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, 50 | IOException { 51 | TokenRequest tokenRequest = parseTokenRequest(req, getTwoTokenManager()); 52 | TokenResponse tokenResponse = handleTokenRequest( 53 | req, resp, tokenRequest, req.getServerName()); 54 | resp.setContentType("application/json"); 55 | PrintWriter out = resp.getWriter(); 56 | String json = GSON.toJson(tokenResponse); 57 | log.warning("Sends resonson: " + json); 58 | out.write(json); 59 | } 60 | 61 | private TokenRequest parseTokenRequest( 62 | HttpServletRequest req, TwoTokenManager twoTokenManager) { 63 | String token = twoTokenManager.parseLatHeader(req); 64 | Action action = null; 65 | String actionStr = req.getParameter("action"); 66 | if (!Strings.isNullOrEmpty(actionStr)) { 67 | try { 68 | action = Action.valueOf(actionStr); 69 | } catch (IllegalArgumentException e) { 70 | } 71 | } 72 | log.warning("Recieved rquest: " + actionStr + " with token size " 73 | + (token == null ? 0 : token.length())); 74 | return new TokenRequest(action, token); 75 | } 76 | 77 | private TokenResponse handleTokenRequest( 78 | HttpServletRequest req, 79 | HttpServletResponse resp, 80 | TokenRequest tokenRequest, 81 | String audience) { 82 | TwoTokenManager twoTokenManager = getTwoTokenManager(); 83 | if (tokenRequest.getAction() == Action.END) { 84 | // Service worker initiated log out. 85 | twoTokenManager.clearSessionCookie(req, resp); 86 | return generateEndResponse("Session ended in response to client request."); 87 | } else if (tokenRequest.getAction() != Action.REFRESH_BY_LAT) { 88 | return generateErrorResponse("Only support REFRESH_BY_LAT for now"); 89 | } 90 | if (Strings.isNullOrEmpty(tokenRequest.getLat())) { 91 | return generateErrorResponse("Not valid LAT found."); 92 | } 93 | OpaqueLat lat = twoTokenManager.parseLat(tokenRequest.getLat()); 94 | if (lat == null) { 95 | return generateErrorResponse("Invalid LAT."); 96 | } 97 | if (!audience.equals(lat.getAudience())) { 98 | return generateEndResponse("LAT audience is invalid."); 99 | } 100 | if (lat.getExpiration() >= 0 && 101 | lat.getExpiration() <= System.currentTimeMillis()) { 102 | return generateEndResponse("Expired LAT."); 103 | } 104 | boolean bigAccountChanged; 105 | try { 106 | bigAccountChanged = twoTokenManager.hasBigAccountChangeSince( 107 | lat.getUserId(), lat.getIssueAt()); 108 | } catch (IllegalArgumentException e) { 109 | return generateErrorResponse("Invalid LAT."); 110 | } 111 | if (bigAccountChanged) { 112 | return generateEndResponse("LAT invalidated due to big account change."); 113 | } 114 | // Refresh SAT. 115 | long satLifetime = twoTokenManager.issueNewSessionCookie(req, resp, lat.getUserId()); 116 | return generateRefreshedResponse(satLifetime); 117 | } 118 | 119 | private TokenResponse generateErrorResponse(String error) { 120 | return new TokenResponse(error); 121 | } 122 | 123 | private TokenResponse generateEndResponse(String reason) { 124 | return new TokenResponse(TokenResponse.Result.END, -1, reason); 125 | } 126 | 127 | private TokenResponse generateRefreshedResponse(long satLifetime) { 128 | return new TokenResponse(satLifetime); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/TwoTokenHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos; 18 | 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.servlet.http.HttpServletResponse; 21 | 22 | 23 | public interface TwoTokenHandler { 24 | 25 | /** 26 | * Checks if big account changes happened after a LAT issued. 27 | */ 28 | boolean hasBigAccountChangeSince(String userId, long issueAt); 29 | 30 | /** 31 | * Issues a SAT for the specified user, sets the SAT in the session cookie, 32 | * and returns the lifetime of the SAT (so that the service worker can refresh 33 | * SAT after it's expired). 34 | */ 35 | long issueNewSessionCookie( 36 | HttpServletRequest req, HttpServletResponse resp, String userId); 37 | 38 | /** 39 | * Clears the session cookie, so as to sign user out. 40 | */ 41 | void clearSessionCookie(HttpServletRequest req, HttpServletResponse resp); 42 | } 43 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/TwoTokenManager.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos; 18 | 19 | import java.util.Random; 20 | import java.util.logging.Level; 21 | import java.util.logging.Logger; 22 | 23 | import javax.annotation.Nullable; 24 | import javax.servlet.http.HttpServletRequest; 25 | import javax.servlet.http.HttpServletResponse; 26 | 27 | import com.google.common.base.Strings; 28 | import com.google.dosidos.data.OpaqueLat; 29 | import com.google.dosidos.data.SessionState; 30 | import com.google.dosidos.data.SessionState.SessionStateChangeType; 31 | import com.google.dosidos.util.EncryptionUtil; 32 | import com.google.gson.Gson; 33 | 34 | /** 35 | * Allows client to inject its own implementation for below functionalities. 36 | */ 37 | public class TwoTokenManager { 38 | private static final Gson GSON = new Gson(); 39 | private static final Logger log = Logger.getLogger( 40 | TwoTokenManager.class.getName()); 41 | private static Random random = new Random(); 42 | 43 | private final Settings settings; 44 | private final TwoTokenHandler handler; 45 | private final LaunchHook hook; 46 | 47 | public TwoTokenManager( 48 | Settings settings, TwoTokenHandler handler, LaunchHook hook) { 49 | this.settings = settings; 50 | this.handler = handler; 51 | this.hook = hook; 52 | } 53 | 54 | public boolean isEnabled(String userAgent, @Nullable String userId) { 55 | return !Strings.isNullOrEmpty(userId) && 56 | hook.isEnabledForUserAgent(userAgent) && 57 | hook.isEnabledForUser(userId); 58 | } 59 | 60 | public boolean isEnabledForUserAgent(String userAgent) { 61 | return hook.isEnabledForUserAgent(userAgent); 62 | } 63 | 64 | public String parseLatHeader(HttpServletRequest req) { 65 | return req.getHeader(settings.getLatHeaderName()); 66 | } 67 | 68 | public OpaqueLat parseLat(String lat) { 69 | try { 70 | OpaqueLat opaqueLat = GSON.fromJson( 71 | EncryptionUtil.decrypt(lat, settings.getAesKey()), 72 | OpaqueLat.class); 73 | return opaqueLat; 74 | } catch (RuntimeException e) { 75 | log.log(Level.SEVERE, "Error occurs when parsing LAT.", e); 76 | } 77 | return null; 78 | } 79 | 80 | public boolean hasBigAccountChangeSince(String userId, long issueAt) { 81 | return handler.hasBigAccountChangeSince(userId, issueAt); 82 | } 83 | 84 | public long issueNewSessionCookie( 85 | HttpServletRequest req, HttpServletResponse resp, String userId) { 86 | return handler.issueNewSessionCookie(req, resp, userId); 87 | } 88 | 89 | public SessionState generateSessionStateOnLogin(String userId, 90 | String audience, long satLifetime) { 91 | long expiration = System.currentTimeMillis() 92 | + settings.getLatLifetime() * 1000L; 93 | OpaqueLat lat = new OpaqueLat( 94 | userId, 95 | audience, 96 | System.currentTimeMillis(), 97 | expiration, 98 | String.valueOf(random.nextLong())); 99 | return new SessionState( 100 | SessionStateChangeType.NEW, 101 | EncryptionUtil.encrypt(GSON.toJson(lat), settings.getAesKey()), 102 | satLifetime); 103 | } 104 | 105 | public void clearSessionCookie( 106 | HttpServletRequest req, HttpServletResponse resp) { 107 | handler.clearSessionCookie(req, resp); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/data/Lat.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos.data; 18 | 19 | /** 20 | * The data structure to generate the LAT JWT. 21 | */ 22 | public class Lat { 23 | 24 | private String iss; 25 | private String url; 26 | private String aud; 27 | private long exp; 28 | private String lat; 29 | 30 | public Lat() { 31 | this(null, null, null, 0, null); 32 | } 33 | 34 | public Lat(String issuer, String tokenUrl, String audience, 35 | long expiration, String opaqueLat) { 36 | this.iss = issuer; 37 | this.url = tokenUrl; 38 | this.aud = audience; 39 | this.exp = expiration; 40 | this.lat = opaqueLat; 41 | } 42 | 43 | public String getIssuer() { 44 | return iss; 45 | } 46 | 47 | public void setIssuerd(String issuer) { 48 | this.iss = issuer; 49 | } 50 | 51 | public String getTokenEndpointUrl() { 52 | return url; 53 | } 54 | 55 | public void setTokenEndpointUrl(String tokenEndpointUrl) { 56 | this.url = tokenEndpointUrl; 57 | } 58 | 59 | public String getAudience() { 60 | return aud; 61 | } 62 | 63 | public void setAudience(String audience) { 64 | this.aud = audience; 65 | } 66 | 67 | public long getExpiration() { 68 | return exp; 69 | } 70 | 71 | public void setExpiration(long expiration) { 72 | this.exp = expiration; 73 | } 74 | 75 | public String getOpaqueLat() { 76 | return lat; 77 | } 78 | 79 | public void setOpaqueLat(String opaqueLat) { 80 | this.lat = opaqueLat; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/data/OpaqueLat.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos.data; 18 | 19 | /** 20 | * The data structure to generate the LAT opaque string. 21 | */ 22 | public class OpaqueLat { 23 | 24 | private String nonce; 25 | private String sub; 26 | private String aud; 27 | private long iat; 28 | private long exp; 29 | 30 | public OpaqueLat() { 31 | this(null, null, -1, 0, null); 32 | } 33 | 34 | public OpaqueLat(String userId, String audience, long issueAt, 35 | long expiration, String nonce) { 36 | this.sub = userId; 37 | this.aud = audience; 38 | this.iat = issueAt; 39 | this.exp = expiration; 40 | this.nonce = nonce; 41 | } 42 | 43 | public String getUserId() { 44 | return sub; 45 | } 46 | 47 | public void setUserId(String userId) { 48 | this.sub = userId; 49 | } 50 | 51 | public long getIssueAt() { 52 | return iat; 53 | } 54 | 55 | public void setIssueAt(long issueAt) { 56 | this.iat = issueAt; 57 | } 58 | 59 | public String getNonce() { 60 | return nonce; 61 | } 62 | 63 | public void setNonce(String nonce) { 64 | this.nonce = nonce; 65 | } 66 | 67 | public String getAudience() { 68 | return aud; 69 | } 70 | 71 | public void setAudience(String audience) { 72 | this.aud = audience; 73 | } 74 | 75 | public long getExpiration() { 76 | return exp; 77 | } 78 | 79 | public void setExpiration(long expiration) { 80 | this.exp = expiration; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/data/SessionState.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos.data; 18 | 19 | import java.io.Serializable; 20 | 21 | import javax.annotation.Nullable; 22 | 23 | import com.google.common.base.Preconditions; 24 | import com.google.common.base.Strings; 25 | 26 | /** 27 | * This class wraps server side authenticated web session status. It can be 28 | * piggybacked in a normal HTTP response, so as to sync web session status to 29 | * the client side. This could happen in below two cases: 30 | *
    31 | *
  1. After a successful log in, pass the new session info to client side.
  2. 32 | *
  3. After a log out, ask the client side to clear saved auth tokens.
  4. 33 | *
34 | */ 35 | public class SessionState implements Serializable { 36 | private static final long serialVersionUID = 1L; 37 | 38 | private final SessionStateChangeType sessionStateChangeType; 39 | 40 | /** 41 | * In the JWT format, so as to be consistent with federated signon case. 42 | */ 43 | private final String latJwt; 44 | 45 | /* 46 | * The life time of the SAT in milliseconds. Client (service worker) needs to 47 | * refresh the SAT before it expires.

A service worker cannot get the 48 | * expiration time from the session Cookie, since document.cookie doesn't 49 | * return that information. As a result, the SAT expiration time must be 50 | * included in the response.

51 | */ 52 | private final long satLifetime; 53 | 54 | public SessionState() { 55 | this(SessionStateChangeType.END); 56 | } 57 | 58 | public SessionState(SessionStateChangeType sessionStateChangeType) { 59 | this(sessionStateChangeType, null, -1); 60 | } 61 | 62 | public SessionState( 63 | SessionStateChangeType sessionStateChangeType, 64 | @Nullable String latJwt, 65 | long satLifetime) { 66 | this.sessionStateChangeType = sessionStateChangeType; 67 | if (sessionStateChangeType == SessionStateChangeType.NEW) { 68 | // LAT and SAT expiration time must be set for a NEW session state change. 69 | Preconditions.checkArgument(!Strings.isNullOrEmpty(latJwt)); 70 | Preconditions.checkArgument(satLifetime > 0); 71 | this.latJwt = latJwt; 72 | this.satLifetime = satLifetime; 73 | } else { 74 | Preconditions.checkArgument(Strings.isNullOrEmpty(latJwt)); 75 | Preconditions.checkArgument(satLifetime <= 0); 76 | this.latJwt = null; 77 | this.satLifetime = -1; 78 | } 79 | } 80 | 81 | public SessionStateChangeType getSessionStateChangeType() { 82 | return sessionStateChangeType; 83 | } 84 | 85 | public String getLatJwt() { 86 | return latJwt; 87 | } 88 | 89 | public long getSatLifetime() { 90 | return satLifetime; 91 | } 92 | 93 | /** 94 | * Enumerates session state change types. 95 | */ 96 | public enum SessionStateChangeType { 97 | 98 | /** 99 | * A new authenticated web session is just created. To be used after a 100 | * successful log in. 101 | */ 102 | NEW, 103 | 104 | /** 105 | * The authenticated web session is ended. To be used after server side log 106 | * out. 107 | */ 108 | END 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/data/TokenRequest.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos.data; 18 | 19 | /** 20 | * A request to the Token Endpoint. Currently we only support refreshing SAT by 21 | * a LAT. Other refreshing way may be supported by adding a new {@code Action} 22 | * type and additional parameters. 23 | */ 24 | public class TokenRequest { 25 | private final Action action; 26 | 27 | /** 28 | * The LAT will be submitted via HTTP Header. 29 | */ 30 | private final String lat; 31 | 32 | public TokenRequest() { 33 | this(Action.END, null); 34 | } 35 | 36 | public TokenRequest(Action action, String lat) { 37 | this.action = action; 38 | this.lat = lat; 39 | } 40 | 41 | public Action getAction() { 42 | return action; 43 | } 44 | 45 | public String getLat() { 46 | return lat; 47 | } 48 | 49 | /** 50 | * The action type of the request. 51 | */ 52 | public enum Action { 53 | 54 | /* 55 | * Refreshes SAT by providing a LAT. 56 | */ 57 | REFRESH_BY_LAT, 58 | 59 | /* 60 | * Asks server to clear current authenticated web session. To prevent abuse, 61 | * the server may require a valid LAT in the header. 62 | */ 63 | END 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/data/TokenResponse.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos.data; 18 | 19 | import javax.annotation.Nullable; 20 | 21 | /** 22 | * The response from Token Endpoint. 23 | */ 24 | public class TokenResponse { 25 | private final Result result; 26 | private final long satLifetime; // in seconds 27 | private final String error; 28 | 29 | public TokenResponse() { 30 | this(Result.ERROR, -1, null); 31 | } 32 | 33 | public TokenResponse(String error) { 34 | this(Result.ERROR, -1, error); 35 | } 36 | 37 | public TokenResponse(long satLifetime) { 38 | this(Result.REFRESHED, satLifetime, null); 39 | } 40 | 41 | public TokenResponse( 42 | Result result, long satLifetime, @Nullable String error) { 43 | this.result = result; 44 | this.satLifetime = satLifetime; 45 | this.error = error; 46 | } 47 | 48 | public Result getResult() { 49 | return result; 50 | } 51 | 52 | public long getSatLifetime() { 53 | return satLifetime; 54 | } 55 | 56 | public String getError() { 57 | return error; 58 | } 59 | 60 | /** 61 | * The action type of the request. 62 | */ 63 | public enum Result { 64 | 65 | /* 66 | * The refresh request has been processed successfully. 67 | */ 68 | REFRESHED, 69 | 70 | /* 71 | * Error returned. Whether client should retry is undefined for now. 72 | */ 73 | ERROR, 74 | 75 | /* 76 | * The server asks the client the clear current LAT. 77 | */ 78 | END 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/util/CookieUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos.util; 18 | 19 | import javax.servlet.http.Cookie; 20 | import javax.servlet.http.HttpServletRequest; 21 | import javax.servlet.http.HttpServletResponse; 22 | 23 | /** 24 | * Utility methods to handle the cookie. 25 | */ 26 | public class CookieUtil { 27 | public static void setSessionCookie(HttpServletResponse response, String cookieName, 28 | String domain, String cookieValue, int maxAge) { 29 | Cookie cookie = new Cookie(cookieName, cookieValue); 30 | cookie.setDomain(domain); 31 | maxAge = maxAge > 0 ? maxAge : 0; 32 | cookie.setMaxAge(maxAge); 33 | cookie.setPath("/"); 34 | response.addCookie(cookie); 35 | } 36 | 37 | public static void clearSessionCookie(HttpServletResponse response, String cookieName, 38 | String domain) { 39 | Cookie cookie = new Cookie(cookieName, ""); 40 | cookie.setMaxAge(0); 41 | cookie.setDomain(domain); 42 | cookie.setPath("/"); 43 | response.addCookie(cookie); 44 | } 45 | 46 | public static void refreshSessionCookie(HttpServletRequest request, HttpServletResponse response, 47 | String cookieName, String domain, int maxAge) { 48 | Cookie cookie = getSessionCookie(request, cookieName); 49 | if (cookie != null) { 50 | cookie.setMaxAge(maxAge); 51 | cookie.setDomain(domain); 52 | cookie.setPath("/"); 53 | response.addCookie(cookie); 54 | } 55 | } 56 | 57 | public static String getSessionCookieValue(HttpServletRequest request, String cookieName) { 58 | Cookie cookie = getSessionCookie(request, cookieName); 59 | if (cookie != null) { 60 | return cookie.getValue(); 61 | } 62 | return null; 63 | } 64 | 65 | private static Cookie getSessionCookie(HttpServletRequest request, String cookieName) { 66 | Cookie[] cookies = request.getCookies(); 67 | if (cookies != null) { 68 | for (int i = 0; i < cookies.length; i++) { 69 | if (cookieName.equals(cookies[i].getName())) { 70 | return cookies[i]; 71 | } 72 | } 73 | } 74 | return null; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /library/src/main/java/com/google/dosidos/util/EncryptionUtil.java: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | package com.google.dosidos.util; 18 | 19 | import java.io.UnsupportedEncodingException; 20 | import java.security.InvalidKeyException; 21 | import java.security.NoSuchAlgorithmException; 22 | import java.security.SecureRandom; 23 | 24 | import javax.crypto.BadPaddingException; 25 | import javax.crypto.Cipher; 26 | import javax.crypto.IllegalBlockSizeException; 27 | import javax.crypto.NoSuchPaddingException; 28 | import javax.crypto.spec.SecretKeySpec; 29 | import javax.xml.bind.DatatypeConverter; 30 | 31 | import com.google.common.io.BaseEncoding; 32 | 33 | /** 34 | * Uses a key to encrypt/decrypt a cookie value. 35 | */ 36 | public class EncryptionUtil { 37 | 38 | public static String encrypt(String plainText, String key) { 39 | try { 40 | Cipher cipher = Cipher.getInstance("AES"); 41 | cipher.init(Cipher.ENCRYPT_MODE, getAesKey(key)); 42 | byte[] salt = new byte[8]; 43 | SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); 44 | random.nextBytes(salt); 45 | cipher.update(salt); 46 | byte[] encrypted = cipher.doFinal(plainText.getBytes("ISO-8859-1")); 47 | return BaseEncoding.base64Url().encode(encrypted); 48 | } catch (InvalidKeyException e) { 49 | throw new RuntimeException(e); 50 | } catch (NoSuchAlgorithmException e) { 51 | throw new RuntimeException(e); 52 | } catch (NoSuchPaddingException e) { 53 | throw new RuntimeException(e); 54 | } catch (IllegalBlockSizeException e) { 55 | throw new RuntimeException(e); 56 | } catch (BadPaddingException e) { 57 | throw new RuntimeException(e); 58 | } catch (UnsupportedEncodingException e) { 59 | throw new RuntimeException(e); 60 | } 61 | } 62 | 63 | public static String decrypt(String encryptedText, String key) { 64 | try { 65 | byte[] encrypted = BaseEncoding.base64Url().decode(encryptedText); 66 | Cipher cipher = Cipher.getInstance("AES"); 67 | cipher.init(Cipher.DECRYPT_MODE, getAesKey(key)); 68 | byte[] plain = cipher.doFinal(encrypted); 69 | if (plain == null || plain.length <= 8) { 70 | throw new RuntimeException("wrong encrypted text."); 71 | } 72 | byte[] data = new byte[plain.length - 8]; 73 | System.arraycopy(plain, 8, data, 0, data.length); 74 | return new String(data, "ISO-8859-1"); 75 | } catch (InvalidKeyException e) { 76 | throw new RuntimeException(e); 77 | } catch (NoSuchAlgorithmException e) { 78 | throw new RuntimeException(e); 79 | } catch (NoSuchPaddingException e) { 80 | throw new RuntimeException(e); 81 | } catch (IllegalBlockSizeException e) { 82 | throw new RuntimeException(e); 83 | } catch (BadPaddingException e) { 84 | throw new RuntimeException(e); 85 | } catch (UnsupportedEncodingException e) { 86 | throw new RuntimeException(e); 87 | } 88 | } 89 | 90 | private static SecretKeySpec getAesKey(String key) { 91 | final byte[] symKeyData = DatatypeConverter.parseHexBinary(key); 92 | return new SecretKeySpec(symKeyData, "AES"); 93 | } 94 | } 95 | --------------------------------------------------------------------------------