AccountManager.getAuthToken never returns - android

I am trying to get the auth token for Google account, for the same I am using below code:
AccountManager acoountManager = AccountManager.get( getContext( ) );
AccountManagerFuture< Bundle > accountManagerFuture = acoountManager.getAuthToken( account, "android", null, (Activity) ctx, null, null );
Bundle authTokenBundle = accountManagerFuture.getResult( );
It should return the auth token if my app have permission to access Google Account token else show a permission request screen.
My problem is, in some of the devices it never returns. Also I observed that it was working once I do factory reset to the same device. I am not able to understand why its not returning before without factory reset, was it missing any system service?
Please help!!!

I found my answer here,
https://code.google.com/p/android/issues/detail?id=25473
This is a bug in android which is fixed in lollipop.

Related

PayPal Here Android SDK Error - BadConfiguration: Cannot proceed with this merchant account

I'm trying to integrate the PayPal Here swipers into a Xamarin Android app. Everything is fine until I try and give my credentials to the SDK. Specifically, the line containing the call to PayPalHereSDK.SetCredentials
public void InitializeSdk( Context context, string serverName, string accessToken, string refreshUrl, string expires, IPayPalHereSdkWrapperCallback listener ) {
PayPalHereSDK.Init( context, serverName );
PayPalHereSDK.RegisterAuthenticationListener( this );
PayPalHereSDK.CardReaderManager.RegisterCardReaderConnectionListener( this );
if ( !string.IsNullOrEmpty( accessToken ) ) {
var credentials = new OAuthCredentials( accessToken, refreshUrl, expires );
PayPalHereSDK.SetCredentials( credentials, new SetAccessTokenResponseHandler( listener ) );
}
}
My SetAccessTokenResponseHandler class implements the Com.PayPal.Merchant.Sdk.Domain.IDefaultResponseHandler interface. As described above, the OnError function is called when call the PayPalHereSDK.SetCredentials function. I'm given the error code "BadConfiguration" and the message "Cannot proceed with this merchant account. ready"
I've searched Google high and low and, I believe, scoured SO pretty thoroughly. I can't seem to overcome the error, so I'm asking for help!
I think the paypal email is not verified properly. Please go through the merchant onboarding guide document to get more details regarding making the merchant eligible.
https://github.com/paypal/paypal-here-sdk-android-distribution/blob/master/docs/Merchant%20Onboarding%20Guide.pdf
Hope this helps. Cheers.
I'm not sure what exactly the issue was, but I ended up deleting the Sandbox App in my PayPal dev portal and creating a new one. Everything works now. head scratch
I resorted to this because, while trying to follow Sundar's suggestion, I started getting an "invalid scope" error. I had received them before and KNEW I had it fixed ( and no code had changed ). When I deleted/recreated the app, that error went away. Frustrating, but that's what worked!

Consume WebAPI2 site from Android client with Google Authentication

