After spending roughly two days on this, I'm getting a little rattled. Although by now, having chosen OAuth2 over OpenID, I'm pretty well versed on the difference between Authenticate and Authorize.
I want my android app to provide several ways to authenticate users, one of them is google accounts, and later also facebook and twitter accounts. I'm trying to use the AccountManager class to get an OAuth access token to (for now) just verify the user's email address. The goal is that if the user already has a google account saved on the android device, they can authorize my app once, MAYBE even without typing a password, and never have to login again from their android.
I decided to use Google's own AccountManager as it promised to handle much of this natively in the Android framework, without even opening a browser window. I am using the library / build target for google APIs version 7 (Android 2.1), the first level that supports AccountManager.
I have tried this two different ways, one using AccountManager.getAuthTokenByFeatures() where you do not specify an Account object, and the other using getAuthToken() where you do specify such an object.
In each case, the call completes (as I expect it to) and the application displays an authorization dialog asking if I want to authorize the app. So far, so good. If I refuse, the program throws the exception that I expect. If I accept, a "Google sign-in" dialog appears asking me for the password to the account. Note that I already entered the password when I added the account to the device. If I type the password in the dialog, there is an "Authorizing" wait screen and then the same dialog re-appears. Oddly enough, the "Authorizing" wait screen seems to take a little bit longer if I type the CORRECT password. So it appears that I cannot get to the code path where I successfully obtain the token.
As tempting as it is to vent about google not being clear about what AUTH_TOKEN_TYPE is or the fact that the userinfo.email URL is undocumented, I would really just like to learn what I am doing wrong here and move past this.
Here is my code, I will monitor this and of course be happy to answer any questions. Right now I am going to work on getting a capture of the network traffic, to see if that provides any further insight into what is going wrong.
Here are images showing the auth screen (ok) and the password dialog (less ok)
http://imageshack.us/g/707/device20120609020618.png/
public void loginToGoogle() {
System.out.println("Starting");
AccountManagerFuture<Bundle> bundleFuture =
AccountManager.get(_activity).getAuthTokenByFeatures(
"com.google",
"https://www.googleapis.com/auth/userinfo.email",
null,
_activity,
null,
null,
new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> future) {
Bundle bundle;
try {
bundle = future.getResult();
for (String s : bundle.keySet()) {
System.out.println("Found key: "+ s);
}
System.out.println(bundle.getString(AccountManager.KEY_ACCOUNT_NAME));
System.out.println(bundle.getString(AccountManager.KEY_AUTHTOKEN));
//Use Token
} catch (OperationCanceledException e) {
Log.e("e", e.getMessage(), e);
System.out.println("User appears to have denied auth request");
} catch (AuthenticatorException e) {
Log.e("e", e.getMessage(), e);
} catch (IOException e) {
Log.e("e", e.getMessage(), e);
}
}
},
null);
System.out.println("Done with AccountManager call");
}
Your issue most likely lies in the Auth Token Type you pass into the getAuthTokenByFeatures. Since you're using oauth2, you need to add it to beginning of your auth type:
String AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/userinfo.email"
However, I'm not positive that the Auth Token Type you are trying to use is valid. I've only dealt with calendars and tasks myself. Hope this works out, if not, just let me know and I'll find you the right Auth type
Related
I'm not using FirebaseUI. With FirebaseUI I managed to store the email and password into Smart Lock after creating a new account for an user of the app.
I had to remove FirebaseUI, because it doesn't allow a precise control over which account gets signed in with silent sign-in, and I rely on that to switch between the multiple accounts one user can have on a device.
Using the CredentialsClient I had no problems retrieving those passwords; the user got presented a Dialog where he could choose the account and the password would get handed over to the app via onActivityResult. I followed Google's guide Retrieve a user's stored credentials in order to do this.
But I am unable to save the new email and password as a new credential. I always get the error com.google.android.gms.common.api.CommonStatusCodes.SIGN_IN_REQUIRED with the message Passphrase required in form of an com.google.android.gms.common.api.ApiException.
This is my code:
CredentialsOptions options = new CredentialsOptions.Builder()
.forceEnableSaveDialog()
.build();
CredentialsClient mCredentialsClient = Credentials.getClient(MainActivity.activity, options);
Credential credential = new Credential.Builder("email#example.com")
.setPassword("dummy-password")
.build();
mCredentialsClient.save(credential).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
LogWrapper.i(TAG, "Credentials saved");
}
else if (task.getException() instanceof ResolvableApiException) {
ResolvableApiException rae = (ResolvableApiException) task.getException();
LogWrapper.e(TAG, "RAE EXCEPTION WHEN SAVING PASSWORD:", rae);
}
else {
Exception e = task.getException();
LogWrapper.e(TAG, "EXCEPTION WHEN SAVING PASSWORD:", e);
}
}
});
I have three Google Accounts on that device, one of which has configured a passphrase for syncing, the other two don't.
Keep your info private
With a passphrase, you can use Google's cloud to store and sync your
Chrome data without letting Google read it. Your payment methods and
addresses from Google Pay aren't encrypted by a passphrase.
Passphrases are optional. Your synced data is always protected by
encryption when it's in transit.
I am not sure if this error message is being caused because of this, that this is the passphrase which is required. But then again I wonder why FirebaseUI had no issues with storing the Credentials, even into the correct account without asking, which is one which doesn't use a passphrase.
I also signed into the account via FirebaseUI's Google Sign-In where I want to save the credentials to, and then executed the code above in order to see if that signed-in user would set the app into a state where it is signed into that Google Account as the error messages seems to expect me to, and willing to save some email and password credentials into that account. That didn't make any difference.
I read Firebase's approach to using Smart Lock, and I can't find any real differences to what I'm doing.
It is basically the same as shown in the CredentialsQuickstart app.
So the question is: What needs to be signed in? Why am I getting this message?
Update: I've downloaded and compiled https://github.com/android/identity-samples/tree/master/CredentialsQuickstart and there I'm getting the same error, so this seems to be some more general issue.
This is on Android 10. I've also tried it on another device with Android 9 and there I have the same problem.
Has this API been "deprecated" by this thing called "AutoFill service"? I found an option in "Languages & Input" under "Tools" where there is an AutoFill Service from Google where I can select one account into which the credentials get stored into. And this is configured to use the account which has no passphrase, the one where FirebaseUI correctly stored the Credentials into. I think on a Samsung device it also offers an additional AutoFill service.
That would be this then: https://developer.android.com/guide/topics/text/autofill
I am currently at a loss on how to proceed with my app.
I currently authenticate following the tutorial here and everything works swimmingly.Azure Mobile Apps Authentication
Where I am at a loss is how to use the id and/or token that is stored following this process to obtain basic profile information say a users email or their profile photo.
From what I have read online this is merely a azureId that stored not the google profile ID which I would use with the google+apis.
Has anyone got a reference that shows a novice programmer how to get the email address or userId required to use the google api.
The only reference I can find is a blog post from 2014. Surely there must be an easier way. And one specifically written to work on Mobile apps as opposed to mobile services. Blog post describing how to expand on authentication with google on mobile services which is no use
Here is my process
// We first try to load a token cache if one exists.
Log.v(TAG, "Click"+USERIDPREF );
if (loadUserTokenCache(mClient))
{
Log.v(TAG, "table" +mClient.getCurrentUser().toString());
createTable();
returnHome();
}
// If we failed to load a token cache, login and create a token cache
else
{
// Login using the Google provider.
final ListenableFuture<MobileServiceUser> mLogin = mClient.login(MobileServiceAuthenticationProvider.Google);
Futures.addCallback(mLogin, new FutureCallback<MobileServiceUser>() {
#Override
public void onFailure(Throwable exc) {
Log.v(TAG, "Login On fail " +exc.getMessage() );
}
#Override
public void onSuccess(MobileServiceUser user) {
Log.v(TAG, "On Success" );
createTable();
cacheUserToken(mClient.getCurrentUser());
Log.v(TAG, "On Success" + mClient.getCurrentUser() );
returnHome();
}
});
}
The first documentation link you posted has the answer. From https://azure.microsoft.com/en-us/documentation/articles/app-service-authentication-overview/#working-with-user-identities-in-your-application:
Code that is written in any language or framework can get the information that it needs from these headers. For ASP.NET 4.6 apps, the ClaimsPrincipal is automatically set with the appropriate values.
Your application can also obtain additional user details through an HTTP GET on the /.auth/me endpoint of your application. A valid token that's included with the request will return a JSON payload with details about the provider that's being used, the underlying provider token, and some other user information. The Mobile Apps server SDKs provide helper methods to work with this data. For more information, see How to use the Azure Mobile Apps Node.js SDK, and Work with the .NET backend server SDK for Azure Mobile Apps.
So to summarize, you have different options depending on which language you use, but the most cross-platform option is to send an authenticated request to your mobile app's /.auth/me endpoint. You'll get back a JSON object which contains a bunch of user claims (name, provider-specific ID, email, etc.).
I have my own type account in my application and I putted Log out button in my app. I would like to find out what is better way for Logging out
I wonder is it better to removeAccont like this:
mAccountManager.removeAccount(account, new AccountManagerCallback<Boolean>() {
#Override
public void run(AccountManagerFuture<Boolean> future) {
try {
if (future.getResult()) {
}
} catch (Exception e) {
e.printStackTrace();
}
}
}, null);
or change password?
or remove Auth Tokens?
This depends on many things. Primarily: What do you want to accomplish?
First of all, if you actually log the user out, the auth tokens would be invalidated and hence you can just remove them. This is basically what logging out means.
Whether you remove the password, or still keep it saved, is entirely up to you. Although, you probably should not save the password itself on an android device. You should rather save a refresh token with which you can get a new access token as the accounts password. This stored data is always a security risk, and exposing user passwords is not a good idea.
If you remove the account, the users device will be kept "clean", on the other hand: How many accounts do you suppose a normal user is going to have?
If you keep the account, but just remove the password and tokens, you can still query the account manager to support AutoCompleteTextView to facilitate the next user login.
What I do is:
Invalidate the tokens
Remove access token
setPassword(account, null) (Password is the refresh token, which got invalidated anyways)
Keep the account.
And as mentioned earlier, I use an AutoCompleteTextView to suggest the old account at the next login.
I want to post a simple status message to a Twitter account that's linked to my app. All users of my app will post to the same Twitter account.
I've registered my app with Twitter (according to the guidance given here: How to post a tweet from an Android app to one specific account?) and I have the necessary ConsumerKey, ConsumerSecret, AccessToken and AccessTokenSecret. I've set the account to Read & Write, and set the REQUEST type to GET.
I'm using Twitter4J and installed the twitter4j-core-3.0.3.jar into my app. The Manifest file has the required "android.permission.INTERNET". This is the code …::
AccessToken a = new AccessToken(AccessToken, AccessTokenSecret);
Twitter twitter = new TwitterFactory().getInstance();
twitter.setOAuthConsumer(ConsumerKey, ConsumerSecret);
twitter.setOAuthAccessToken(a);
try
{
twitter.updateStatus("Tweet Test #1");
Log.v(TAG, "Twitter Tweet sent!");
}
catch (TwitterException e)
{
// TODO Auto-generated catch block
Log.e(TAG, "Error sending Tweet:" + e.getMessage());
}
The twitter.updateStatus("xxxxxx") call causes an exception that reports “Received authentication challenge is null” in the logcat.
I assumed I could just post, but it seems Twitter wants something more?
Can anybody offer any advice as to what I'm doing wrong?
Managed to get to the bottom of this in the end!
My particular problem was that I didn't enter a Callback URL in the Twitter Apps setup page. I'm only interested in sending tweets to my app's linked Twitter account and I already have the 4 tokens/secrets, so I don't need to get the user to authorise thier own account via my app. As such, I don't need a Callback URL.
Unfortunately, the Twitter apps page lets you leave that field blank when you request the tokens/secret, and they don't make it clear that the Callback URL is a required field. If you don't need it, you can put absolutely anything you like in there; But if you leave it blank, Twitter won't let you tweet from your app! Setting the access type to "Read & Write" is good enough just to update status, but set it to "Read, Write & Direct Messages" if you want to do more.
Some tutorials say you should set the app type to "Browser" (instead of "Desktop"), but that option seems to have disappeared from the Twitter apps page, so I guess that's no longer important.
I managed to find some very good tutorials about tweeting from an Android app (here, here and, in particular, here) which make it clear that the Callback URL is a requirement, and go on to explain very clearly how to get it to work.
Have you checked to make sure there aren't any extra whitespaces in your Twitter4J configuration file? Double-check each of your consumer.. and access.. fields just in case.
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