I have a Google Cloud Endpoint which I access from an HTML page through JavaScript and the Google JavaScript client Library.
I authenticate with OAuth2.0 by using the standard
gapi.auth.authorize({client_id: CLIENT_ID, scope: SCOPES, immediate: mode}, callback);
Everything works correctly and I am able to read/write data from/to the underlying Datastore.
In the same AppEngine project I have a servlet that generates a PDF based on data that is in the Datastore.
I would like to be able to call this Servlet from my HTML page using the same user that was authenticated through the api.auth.authorize() method.
And in the servlet, get the User through
UserService userService = UserServiceFactory.getUserService();
and query the datastore for the data of this user and then generate a PDF showing this data.
I have no idea how to call this url (servlet) with the credentials of the OAuth autheticated user.
Can you help me please??
Thanks in advance!
Note that the same question was asked some months ago but without a "complete" answer: GAE User API with OAuth2
You should look into bucket/object ACLs. When your API endpoint gets the User object, it can use the user's email to set the ACL on the PDF which is generated. That way, you can serve the PDF file to the user simply using its URL. You could also check with an endpoints API call whether the user is indeed authenticated as the person who is allowed to access the requested PDF (having stored a Datastore entry, perhaps, parallel to the object), and generate a signed URL once this is confirmed.
Related
I have 3 applications:
An IdentityServer4 API which provides Google authentication and also provides an access token to authorize the resource API.
A simple Resource API which provides some data from DB.
A simple Client in React which have 4 buttons:
Login, for Google auth
Logout
Get data - a simple request with the access token to the Resource API and gets the data from Db
Get user data - returns user profile and token (for debug purpose)
I didn't put any sample code because my problem is not code related, it's knowledge that I'm missing and I ask for guidance.
The workflow is working just fine: the user press the Login button, it is redirected to IdentityServer4 API for Google Auth. From there it is redirected to a Callback Page from the Client and from there to the Index page. I receive the user data and the token, I can request data from the Resource API and it's working.
My problem is: How do I give a Role to the Google Users ?
I don't have users saved in DB. I want three types of Users: SuperAdmin, Admin, Viewer and each of these roles have limited Endpoints which can access.
For limiting their access I saw that I can use Claims-based authorization or Role-based authorization.
So, my question is how ca I give a Google User who wants to login in my app, a specific Claim/Role ? What is the workflow ? I must save it first in DB ? Or there exists a service from Google where I can add an email address and select a Role for that address ?
Thank you very much !
After you get the response from Google in your callback you can handle the user and do what ever you want to do with it. Below are the some typical tasks that you can do in callback that I took from documentation page of identityserver4 link:
Handling the callback and signing in the user
On the callback page your typical tasks are:
inspect the identity returned by the external provider.
make a decision how you want to deal with that user. This might be
different based on the fact if this is a new user or a returning
user.
new users might need additional steps and UI before they are allowed
in.
probably create a new internal user account that is linked to the
external provider.
store the external claims that you want to keep.
delete the temporary cookie
sign-in the user
What I would do is creating an new internal user account that is linked to the external provider and add a role to that user.
If you don't want to save users in db, you can add an extra claim to user in callback method and use that claim in token. and i think this link will help with that.
I would like to make authenticated requests to a an App Engine app. I am having trouble setting up Cloud Endpoints(I have failed to follow the tutorial many times.) so I am thinking about an alternative to retrieve user data from the datastore. Can I just return a json object after a user makes a request to a specific URL?
For example:
I have a webapp called Library of Books. It's an awesome app that simply allows authenticated users to keep track of a list of their favourite books. This is the Book Entity:
The user can access his list of fav books by visiting /my-fav-books. When visiting this url, the user sends his cookies which would validate his authenticated session and the server could then return the user's list of books via a json object.
Could this work?
I am having troubles in implementing OAuth in the right way.
I use a client/API architecture (Angular for front and Node.js for back) and I would like user to sign in using Google OAuth authentication only.
Here is what I think is the right way for the moment (tell me if I misunderstood something) :
Angular open a Google popup asking user's consent.
Once the user agree, Google Authorization server sends back to angular a verification code.
This verification code is forwarded to an API endpoint.
Then, the API asks Google Authorization server to exchange this code for an access_token, an id_token and a refresh_token.
Google sends those 3 tokens.
The API uses access_token to retrieve user from Google API
The API persists the user
Here is the little dillema, in my opinion, the access_token and refresh_token should be stored into the database and the id_token should be sent back to Angular client.
This way, it would allow the API to ask for resource in Google API and if the token expires it can still ask for a new token thanks to the refresh_token.
Client-side, the id_token is embedded in all requests thus allowing the API to identify the client and verify his authentication with Google certs from https://www.googleapis.com/oauth2/v3/certs.
Supposing this is right way to use tokens, how could I deal with id_token expiration since client does not have any refresh token ?
Thanks !
I do it slightly different (I have the same basic architecture though).
Angular decides the user needs to log in and displays a login popup.
The url in the login popup is not serviced by angular, but is instead directly run off of the backend server: /auth/google . (I use hapijs and bell, personally).
/auth/google is serviced by a bell plugin and initiates the OAUTH dance.
the end of the OAUTH dance results in my node server generating a local token (I just generate random bytes and store them in redis mapped to user ids)
because the initial login popup was created by window.open, the success page (generated on the api side rather than in angular) can use window.opener.postMessage to communicate the token back to the angular runtime.
This way, all my sensitive google credentials (the user's oauth token, refresh token if needed, and my application's api ID and secret) are only on the server, except for during the OAUTH dance relay when they're in a URL string during the client redirects. This is reasonably secure.
Then for all the actual user interactions with the api, I use the token I generated in step four to authenticate. This could be a JWT if you wanted, but I don't do it that way; I just use redis to map from 'longrandostring' -> userId. That lets me (for example) force everyone to re-login if I wipe the redis database that has all the tokens stored, or I can write a lua script to delete all the entries that map to a certain userid.
If you need a refresh token, you can set access_type=offline in the initial request to oauth2/auth, and you'll get a refresh token as part of the response, unless you've previously gotten a refresh token. You can then persist it on the server side and get new access tokens as needed. If you set approval_prompt=force as well, you'll force a new consent screen and be guaranteed a refresh token (but after some small number of refresh tokens granted to a user, older ones expire on the same application so it's best to only request them if really needed).
My Setup
Backend: Google App Engine (Java) w/ Google Cloud Endpoints using Endpoint's built in authentication
Frontend: AngularJS web app
Problem
I need to get the Google+ profile for my users. The keyword "me" can generally be used to get the current user's Google+ profile, however since all the authentication, in Google Cloud Endpoints, is done under the hood, I don't see anyway to get credentials, nor a token, for the current user. All you get it the com.google.appengine.api.users.User object.
Is there any way to get user credentials, or the access token, when using Google Cloud Endpoint's built in authentication?
Note: Google+ profile ID is different form Google account ID.
Possible Solution
I could just use the Google+ JS client with the keyword "me" and have the user send their Google+ ID and then subsequently store it and tie it to their Google Account ID, but this would be incredible insecure as the user could hack their way to sending the ID of any Google+ account.
It is possible to get the user access token when using Google Cloud Endpoint's built in authentication.
Add the parameter HttpServletRequest request to your Google Cloud endpoint as shown below. This will allow you to get the raw request.
You will then need to retreive the header called Authentication. This will get a Bearer access token that will allow you to build credentials to impersonate the authenticated user.
Next you will use that Bearer access token to build a com.google.api.client.googleapis.auth.oauth2.GoogleCredential object. You will need this to build the Plus service.
Use the Plus builder to build a Plus service object with the credential you just created.
Sample Code
#ApiMethod(path = "myPath")
public void myEndpoint(HttpServletRequest request, ParmOne paramOne, ...) throws OAuthRequestException {
if (user == null) {
throw new OAuthRequestException("Authentication error!");
}
GoogleCredential credentialAsUser = new GoogleCredential().setAccessToken(request.getHeader("Authorization").substring(7)); // Start string at index position 7 to remove prefix "Bearer" from token.
Plus plus = new Plus.Builder(new UrlFetchTransport(), new JacksonFactory(), credentialAsUser).setApplicationName("my-app").build();
Person profile = plus.people().get("me").execute();
}
Documentation
The Java docs for the Google Plus client can be found here.
The Java docs for instructions on creating Google credentials can be found here.
Additional Answer for Android Clients
Problem
In addition to the Marc's answer it is important that the GoogleCredentials-Object needs an access_token in the request-header.
If you call the endpoint with your apiExplorer or a javascript endpoint, this token is already served in the Authorization-header. But if you follow the docs for an android client your requests header contains an id_token, so GoogleCredentials.setAccessToken does not work.
Solution
To change the type of authorization to an access_token simply create your GoogleAccountCredential-Object in Android with usingOAuth2 instead of usingAudience.
Example
Replace this code in your Android App
credential = GoogleAccountCredential.usingAudience(this,
"server:client_id:1-web-app.apps.googleusercontent.com");
with this
credential = GoogleAccountCredential.usingOAuth2(this,
Collections.singleton(Scopes.PLUS_LOGIN));
and send it to your Api, as it is explained by the documentation
Helloworld.Builder helloWorld = new Helloworld.Builder(AppConstants.HTTP_TRANSPORT,
AppConstants.JSON_FACTORY,credential);
I have run through the google example of using Authsub to retrieve Google feed data (http://code.google.com/appengine/articles/python/retrieving_gdata_feeds.html)
If I understand correctly, it is possible for a malicious hacker to call the 'next_url' (which google auth service calls with your token) and inject their own token?
Meaning that they could cause the Web apps to write to the Hackers google doc account instead of the authenticated user!
Does anyone know how to secure this url so that only google auth service can call it?
Below is the code I am referring to:
def get(self):
next_url = atom.url.Url('http', settings.HOST_NAME, path='/step1')
# Initialize a client to talk to Google Data API services.
client = gdata.service.GDataService()
gdata.alt.appengine.run_on_appengine(client)
# Generate the AuthSub URL and write a page that includes the link
self.response.out.write("""<html><body>
Request token for the Google Documents Scope
</body></html>""" % client.GenerateAuthSubURL(next_url,
('http://docs.google.com/feeds/',), secure=False, session=True))