Google Cloud Endpoints custom Authentication - android

I am quite new to google cloud endpoints and I would like know how to Use Auth with Endpoints, the tutorial here is good, but I don't understand this thing:
It says, that I should add a user(com.google.appengine.api.users.User
) parameter to backend's methods for auth. If I want to use android as client part, I should provide GoogleAccountCredential object to make an authenticated call [2]. The GoogleAccountCredential is created this way
credential = GoogleAccountCredential.usingAudience(this,
"server:client_id:1-web-app.apps.googleusercontent.com");
credential.setSelectedAccountName(accountName);
The accountName is the name of a Google Account, so I assume, that everyone, who has the Google Account and is using my Google Cloud Endpoint application can create the GoogleAccountCredential object and make an authenticated call to backend.
But there are obviously many methods in my backend, which can be invoked only by some users of my app. (example: There is a method, which will give me a details about my friend, it's clear that this method can be called only by his friends.). Hence my question is: Is there any way to map the com.google.appengine.api.users.User to some my custom User entity, to be possible to check whether the User is really authorized to call the backend's method and not only to know that the method was called by a User with Google Account ? Should I write my custom Authenticator for this, if so, could you advise me how ?
Thank you!

You can set an Authenticator class which will handle the custom authentication.
https://cloud.google.com/appengine/docs/java/endpoints/javadoc/com/google/api/server/spi/config/Authenticator
you just need to set the authenticators param in the #ApiMethod and you can write your own authentication logic

Related

Clarification on Firebase auth and Google endpoints security

I have an android app that authenticates using Firebase. I would like to access a Google endpoints api. I set arguments to the decorator #endpoints.api so that only this app can access it.
I was reading the tutorial at
https://cloud.google.com/endpoints/docs/frameworks/java/calling-from-android,
where it states to use the endpoints service object, I would first have to declare credentials as such,
credential = GoogleAccountCredential.usingAudience(this,
"server:client_id:1-web-app.apps.googleusercontent.com");
and then pass this credential to the service object builder.
However, since I authenticate with Firebase, this credential object would not work. An attempt to do so would output a GoogleAuthIOException error.
I cannot find any information on Firebase credential objects that would work for the service builder's third argument.
The closest I have come to is
AuthCredential credential = GoogleAuthProvider.getCredential(idToken, null);
I was reading up on some possible solutions such as at
Cloud Endpoints with Firebase Authentication, but this solution seems to suggest that I have to set up FirebaseAdminSDK on the endpoints service.
My major query is then this: for Firebase authentication on Android, do I have to pass its id token to the endpoint service which will then verify it using FirebaseAdminSDK as this second link suggests, or can I set up a simple Firebase credential variable to pass into the service object, similar to the example in this post's first link?

Integrating Youtube Data API v3 in android

I am trying to access users YouTube private data like subscriptions which require OAuth authentication. I have tried 2 approaches so far.
1.Using GoogleAuthUtil.getToken() method. But it's deprecated and using this function only access token can be acquired which expires after 1 hour. I have no idea how to retrieve refresh token using this method.
2.According to this article and this answer we should avoid use of getToken method because of security and UX problems. So I tried using Auth.GOOGLE_SIGN_IN_API. According to the documentation, we need to use GoogleSignInOptions. So I used it and using requestScopes() i added the Youtube scope. Also I used requestserverauthcode() to get the exchange code, using which access and refresh token can be acquired. Now here's my problem, we need to use GoogleApiClient with addApi(). The available set of API's doesn't have a Youtube API listed to put it in addApi(). So there is no way I can get access to access tokens / refresh token.
Can anyone help me out?
EDIT:
I solved it. I used getToken approach to get tokens. Whenever a 401 authorization error occurs, we need to call getToken method returns a new token.

Protecting my Google App Engine API Endpoints

I have been doing a lot of research recently on securing my app engine. Currently, I've been reading through the question below and the links in that question:
How do I restrict Google App Engine Endpoints API access to only my Android applications?
However, it doesn't answer my problem. My question is similar to the question above, restricting access to my endpoint API to only my app. The guy seemed to have got it working when he inputs a correct email into the credentials.
My question is if I can achieve the same results without having to input any credentials. I want it so that only my app can use my endpoint API so to prevent other apps from abusing it and using up my quota. I already got a client id for my android application, and have placed it within my #API annotation. To test if it worked, I made a random value for the client id in the #API notation of another api class. However, my app was still able to use methods from both class. Any help?
-Edit-
From reading from the docs and researching further, the endpoint way of authorizing apps is by authenticating the user and for my API to check if user is null. My question is that in the process of authenticating the user, is Google somehow able to read my app's SHA1 fingerprint and authorize it to its list of client ids? If so, how can I replicate this process in my endpoint so that I check the SHA1 fingerprint of the app making the request and compare it to a set value? I don't understand the mechanics behind the endpoints very well, so correct me if I am understanding this wrong.
If the android app has access, then the user has access. A motivated party has many options for inspecting your protocol, including putting the device behind transparent proxy or simply running the app through a debugger. I do suggest running your app through ProGuard before publishing, as this will make the process [a bit] more difficult.
Ultimately, you'll need to make your appengine API robust against untrusted parties. This is simply the state of the web.
How you can protect your endpoint API is described here: http://android-developers.blogspot.com/2013/01/verifying-back-end-calls-from-android.html
The secret is that you request a token from Google Play using the following scope: audience:server:client_id:9414861317621.apps.googleusercontent.com where 9414861317621.apps.googleusercontent.com is your ClientId.
Google Play will look up the id at your endpoints app and return a Google-signed JSON Web Token if it finds the id. Then you pass that id in with your request. Above article says you should pass it in with the body. I would possibly rather add another parameter for that because otherwise you can't pass your own entities anymore. Anyway, your server backend receives the token, and you ask Google as described if it is authentic, before you process the API request.
If you pass in the token using an extra parameter, you can catch it on the server side by adding HttpServletRequest to your endpoint signature and then using request.getHeader("Yourname") to read it out. Make sure you never add the parameter as a URL parameter as it may be logged somewhere.
public void endpointmethod(
// ... your own parameters here
final HttpServletRequest request
) throws ServiceException, OAuthRequestException {
request.getHeader("YourHeaderName") // read your header here, authenticate it with Google and raise OAuthRequestException if it can't be validated
On the Android side you can pass in your token when you build the endpoint api, like this, so you don't have to do it with each and every request:
Yourapiname.Builder builder = new Yourapiname.Builder(AndroidHttp.newCompatibleTransport(), getJsonFactory(), new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) {
httpRequest.setHeader(...);
}})
Hope this helps you make your endpoints API secure. It should.