I've been wracking my brain these past two days to try and understand how to use the authentication built into ASP.NET's WebAPI 2 using Google as an external authentication, and not being familiar with OAuth 2, I'm quite lost. I have followed this tutorial to set up the sign-in button on my Android client and send the "idToken" to the Web API. I've also followed this (now out of date) tutorial on setting up Google as an external login.
The problem happens when I try to send it I get {"error":"unsupported_grant_type"} as a response. Some other tutorials lead me to believe that the POST to mysite.com/token does not contain the correct data. This means I'm either building the request incorrectlyon the client, I'm somehow handling it incorrectly on the backend, I'm sending it to the wrong url, or I'm doing something entirely else wrong.
I found this SO answer which says to get a URL from /api/Accounts/ExternalLogins, but the sign-in button already gives me the access token that would supply to me (if I understand that correctly).
If someone could help me out here on what the exact process should be from start to finish, that would be amazing.
UPDATE: Okay, so here are some things that I've learned since I asked this question.
website.com/token URI is the redirect for the built in OAuth server in the WebAPI2 template. This is not useful for this particular problem.
The id_token is an encoded JWT token.
The website.com/signin-google URI is the redirect for normal Google login, but does not accept these tokens.
I may have to write my own AuthenticationFilter that uses the Google Client library to authorize through the Google API.
UPDATE 2: I'm still working on getting this AuthenticationFilter Implementation. Things seem to be going well at this point, but I'm getting stuck on some things. I've been using this example to get the token verification code, and this tutorial to get the AuthenticationFilter code. The result is a mix of both of them. I'll post it here as an answer once it's complete.
Here are my current problems:
Producing an IPrincipal as output. The verification example makes a ClaimPrincipal, but the AuthenticationFilter example code uses a UserManager to match the username to an existing user and returns that principal. The ClaimsPrincipal as created in the verification example directly does not auto-associate with the existing user, so I need to attempt to match some element of the claims to an existing user. So how do I do that?
I still have an incomplete idea of what a proper flow for this is. I'm currently using the Authentication header to pass my id_token string using a custom scheme: "goog_id_token". The client must send their id_token for every method called on the API with this custom AuthenticationFilter. I have no idea how this would usually be done in a professional environment. It seems like a common enough use case that there would be tons of information about it, but I haven't seen it. I have seen the normal OAuth2 flow, and since I'm only using an ID Token, and not an Access Token I'm a bit lost on what an ID Token is supposed to be used for, where it falls in a flow, and where it's supposed to live in an HTTP packet. And because I didn't know these things, I've kind of been making it up as I go along.
Wow, I did it. I figured it out. I... I can't believe it.
As metioned in my question Update 2, this code is assembled from Google's official API C# example and Microsoft's Custom AuthenticationFilter tutorial and code example. I'm going to paste the AuthorizeAsync() here and go over what each block of code does. If you think you see an issue, please feel free to mention it.
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
bool token_valid = false;
HttpRequestMessage request = context.Request;
// 1. Look for credentials in the request
//Trace.TraceInformation(request.ToString());
string idToken = request.Headers.Authorization.Parameter.ToString();
The client adds the Authorization header field with the scheme followed by a single space, followed by the id token. It looks something like Authorization: id-token-goog IaMS0m3.Tok3nteXt.... Putting the ID token in the body as given in the google documentation made no sense in this filter so I decided to put it in the header. For some reason it was difficult to pull custom headers from the HTTP packets so I just decided to use the Authorization header with a custom scheme followed by the ID token.
// 2. If there are no credentials, do nothing.
if (idToken == null)
{
Trace.TraceInformation("No credentials.");
return;
}
// 3. If there are credentials, but the filter does not recognize
// the authentication scheme, do nothing.
if (request.Headers.Authorization.Scheme != "id-token-goog")
// Replace this with a more succinct Scheme title.
{
Trace.TraceInformation("Bad scheme.");
return;
}
This whole point of a filter is to ignore requests that the filter doesn't govern (unfamiliar auth schemes, etc), and make judgement on requests that it's supposed to govern. Allow valid authentication to pass to the downstream AuthorizeFilter or directly to the Controller.
I made up the scheme "id-token-goog" because I had no idea if there was an existing scheme for this use case. If there is, somebody please let me know and I'll fix it. I guess it doesn't particularly matter at the moment as long as my clients all know the scheme.
// 4. If there are credentials that the filter understands, try to validate them.
if (idToken != null)
{
JwtSecurityToken token = new JwtSecurityToken(idToken);
JwtSecurityTokenHandler jsth = new JwtSecurityTokenHandler();
// Configure validation
Byte[][] certBytes = getCertBytes();
Dictionary<String, X509Certificate2> certificates =
new Dictionary<String, X509Certificate2>();
for (int i = 0; i < certBytes.Length; i++)
{
X509Certificate2 certificate =
new X509Certificate2(certBytes[i]);
certificates.Add(certificate.Thumbprint, certificate);
}
{
// Set up token validation
TokenValidationParameters tvp = new TokenValidationParameters()
{
ValidateActor = false, // check the profile ID
ValidateAudience =
(CLIENT_ID != ConfigurationManager
.AppSettings["GoogClientID"]), // check the client ID
ValidAudience = CLIENT_ID,
ValidateIssuer = true, // check token came from Google
ValidIssuer = "accounts.google.com",
ValidateIssuerSigningKey = true,
RequireSignedTokens = true,
CertificateValidator = X509CertificateValidator.None,
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
return identifier.Select(x =>
{
// TODO: Consider returning null here if you have case sensitive JWTs.
/*if (!certificates.ContainsKey(x.Id))
{
return new X509SecurityKey(certificates[x.Id]);
}*/
if (certificates.ContainsKey(x.Id.ToUpper()))
{
return new X509SecurityKey(certificates[x.Id.ToUpper()]);
}
return null;
}).First(x => x != null);
},
ValidateLifetime = true,
RequireExpirationTime = true,
ClockSkew = TimeSpan.FromHours(13)
};
This is all unchanged from the Google example. I have almost no idea what it does. This basically does some magic in creating a JWTSecurityToken, a parsed, decoded version of the token string, and sets up the validation parameters. I'm not sure why the bottom portion of this section is in it's own statement block, but it has something to do with the CLIENT_ID and that comparison. I'm not sure when or why the value of CLIENT_ID would ever change, but apparently it's necessary...
try
{
// Validate using the provider
SecurityToken validatedToken;
ClaimsPrincipal cp = jsth.ValidateToken(idToken, tvp, out validatedToken);
if (cp != null)
{
cancellationToken.ThrowIfCancellationRequested();
ApplicationUserManager um =
context
.Request
.GetOwinContext()
.GetUserManager<ApplicationUserManager>();
Get the user manager from the OWIN context. I had to dig around in context intellisense until I found GetOwinCOntext(), and then found that I had to add using Microsoft.Aspnet.Identity.Owin; in order to add the partial class that included the method GetUserManager<>().
ApplicationUser au =
await um
.FindAsync(
new UserLoginInfo(
"Google",
token.Subject)
);
This was the very last thing I had to fix. Again, I had to dig through um Intellisense to find all of the Find functions and their overrides. I had noticed from the Identity Framework-created tables in my database that there is one called UserLogin, whose rows contain a provider, a provider key, and a user FK. The FindAsync() takes a UserLoginInfo object, which contains only a provider string and a provider key. I had a hunch that these two things were now related. I had also recalled that there was a field in the token format that included a key-looking field that was a long number that started with a 1.
validatedToken seems to be basically empty, not null, but an empty SecurityToken. This is why I use token instead of validatedToken. I'm thinking there must be something wrong with this, but since the cp is not null, which is a valid check for a failed validation, it makes enough sense that the original token is valid.
// If there is no user with those credentials, return
if (au == null)
{
return;
}
ClaimsIdentity identity =
await um
.ClaimsIdentityFactory
.CreateAsync(um, au, "Google");
context.Principal = new ClaimsPrincipal(identity);
token_valid = true;
Here I have to create a new ClaimsPrincipal since the one created above in validation is empty (apparently that's correct). Took a guess on what the third parameter of CreateAsync() should be. It seems to work that way.
}
}
catch (Exception e)
{
// Multiple certificates are tested.
if (token_valid != true)
{
Trace.TraceInformation("Invalid ID Token.");
context.ErrorResult =
new AuthenticationFailureResult(
"Invalid ID Token.", request);
}
if (e.Message.IndexOf("The token is expired") > 0)
{
// TODO: Check current time in the exception for clock skew.
Trace.TraceInformation("The token is expired.");
context.ErrorResult =
new AuthenticationFailureResult(
"Token is expired.", request);
}
Trace.TraceError("Error occurred: " + e.ToString());
}
}
}
}
The rest is just exception catching.
Thanks for checking this out. Hopefully you can look at my sources and see which components came from which codebase.

