Store Google+ friends list on backend server - android

I've integrated Google+ sign-in in my Android application. Now I wan't to get the user's friend list and store it on the server. I can't get the friends list on the client app and send it to the server since the data can be easily tampered. So I thought about generating an access token using the following code and send it to the server, which the server will then use to query the Google+ API and get the user's friends.
String accountName = Plus.AccountApi.getAccountName(mGoogleApiClient);
Account account = new Account(accountName, GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
String scope = "oauth2:" + Constants.SCOPE_PLUS_LOGIN + " " + Constants.SCOPE_EMAIL;
try {
String accessToken = GoogleAuthUtil.getToken(getApplicationContext(), account, scope);
} catch (IOException | GoogleAuthException e) {
e.printStackTrace();
}
But is sending the access token to the server directly using https safe enough? Cause if the token is compromised then any third party can use it to steal the user's personal information.
Or is there any better way to fetch and store the signed in user's friend list on the server?

1) Always transfer access_tokens over HTTPS.
2) Don't build any way to get access_tokens from your server. Make sure the server only supports sending access_tokens to it.
3) access_tokens expire after one hour so the window for abuse is limited.

Related

Android login via Google Sign-In with a Spring Boot backend

As the title says, I'm trying to use the Google Sign-In API with a Spring Boot backend server, as described here.
Just to describe the context, the Spring backend is basically a resource+authentication server, that is currently providing Oauth2 authentication to a second spring boot application containing the frontend website, via Google SSO or simple form login (similar to what's described here).
My original idea was to mimic the #EnableOauth2Sso annotation by simply providing an access token to the android app and attach it to every request as "Bearer ".
Using the user credentials for this was pretty straightforward: I simply make a request to the server at "/oauth/token", using those credentials inserted by the user as authentication and I correctly receive the access token.
Now, I have absolutely no idea on how to build a similar procedure with the Google API in Android. The tutorial page I linked before describes how to get a token ID and how the server should validate it, but after that I don't know what to do.
So far I've managed to add a filter to the security chain that simply checks the token like this:
private Authentication attemptOpenIDAuthentication(#NonNull String tokenString){
String clientId = authServices.getClientId();
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, factory)
.setAudience(Arrays.asList(clientId, androidClient))
.build();
try {
GoogleIdToken token = verifier.verify(tokenString);
if (token != null) {
return authServices.loadAuthentication(token.getPayload());
} else {
throw new InvalidTokenException("ID token is null");
}
} catch (GeneralSecurityException | IOException e) {
throw new BadCredentialsException("Could not validate ID token");
}
}
This manages indeed to create an Authentication object, but how can I generate an access token after the authentication filtering?
To recap, so far I've got:
The Android app successfully retrieves the Google token ID and sends it to the server
The server sucessfully intercepts the request and validates the token
I'm basically missing the third point where I return a proper access token to the Android client.
Here you are a simple scheme to better understand the situation:
Is there any other way to validate the token and get an access token from the server, or should I completely change the authentication procedure on Android?
As far as I can tell: Yes, you need an access token from the server. If I understand this correctly, a webapp is already authenticated via Oauth on your backend, so the procedure is similar here: Load the user with the google-ID and generate a token. In my application I used a JWT which is valid for 30 days. If the token expires, the Google authentication in the app is usually still valid, so the token can be renewed using the Google ID. With Oauth you can also send a refresh-token directly.
It is important that the app always checks the Google authentication first and only in a second step that of the backend.
For the Authentication process on the backend u may need to manually implement a dedicated securityConfiguration for this. Have a look at the jhipster project, they implemented a custom jwt-authentication which may give you an idea how it works.

accountmanager refresh token(offline access)

I use google login through account manager in my android app. I can get accesstoken which I send to server and server can create/login new user. Accesstoken is valid only 3600seconds. Problem is that server can not update user's info after this time expires. Web app requires check user's info periodically.
How can I get authentication token and refresh token from android account manager so than server can use refresh token to update data periodically? I don't want use login through webview in android app.
Thanks
This is now possible: https://developers.google.com/+/mobile/android/sign-in#server-side_access_for_your_app
You request a one-time authorisation code, send it to your server and your server exchanges it for an access token and refresh token.
Currently you can't, which I'm sure is not the answer you're hoping for, sorry about that! If you have a web sign in, you can use the hybrid flow to get a refresh token on the server as well (see https://developers.google.com/+/web/signin/server-side-flow), but there's no way of retrieving a code as part of the Android or iOS flows.
If this is something that you need for you use case, could you file a feature request on here: https://code.google.com/p/google-plus-platform/issues - we are actively looking at the number of stars on that to gauge demand for various features.
Google Authorization process through account manager:
Email id can be got from
AccountManager accountManager = AccountManager.get(getApplicationContext());
Account[] accounts = accountManager.getAccountsByType("com.google");
String emailID = accounts[0].name; // you can retrieve using google account chooser way also
These lines should be run in separate token(not in UI thread).
String scope = "oauth2:https://www.googleapis.com/auth/userinfo.profile https://gdata.youtube.com";
String accessToken = GoogleAuthUtil.getToken(mContext, emailID, scope);
save the the accessToken and use for your api access.
After one hour (i.e. 3600 seconds), we need to refresh the access token. But now google is not supporting access after one hour. We have to restart the application and use the following lines to get access token.
String scope = "oauth2:https://www.googleapis.com/auth/userinfo.profile https://gdata.youtube.com";
String accessToken = GoogleAuthUtil.getToken(mContext, emailID, scope);
This background thread will always run in background in while loop

