Webservice credentials - OpenID/Android AccountManager?
Asked Answered
C

4

31

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.

Cleptomania answered 28/7, 2010 at 12:2 Comment(0)
T
31

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.

Tillo answered 17/2, 2011 at 14:16 Comment(2)
+1 - very detailed! What does the custom URL text show? "Click this to return to the application"? Or something like that?Ashford
It can show whatever you like. I used "Continue.." link that looks like a button. With some javascript you could possibly launch the app automatically, without requiring user to click on the link - didn't try this, it would need to be tested.Tillo
L
12

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:

  1. 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;
    }
    
  2. Pass received UserToken to the server over secured channel.

  3. 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;
    }
    
  4. 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);
    }
    
Lochia answered 13/7, 2011 at 14:50 Comment(5)
is it possible to do the same (get the token to be used) for email messages on gmail and for facebook ? if so, can you please answer it on the next post : #11532757Prase
But that doesn't solve the problem since he wants to authenticate the user on his own webservice with a database I guess, so if he wants to authenticate on his webservice he should send the token and his google ID on the webservice and the webservice should send the token on google server who send back a response with the google ID that you can compare with the google ID the user has sended, isn't it right ? how do you do that ?Hilltop
I tested this solution and it works great if you just need an authenticated email from your user. The upside is you can do everything in your app without a web browser. The downside is, you have to ask for permission to edit user's contacts, which some might be hesitant to do. Does anyone know of a way to just ask for authentication without asking for authorization?Zanze
@thomas88wp you can follow this guide which uses the built in Google Account authorization of GAE: blog.notdot.net/2010/05/… . Note that this is only for Google Accounts, not OpenIDFlied
Actually here's a better guide: android-developers.blogspot.no/2013/01/…Flied
Z
3

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.

Zanze answered 18/12, 2012 at 17:16 Comment(0)
S
0

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

Starr answered 2/9, 2010 at 9:44 Comment(2)
The linked example does not use OpenID or Xauth, so it can not be used on GAE to authenticate against existing Google accounts. As I can see it uses it's own authentication scheme: simple POST with username/password as header parameters.Tillo
though @peterKnego has a point regarding the second recommendation, the first link is actually quite useful. but i do agree that more attention should go into formulating a proper answer, fmo. but be careful not to be downvoted in the future.Rothrock

© 2022 - 2024 — McMap. All rights reserved.