-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
appengine/oauth2 sample #168
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,3 +26,7 @@ dependency-reduced-pom.xml | |
buildNumber.properties | ||
|
||
service-account.json | ||
|
||
# intellij | ||
.idea/ | ||
*.iml |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Eclipse files | ||
.project | ||
.classpath | ||
.settings | ||
|
||
# Target folders | ||
target/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Google App Engine Standard Environment | ||
## Oauth2 Sample | ||
|
||
This sample demonstrates using the Oauth2 apis to create an authenticaion filter. | ||
|
||
See the [Google App Engine standard environment documentation][ae-docs] for more | ||
detailed instructions. | ||
|
||
|
||
## Setup | ||
1. In the [Cloud Developers Console](https://cloud.google.com/console) > API Manager > Credentials, | ||
create a Oauth Client ID for a Web Application. You will need to provide an authroized JavaScript | ||
origin. Typically, https://projectID.appspot.com. | ||
1. Edit `src/main/webapp/index.html` and change `YOUR_CLIENT_ID_HERE.apps.googleusercontent.com` to | ||
Client ID from the prior step. | ||
|
||
## Running locally | ||
NOTE: The app can be run locally, but the Oauth2 APIs do not work with the development server. | ||
|
||
$ mvn appengine:devserver | ||
|
||
## Deploying | ||
$ mvn appengine:update -Dappengine.appId=YOUR-PROJECT-ID -Dappengine.version=SOME-VERSION | ||
|
||
1. Using your browser, visit `https://YOUR-PROJECT-ID.appspot.com`, click Sign In. | ||
|
||
1. The Sign In process will then request some text from your app, and then display it, if | ||
the id matches the list in `src/main/java/com/example/appengine/Oauth2Filter.java`. | ||
|
||
## Adding you to the list of valid users | ||
NOTE: Typically, you would use this for Service Accounts, but user accounts work as well. | ||
|
||
1. Enable logging by uncommenting the context.log line in | ||
`src/main/java/com/example/appengine/Oauth2Filter.java`, redeploy, and visit the page | ||
1. Look at the logs in [Cloud Developers Console](https://cloud.google.com/console) > Logs. | ||
|
||
1. Add the `tokenAudience` to the `allowedClients`. | ||
|
||
1. Deploy and visit the page again. | ||
|
||
[ae-docs]: https://cloud.google.com/appengine/docs/java/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<!-- | ||
Copyright 2015 Google Inc. All Rights Reserved. | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
--> | ||
<project> | ||
<modelVersion>4.0.0</modelVersion> | ||
<packaging>war</packaging> | ||
<version>1.0-SNAPSHOT</version> | ||
<groupId>com.example.appengine</groupId> | ||
<artifactId>appengine-oauth2</artifactId> | ||
|
||
<properties> | ||
<appengine.sdk.version>1.9.34</appengine.sdk.version> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nit, optional] shouldn't need to specify appengine.sdk.version, since the parent also defines such a property. We can keep it here if we want pom.xml to work (mostly) without the parent present. |
||
<maven.compiler.target>1.7</maven.compiler.target> | ||
<maven.compiler.source>1.7</maven.compiler.source> | ||
</properties> | ||
<parent> | ||
<groupId>com.google.cloud</groupId> | ||
<artifactId>doc-samples</artifactId> | ||
<version>1.0.0</version> | ||
<relativePath>../..</relativePath> | ||
</parent> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>com.google.appengine</groupId> | ||
<artifactId>appengine-api-1.0-sdk</artifactId> | ||
<version>${appengine.sdk.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>javax.servlet</groupId> | ||
<artifactId>servlet-api</artifactId> | ||
<type>jar</type> | ||
<scope>provided</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<!-- for hot reload of the web application --> | ||
<outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</outputDirectory> | ||
<plugins> | ||
<plugin> | ||
<groupId>com.google.appengine</groupId> | ||
<artifactId>appengine-maven-plugin</artifactId> | ||
<version>${appengine.sdk.version}</version> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/** | ||
* Copyright 2015 Google Inc. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.example.appengine; | ||
|
||
import java.io.IOException; | ||
import java.io.PrintWriter; | ||
|
||
import javax.servlet.http.HttpServlet; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
|
||
// [START example] | ||
@SuppressWarnings("serial") | ||
public class HelloServlet extends HttpServlet { | ||
|
||
@Override | ||
public void doPost(final HttpServletRequest req, final HttpServletResponse resp) | ||
throws IOException { | ||
PrintWriter out = resp.getWriter(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nit, optional] The default content type is HTML, I believe, so technically we are outputting invalid HTML. Maybe set content type to text/plain? |
||
out.print("Hello, world"); // simple hello world response | ||
} | ||
} | ||
// [END example] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
* Copyright 2016 Google Inc. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.example.appengine; | ||
|
||
import static com.google.appengine.api.utils.SystemProperty.environment; | ||
|
||
import com.google.appengine.api.oauth.OAuthRequestException; | ||
import com.google.appengine.api.oauth.OAuthService; | ||
import com.google.appengine.api.oauth.OAuthServiceFactory; | ||
import com.google.appengine.api.oauth.OAuthServiceFailureException; | ||
import com.google.appengine.api.users.User; | ||
import com.google.appengine.api.utils.SystemProperty; | ||
|
||
import java.io.IOException; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import javax.servlet.Filter; | ||
import javax.servlet.FilterChain; | ||
import javax.servlet.FilterConfig; | ||
import javax.servlet.ServletContext; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.ServletRequest; | ||
import javax.servlet.ServletResponse; | ||
import javax.servlet.http.HttpServletResponse; | ||
|
||
/** | ||
* Filter to verify that request has a "Authorization: Bearer xxxx" header, | ||
* and check if xxxx is authorized to use this app. | ||
* | ||
* Note - this is to demonstrate the OAuth2 APIs, as it is possible to lockdown some | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Javadoc error. Should be |
||
* of your app's URL's using cloud console by adding service accounts to the project. | ||
*/ | ||
public class Oauth2Filter implements Filter { | ||
|
||
private ServletContext context; | ||
|
||
@Override | ||
public void init(final FilterConfig config) throws ServletException { | ||
this.context = config.getServletContext(); | ||
} | ||
|
||
// [START oauth2] | ||
@Override | ||
public void doFilter(final ServletRequest servletReq, final ServletResponse servletResp, | ||
final FilterChain chain) throws IOException, ServletException { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line continuation looks weird. We usually indent exactly 4 spaces for line continuations. I'd also expect to break before |
||
final String scope = "https://www.googleapis.com/auth/userinfo.email"; | ||
Set<String> allowedClients = new HashSet<>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we use an ImmutableSet here and add the clients in the builder? That is, does |
||
|
||
HttpServletResponse resp = (HttpServletResponse) servletResp; | ||
|
||
OAuthService oauth = OAuthServiceFactory.getOAuthService(); | ||
|
||
allowedClients.add("407408718192.apps.googleusercontent.com"); // list of client ids to allow | ||
allowedClients.add("755878275993-j4k7emq6rlupctce1c28enpcrr50vfo1.apps.googleusercontent.com"); | ||
|
||
// Only check Oauth2 when in production, skip if run in development. | ||
SystemProperty.Environment.Value env = environment.value(); | ||
if (env == SystemProperty.Environment.Value.Production) { // APIs only work in Production | ||
try { | ||
User user = oauth.getCurrentUser(scope); | ||
String tokenAudience = oauth.getClientId(scope); | ||
|
||
// The line below is commented out for privacy. | ||
// context.log("tokenAudience: " + tokenAudience); // Account we match | ||
|
||
if (!allowedClients.contains(tokenAudience)) { | ||
throw new OAuthRequestException("audience of token '" + tokenAudience | ||
+ "' is not in allowed list " + allowedClients); | ||
} | ||
} catch (OAuthRequestException ex) { | ||
resp.sendError(HttpServletResponse.SC_NOT_FOUND); // Not allowed | ||
return; | ||
} catch (OAuthServiceFailureException ex) { | ||
resp.sendError(HttpServletResponse.SC_NOT_FOUND); // some failure - reject | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SC_INTERNAL_SERVER_ERROR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer NOT_FOUND when it's auth related - ie lie. I am open to argument, however. |
||
context.log("oauth2 failure", ex); | ||
return; | ||
} | ||
} | ||
chain.doFilter(servletReq, servletResp); // continue processing | ||
} | ||
// [END oauth2] | ||
|
||
@Override | ||
public void destroy() { } | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<!-- [START_EXCLUDE] --> | ||
<!-- | ||
Copyright 2016 Google Inc. All Rights Reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
--> | ||
<!-- [END_EXCLUDE] --> | ||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> | ||
<application>YOUR-PROJECT-ID</application> | ||
<version>YOUR-VERSION-ID</version> | ||
<threadsafe>true</threadsafe> | ||
</appengine-web-app> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" | ||
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" | ||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" | ||
version="2.5"> | ||
<filter> | ||
<filter-name>Oauth2Filter</filter-name> | ||
<filter-class>com.example.appengine.Oauth2Filter</filter-class> | ||
</filter> | ||
<filter-mapping> | ||
<filter-name>Oauth2Filter</filter-name> | ||
<url-pattern>/hello</url-pattern> | ||
</filter-mapping> | ||
|
||
<servlet> | ||
<servlet-name>hello</servlet-name> | ||
<servlet-class>com.example.appengine.HelloServlet</servlet-class> | ||
</servlet> | ||
<servlet-mapping> | ||
<servlet-name>hello</servlet-name> | ||
<url-pattern>/hello</url-pattern> | ||
</servlet-mapping> | ||
|
||
<welcome-file-list> | ||
<welcome-file>index.html</welcome-file> | ||
</welcome-file-list> | ||
</web-app> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. License header? |
||
<head> | ||
<meta name="google-signin-scope" content="profile"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing required elements: title and meta tag for UTF-8 encoding. |
||
<meta name="google-signin-client_id" content="YOUR_CLIENT_ID_HERE.apps.googleusercontent.com" /> | ||
|
||
<script src="https://apis.google.com/js/platform.js" async defer></script> | ||
<script> | ||
function onSignIn(googleUser) { | ||
// Useful data for your client-side scripts: | ||
var access_token = googleUser.getAuthResponse().access_token; | ||
|
||
|
||
var xhr = new XMLHttpRequest(); | ||
xhr.open('POST', '/hello'); | ||
xhr.setRequestHeader("Authorization", "Bearer "+access_token); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nit] I think JS style requires spaces around + operator. |
||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); | ||
xhr.onload = function() { | ||
document.getElementById('response').innerHTML = xhr.responseText; | ||
document.getElementById('me').style = "display:inline;"; | ||
}; | ||
xhr.onError = function() { | ||
document.getElementById('response').innerHTML = "Error"; | ||
document.getElementById('me').style = "display:inline;"; | ||
}; | ||
xhr.send(); | ||
} | ||
</script> | ||
</head> | ||
<body> | ||
<div class="g-signin2" data-onsuccess="onSignIn" data-theme="dark"></div> | ||
<div id="me" style="display: none;"> | ||
<h1><span id="response"></span></h1> | ||
</div> | ||
</body> | ||
</html> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason to put this here instead of the root .gitignore?