Invalid OAUTH access token, when using an application access token, Android

As the title says, when I try to request to get the friends list with the field installed:
"me/friends?Fields=installed&access_token=..."
I get the following error in my logcat:
"Invalid OAuth access token"
When looking on the facebook api I see that installed needs to take an application access token. So I generated the application access token using the appId and app Secret using:
https://graph.facebook.com/oauth/access_token?grant_type=client_credentials&client_id=APP_ID&client_secret=APP_SECRET
Below is the code:
try {
JSONObject obj = Util.parseJson(mFacebook.request("me/friends?fields=installed&access_token=..."));
Log.d("json Response", obj.toString());
JSONArray array = obj.optJSONArray("data");
if (array != null) {
for (int i = 0; i < array.length(); i++) {
// String name = array.getJSONObject(i).getString("name");
String id = array.getJSONObject(i).getString("id");
Log.d("Friends: ",id);
}
}
}catch (Exception e){
Log.d("Friends:", e.getMessage());
}
Any one any ideas why its doing this I have been searching for ages.
Cheers
What you are getting after authentication is App Access Token.
Quoting : Authenticating as an App allows you to obtain an access token which allows you to make request to the Facebook API on behalf of an App rather than a User. This is useful, for example to modify the parameters of your App, create and manage test users, or read your application's insights for example. App access tokens can also be used to publish content to Facebook on behalf of a user who has granted a publishing permission to your application
from facebook docs.
What you need for your case is an user access token. Refer this.
A user needs to authenticate and grant access to your app for you have access to his friends list.
EDIT
For checking for a single user the url is
https://graph.facebook.com/{uid}?fields=installed&access_token=< ACCESS_TOKEN >
You are trying to get user friend list and then check if the app is installed. I don't think getting user friend list is possible without user access token.
EDIT 2
Assuming from your comment that you have the list of frind's. Then you need not call for each user instead you can use FQL.
https://graph.facebook.com/fql?q=SELECT uid,is_app_user from user where uid IN (uid1,uid2)
And if you have the user access token then you can directly do.
https://graph.facebook.com/fql?q=SELECT uid,is_app_user from user where uid IN (SELECT uid2 FROM friend WHERE uid1 = me())
Hope this solves your problem.
Don’t request /me/friends – because with an app access token the API won’t know who „me” is supposed to be – request /userid/friends instead.
Sorry, I was wrong before – the way to go is with an user access token, and just setting the request up against /me/friends…

Obtaining a basic google auth-token from AccountManager

I want to obtain a Google Authtoken from the AccountManager that I can send to my Webservice (not hosted on App Engine) to authenticate the User (I just need the email address and eventually his name, if no permission is required for this).
What do I have to use for the "authTokenType" Paramter of the "getAuthToken" method?
And which google Api do I have to use to get the Users Email?
This is doable using OpenID Connect, however it's sort of experimental, so details could change in the future. If you get an OAuth token for the 'https://www.googleapis.com/auth/userinfo.email' or 'https://www.googleapis.com/auth/userinfo.profile' scope you can use it to get user info from https://www.googleapis.com/oauth2/v1/userinfo (including email). Of course the user needs to authorize this.
You should theoretically be able to get the token from AcccountManager using the "oauth2:https://www.googleapis.com/auth/userinfo.profile" as the token type, but that doesn't appear to work on my device (Galaxy Nexus with stock 4.0.4). Since getting a token via the AccountManager doesn't work (at least for now), the only reliable way is to use a WebView and get one via the browser as described here: https://developers.google.com/accounts/docs/MobileApps
There is a demo web app here that does this: https://oauthssodemo.appspot.com
(late) Update: Google Play Services has been released and it is the preferred way to get an OAuth token. It should be available on all devices with Android 2.2 and later. Getting a profile token does work with it, in fact they use it in the demo app
I have had problems with this as well, since I was not able to find anything like a reference. Perhaps this can help you (code copied from an Android example on using the account manager):
Somewhere in an event handler of your Android app, issue a request for an auth token to get the user's email address in Android:
_accountMgr = AccountManager.get(this);
Account [] accounts = _accountMgr.getAccounts();
Account account = accounts[0]; // For me this is Google, still need to figure out how to get it by name.
_accountMgr.getAuthToken(account, AUTH_TOKEN_TYPE, false, new GetAuthTokenCallback(), null);
In the callback, extract the access token:
private class GetAuthTokenCallback implements AccountManagerCallback<Bundle> {
public void run(AccountManagerFuture<Bundle> result) {
Bundle bundle;
try {
bundle = result.getResult();
final String access_token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
// store token somewhere you can supply it to your web server.
} catch (Exception e) {
// do something here.
}
}
}
Make some request to your web server, supplying the access token.
On the web server, validate the access token and obtain the email address:
curl -d 'access_token=<this is the token the app sent you>' https://www.googleapis.com/oauth2/v1/tokeninfo
You should get something like this:
{
"issued_to": "<something>.apps.googleusercontent.com",
"audience": "<something>.apps.googleusercontent.com",
"scope": "https://www.googleapis.com/auth/userinfo.email",
"expires_in": 3562,
"email": "<users email address>",
"verified_email": true,
"access_type": "online"
}
or if something went wrong:
{
"error": "invalid_token",
"error_description": "Bad Request"
}
You can get the User's name with the Google+ People API. (It will not provide the user's email address).
If this is OK, you can use "Know who you are on Google" as the authTokenType.
There is a sample application provided by Google that demonstrates how to use the AndroidAccountManager in conjunction with the Google+ APIs.
Link: http://code.google.com/p/google-plus-java-starter/source/browse/#hg%2Fandroid