Java based Google App Engine, Android and authentication oauth2

Authentication and app engine, there is a lot to be read about it, but a lot seems to be outdated!
Even the google pages https://developers.google.com/appengine/docs/java/endpoints/consume_android#making-authenticated-calls
Here, they talk about 'GoogleAccountCredential.usingAudience', but nowadays, you should use GoogleAuthUtil (as far as I know, please correct me if I'm wrong).
I am trying to set up an app engine as a backend to my Android app (and in future, my iOS app).
I am using Android Studio, used the 'new module' and chose app engine with cloud messaging there.
I created a simple endpoint, and have a function there, here is some code:
public class ReviewEndpoint {
// Make sure to add this endpoint to your web.xml file if this is a web application.
private static final Logger LOG = Logger.getLogger(ReviewEndpoint.class.getName());
/**
* This method gets the <code>Review</code> object associated with the specified <code>id</code>.
* #param id The id of the object to be returned.
* #return The <code>Review</code> associated with <code>id</code>.
*/
#ApiMethod(name = "getReview")
public Review getReview(#Named("id") Long id) {
// Implement this function
Review r = new Review();
r.setData("test!");
As you can see, this is nicely generated by Android Studio. I implemented some stuf like creating the 'review' object and return it at the end.
On the Android side, I can do this:
ReviewEndpoint.Builder b = new ReviewEndpoint.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null);
ReviewEndpoint ep = b.build();
Review review = ep.getReview(1L).execute();
data = review.getData();
and yes, I get 'test!' :)
Now, I want to have this authenticated. I want to know which user wrote what, so I thought I am going to use GMail account and Facebook later.
Here I'm stuck. I am able to get a token from the user on Android:
token = GoogleAuthUtil.getToken(MainScreenActivity.this, mAccount.name, "oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.profile");
then you are able to add this token as credential to the request:
Credential cr = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(token);
ReviewEndpoint.Builder b = new ReviewEndpoint.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), cr);
Then in the app engine I tried to get the user info, but how?
Will it be supplied as 'bearer'? How do I get this bearer token? Should I then do API request to get the data on the server?
this does not work:
OAuthService service = OAuthServiceFactory.getOAuthService();
try {
User user = service.getCurrentUser();
can anyone give me a heads up?
So finally, today, I found out how to do it! I had questions on Stackoverflow on this before and never had an answer, but these to sites gave me the answer:
https://developers.google.com/appengine/docs/java/endpoints/auth
https://developers.google.com/appengine/docs/java/endpoints/consume_android
The first shows what needs to be done on the app engine side. The second page will tell you how to get the credentials. I was quite close. I am not sure if the adjusting of the build.gradle file mentioned in the second link is necessary. What I added to the App Engine:
#Api(name = "reviewEndpoint", version = "v1", ...<<some more stuff here >>
scopes = {Constants.EMAIL_SCOPE},
clientIds = {Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID},
audiences = {Constants.ANDROID_AUDIENCE})
and then get the credentials:
// Initialize the scope using the client ID you got from the Console.
final String scope = "server:client_id:" + Constants.WEB_CLIENT_ID;
credential = GoogleAccountCredential.usingAudience(activity,scope);
You have to add the e-mail address of the user:
credential.setSelectedAccountName("some-mail-address#gmail.com");
you can get the e-mail address using the account picker (also example shown when you follow the link)
and next. you do a call to the endpoint, using the credential, I think Play Services will validate the user, because if I use an e-mail that is not logged in on the device, it will not work. The following code will throw an GoogleAuthIOException :
ReviewEndpoint.Builder b = new ReviewEndpoint.Builder(
AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), id_token);
ReviewEndpoint ep = b.build();
Review review;
review = ep.getReview(1L).execute();
for testing, I've put the e-mail address I get at the server side as a string in the review object, and there it gave me the e-mail address instead of the user object being null. Ow! I forgot to tell you, you need a user argument on the app engine side. Even though you do not see the 'user' argument in the 'getReview' call above, it will be added by App Engine.
So this is how my getReview looks now:
#ApiMethod(name = "getReview")
public Review getReview(#Named("id") Long id, User user) {
// Implement this function
Review r = new Review();
r.setData("user == " + (user == null ? "NULL " : user.toString()));
Hope this will help someone

Android + App engine: user.getUserID() is null in endpoint

I have an Android application with GAE server. I tried to authenticate the user as described on developers.google.com, I added the user parameter to the endpoint methods etc. I get a User which is not null, but this method getUserId() returns null. It is similar to this, rather old problem:
Function User.getUserId() in Cloud endpoint api returns null for a user object that is not null
But I still don't know how to work around it. How do you handle this error? Have you ever encountered it?
In android client here's what I did (its simplified) :
credentials = GoogleAccountCredential.usingAudience(getApplicationContext(), "server:client_id:" + WEB_CLIENT_ID);
credentials.setSelectedAccountName(accountName);
WarriorEntityEndpoint.Builder endpointBuilder = new WarriorEntityEndpoint.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(), credentials);
warriorEntityEndpoint = endpointBuilder.build();
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
try {
warriorEntityEndpoint.getWarrior().execute();
} catch (Exception e) {
}
return null;
}
}.execute();
And on GAE:
#Api(name = "warriorEntityEndpoint", namespace = #ApiNamespace(ownerDomain = "szpyt.com", ownerName = "szpyt.com", packagePath = "mmorpg.monsters"),
version = "version1",
scopes = {"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"},
clientIds = {Constants.ANDROID_CLIENT_ID, Constants.WEB_CLIENT_ID},
audiences = {Constants.ANDROID_AUDIENCE})
public class WarriorEntityEndpoint {
private static final Logger log = Logger.getLogger(WarriorEntityEndpoint.class.getName());
#ApiMethod(name = "getWarrior")
public WarriorEntity getWarrior(User user) throws OAuthRequestException, IOException {
log.log(Level.SEVERE, "this gives correct email: " + user.getEmail());
log.log(Level.SEVERE, "this is null: " + user.getUserId());
I have also another very important question: is this user authenticated, if getMail() gives me correct account, but getUserId() gives null? I read that user object should be null if it was not authenticated but I am not sure any more...
I'm using App engine SDK 1.8.7. I'm testing on a real device and backend deployed to GAE.
I asked the same question a while ago and got an answer. See link:
Function User.getUserId() in Cloud endpoint api returns null for a user object that is not null
The cause is a bug on appengine.
I guess there is no good solution for it right now. I store e-mail as a normal property and remove it from default fetch group, I use long as a primary key (generated by AppEngine) and I query the entity by the e-mail property. I don't like my solution, I'll accept ( and implement :) ) a better one if anyone can provide.
This is a known issue which has been filed with google, I've attached the issue link below.
There are two workarounds (1) save the user and read back from the store, if it refers to a valid account the user id will be populated (this sucks because you pay the saving / loading / deletion cost for each API access that is authenticated even if it is tiny, and obviously some performance cost) and (2) you could use the google+ ID but that is NOT the same as the user id.
This is extremely frustrating and there is currently no ETA as they are working on some fundamental issues with the auth design as far as I understand.
Please, vote for that issue by starring it. You can find all the information here
https://code.google.com/p/googleappengine/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Component%20Status%20Stars%20Summary%20Language%20Priority%20Owner%20Log&groupby=&sort=&id=8848
And here is the current formally approved workaround [(1) above], which you can also find in the link above, but for ease it's here: How can I determine a user_id based on an email address in App Engine?
For workaround (2) mentioned above, you can look at the first link, and go to post #39.

Login in twice when using SyncAdapters

I am creating a new Android app using SyncAdapter to handle db sync.
I have everything in place and the app is working fine but I noticed that I am logged in twice.
The first login takes place when the AuthenticatorActivity class (it extends AccountAuthenticatorActivity) validates the user and password.
If the user and password are correct the AuthenticatorActivity then does:
If the account didn't exist it creates it using mAccountManager.addAccountExplicitly()
The authToken is saved using intent.putExtra(AccountManager.KEY_AUTHTOKEN, authToken);
This was basically copied/pasted from the Android samples, so I guess it's ok.
The issue is that when the SyncAdapter launches and uses
authtoken = mAccountManager.blockingGetAuthToken(account,
AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE, true);
The getAuthToken() method inside the Authenticator class which extends AbstractAccountAuthenticator is called. And inside this method I am hitting the login endpoint once again.
From that point onwards the login endpoint is not hit again until the authToken expires.
This is not something that bothers me a lot but I would like to know if there is a way to avoid doing the login twice.
As you've seen, though Authenticator.java in the SampleSyncAdapter says
The interesting thing that this class demonstrates is the use of authTokens as part of the authentication process. ... If we already have an authToken stored in the account, we return that authToken. If we don't, but we do have a username and password, then we'll attempt to talk to the sample service to fetch an authToken.
that's a lie. Authenticator.getAuthToken doesn't to any cache checking, it just hits the network to get a token.
The solution is to add in the missing check:
Authenticator.java:
#Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
String authTokenType, Bundle loginOptions) throws NetworkErrorException {
// check that authToken type supported
...
// Check if we already have a cached token to return
final AccountManager am = AccountManager.get(mContext);
String cachedAuthToken = am.peekAuthToken(account, authTokenType);
if (cachedAuthToken != null) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNT_TYPE);
result.putString(AccountManager.KEY_AUTHTOKEN, cachedAuthToken);
return result;
}
// Get new authToken from server
...
// If all else fails, prompt the user for credentials
...
}
Note that the rest of your project needs to religiously use AccountManager.invalidateAuthToken when calls fail or else you'll wind up with an infinite loop of calls failing, trying to get a new auth token, and then failing again when the same cached auth token is returned.

Categories

Resources