Authenticate my "app" to Google Cloud Endpoints not a "user"

What I'm trying to do is to authenticate my Android app to the Google Cloud Endpoint.
Basically the endpoints should only allow my Android app to access the methods and nothing else.
I have done these things -
Create a client id using my SHA1 value in Eclipse in the Google Cloud Console.
Create a web client id in the Google Cloud Console for my endpoint project.
Add both these client id's in the "#Api" mentioned on each endpoint.
Add an extra "user" parameter in the endpoint methods.
Regenerate and deploy the backend to the cloud.
But when I'm running this the "user" is always coming as "null". I'm at my wits end trying to find a proper working method for doing all this.
I've searched many forums but no proper answers anywhere.
Here's another similar post Restrict access to google cloud endpoints to Android app
This is the reference I'm using -
https://developers.google.com/appengine/docs/java/endpoints/auth
Has anyone here done this before? My main goal is to not allow unauthenticated apps and outside world to access the endpoints, for obvious security reasons. I don't want to use end-user based authentication since I want to keep my app very simple.
It sounds like it's working as intended. You control which client apps can call your endpoint methods via the client IDs as you have already done. The User parameter is coming in as null precisely because you aren't doing end-user authentication. The User parameter represents an actual real user (Google Account). So if you don't need end-user authenticated methods, you can just simply not define the User parameter, or else ignore the null value. You said your problem is that the User parameter is set null. What are you expecting it to be in this scenario?
You need to call authenticate on the client, then possibly the library you're using will 'inject' the user information.
Here's what worked for me :
Let's say you have the keys below :
static final String WEB_CLIENT_ID = "somekeyfor webclientid.apps.googleusercontent.com";
static final String ANDROID_CLIENT_ID = "somekeyfor androidclientid.apps.googleusercontent.com";
static final String ANDROID_AUDIENCE = WEB_CLIENT_ID;
Your Api anotation should look like this :
#Api(
name = "yourapiname",
clientIds = {CloudEndpoint.WEB_CLIENT_ID,CloudEndpoint.ANDROID_CLIENT_ID},
audiences = {CloudEndpoint.ANDROID_AUDIENCE},
version = "v1",
namespace = #ApiNamespace(
ownerDomain = "myapp.app.com",
ownerName = "myapp.app.com",
packagePath = ""
)
)
In the annotation below, notice how your audience is the variable --> ANDROID_AUDIENCE which is equal to WEB_CLIENT_ID.
Now in your app side, when you create the googleAccountCredential object, you should pass in the Web Client Id like this :
mAccountCredentials = GoogleAccountCredential.usingAudience(getApplicationContext(),"server:client_id:" + "yourwebclientID");
Note that even if this is properly done, your user object in the endpoint might still coming out as Null if the account name you pass in mAccountCredentials.setSelectedAccountName("accontname") does not exist in the device. Therefore make sure the account name you pass does exist in the Android device by going to --> (Settings/Accounts)