Webservice credentials - OpenID/Android AccountManager?

I'm building a webservice and would like to use the user's google account credentials.
The service runs on GAE and will have a web client and an Android native client.
This is my first attempt of something like this and I've been reading about OpenID and the Android AccountManager library.
I'm still not sure what are my options in terms of storing the users in my Datastore. What Identifier should I use ? Is it possible to use OpenID on a native Android application ?
Any help and/or pointers would be appreciated. Thanks.
We had a similar requirements on the last project: GAE backend with GWT frontend and Android/iPhone clients. Also, we did not want to store user credentials.
So we choose to use OpenID, which is unfortunately a Web standard and does not play well with mobile devices, but is doable.
On the GAE side we simply enabled federated login which gave us OpenID.
On mobile devices, when user needs to login we present to them a list op OpenID authenticators (Google, Yahoo, etc..). Then we open a native browser (not embedded browser) and direct user to chosen OpenID authentication site. The upside is that user's browser usually already has username/pass remembered, so this step just requires user to press one button.
This is all pretty straightforward. Now here is the tricky part:
After user confirms login, OpenID redirects back to our GAE return url (you need to provide this url when request is made). On this url we create a custom URL, for example:
yourappname://usrname#XXXYYYZZZ
where XXXYYYZZZZ is auth token. We get this token from the return page where it's stored as an ACSID cookie: we used some JSP to read this cookie and wrap it into above custom URL.
Then we register our Android and iPhone apps to handle the yourappname:// URLs, so that when user cliskc this link, our app is invoked and the link is passed to it. We extract user name and token from this link and we use it in REST requests to the GAE backend.
If you have any more questions I'd gladly update this post.
Update:
The user session cookie on production AppEngine is named ACSID, while on development AppEngine server it's named dev_appserver_login.
I spent about a week to find a suitable and modern looking way for this - without web browser and by using android account manager.
If you would like to use Google account and AccountManager to identify the user you can:
Get his token to Google Contacts (auth token type is "cp") through AccountManager on background thread:
public String getUserToken(Activity activity)
{
AccountManager accountManager = AccountManager.get(activity);
AccountManagerFuture<Bundle> amf = accountManager.getAuthTokenByFeatures("com.google", "cp", null, activity, Bundle.EMPTY, Bundle.EMPTY, null, null );
Bundle bundle = null;
try {
bundle = amf.getResult();
String name = (String) bundle.get(AccountManager.KEY_ACCOUNT_NAME);
String type = (String) bundle.get(AccountManager.KEY_ACCOUNT_TYPE);
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
return token;
} catch (OperationCanceledException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (AuthenticatorException e) {
e.printStackTrace();
}
return null;
}
Pass received UserToken to the server over secured channel.
Validate the token at the server by google using gdata library (Google Data API library):
public String getUserId(String token)
{
ContactsService contactsService = new ContactsService("Taxi");
contactsService.setUserToken(token);
IFeed feed = null;
try {
feed = contactsService.getFeed(new URL("https://www.google.com/m8/feeds/contacts/default/full?max-results=10000"), ContactFeed.class);
} catch (IOException e) {
e.printStackTrace();
} catch (ServiceException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
if (feed == null)
return null;
String externalId = feed.getId();
IPerson person = feed.getAuthors().get(0);
String email = person.getEmail();
String name = person.getName();
String nameLang = person.getNameLang();
return externalId;
}
Google token can expire (usually after an hour), so if you failed to validate the token at the server, you must send response back to client, invalidate the token and get a new one. Use account manager to invalidate the token:
public void invalidateUserToken(Context context, String token)
{
AccountManager accountManager = AccountManager.get(context);
accountManager.invalidateAuthToken("com.google", token);
}
I think this blog post does exactly what you want. It worked for me. Both of the solutions posted here are viable and clever, but I think this does it exactly how the asker was asking.
Essentially, you're just getting an authToken using the "ah" scope, and passing it to the right webpage to get the ACSID cookie that will let you access any AppEngine page that uses UserService for authentication.
http://developer.android.com/search.html#q=AccountManager&t=0
http://developer.android.com/resources/samples/SampleSyncAdapter/index.html
at the bottom of this page you will find all needed code
best regards

Categories

Resources