Android subscription and Google API

I'm trying to use the new Android subscription system from Google Play into my application (I already had in-app billing working fine). I have successfully done the subscription billing, but I now want to retrieve informations about this subscription by using the google apis as indicated in the android documentation (http://developer.android.com/guide/market/billing/billing_subscriptions.html).
I want my service to be able to do the API call to retrieve these informations, but I have problems with authentication (with oauth2). So far, this is what I do (in my php service) :
require_once 'google-api-php-client/src/apiClient.php'
const SERVICE_ACCOUNT_NAME = 'email from services account access';
$key = 'content of my private key retrieved from services account access';
$client = new apiClient();
$cred = new apiAssertionCredentials(SERVICE_ACCOUNT_NAME, array('https://www.googleapis.com/auth/androidpublisher'), $key);
$assertion = $cred->generateAssertion(); // This generate my encrypted JWT
I then try to retrieve the access token with this JWT object. The problem is that when I use the access token given I got the error that the developer account does not own the application, which is not true.
(I know this is not the way to do it, but I just wanted to retrieve the access_token using the JWT to understand why it is not working, if I do it as indicated in the google apis documentation it is not working too).
I need to do this API call from a server, so no end-user has to be involved (no manual consent).
I had the same issue, and ultimately discovered that as of right now service accounts can not access the Play API.
I'm not sure when Google is planning on fixing this but you can get around this by creating a web app client ID and setting up a basic login page to first generate a code using the new web app Client data and going to $client->createAuthUrl():
$client = new apiClient();
$key = file_get_contents(KEY_FILE);
$client->setClientId(CLIENT_ID);
$client->setClientSecret(CLIENT_SECRET);
$client->setRedirectUri(MY_WEBAPP_URL);
$client->setDeveloperKey($key);
$client->setScopes(array('https://www.googleapis.com/auth/androidpublisher'));
$authUrl = $client->createAuthUrl();
print "<a class='login' href='$authUrl'>Connect Me!</a>";
This should take you to a Google login page where you should log in with the developer account. When you authorize the app, it will take you back to your web app URL as defined when you set up the client ID with a CODE as a get parameter. You can use to generate a token (and more importantly, a refresh token) like so:
$url = 'https://accounts.google.com/o/oauth2/token';
$fields = array(
'grant_type'=>'authorization_code',
'code'=>$code,
'client_id'=>CLIENT_ID,
'client_secret'=>CLIENT_SECRET,
'redirect_uri'=>MY_WEBAPP_URL
);
// cURL call to OAuth URL with $fields sent as POST
This should return you JSON data with a refresh token. Save this token and use it to make another call whenever you need to generate an access token. You will essentially run the same code you did to get the refresh token, but with different fields:
$fields = array(
'grant_type'=>'refresh_token',
'refresh_token'=>$refresh_token,
'client_id'=>CLIENT_ID,
'client_secret'=>CLIENT_SECRET,
);
This will give you an access token you can use to get purchase data from the following URL:
https://www.googleapis.com/androidpublisher/v1/applications/[PACKAGE]/subscriptions/[SKU]/purchases/[PURCHASE_TOKEN]?access_token=[ACCESS_TOKEN]
The trick is getting the refresh token, once you have that the rest should be pretty straightforward.
I`ve got the same problem. It occurs because you authorize user in Google API who does not own the application and try to get data which belows to your app.
In this topic it is well described. http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=2528691&topic=16285&ctx=topic
You should authorize by OAuth2 the owner of application, and then use Google API with obtained token.

Categories

Resources