I'm trying to retrieve the AuthToken for Facebook (saved by Facebook for Android) by using the following piece of code.
AccountManager am = AccountManager.get(this);
Account[] accounts = am.getAccountsByType("com.facebook.auth.login");
if (accounts.length > 0) {
for(int j = 0; j < accounts.length; j++) {
Account account = accounts[j];
if(account.type != null && account.type.equals("com.facebook.auth.login")) {
Log.e(RuntimeVars.MY_NAME, "FACEBOOK-TYPE FOUND");
am.getAuthToken(account, "com.facebook.auth.login", null, ConversationList.this,
new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> arg0) {
try {
Bundle b = arg0.getResult();
Log.e(RuntimeVars.MY_NAME, "THIS AUTHTOKEN: " + b.getString(AccountManager.KEY_AUTHTOKEN));
}
catch (Exception e) {
Log.e(RuntimeVars.MY_NAME, "EXCEPTION#AUTHTOKEN");
}
}
}, null);
}
}
}
The login credentials are found and FACEBOOK-TYPE FOUND is written into LogCat, but neither THIS AUTHTOKEN: [...] nor EXCEPTION#AUTHTOKEN is logged. So I suppose am.getAuthToken is never called.
What am I missing?
In general, if there is a better (and at least working) approach to retrieve the Facebook authtoken from the Android accounts please let me know.
Thanks a lot for your help!
Best regards
S.
Why not use the Facebook SDK?
The Facebook class in it has a member to get the OAuth 2.0 access token (if that is what you need), getAccessToken().
To explain the fact that neither of your logging statements are being reached, consider:
Line ~8:
am.getAuthToken(account, "com.facebook.auth.login", null, ConversationList.this,
... can return a token if it's immediately available. Maybe that's the answer you're looking for? Quoting the AccountManager documentation:
If a previously generated auth token
is cached for this account and type,
then it is returned. Otherwise, if a
saved password is available, it is
sent to the server to generate a new
auth token. Otherwise, the user is
prompted to enter a password.
Try calling AccountManager.blockingGetAuthToken instead. If that works, then there's something more interesting at fault here...
Also, make sure your manifest has the USE_CREDENTIALS permission set correctly.
add try { before am.getAuthToken and catch Exception where this method declaration ends.This will give you why and where excepption is happening
Related
I am trying to get my friend name and ids with Graph API v2.0, but data returns empty:
{
"data": [
]
}
When I was using v1.0, everything was OK with the following request:
FBRequest* friendsRequest = [FBRequest requestForMyFriends];
[friendsRequest startWithCompletionHandler: ^(FBRequestConnection *connection,
NSDictionary* result,
NSError *error) {
NSArray* friends = [result objectForKey:#"data"];
NSLog(#"Found: %i friends", friends.count);
for (NSDictionary<FBGraphUser>* friend in friends) {
NSLog(#"I have a friend named %# with id %#", friend.name, friend.id);
}
}];
But now I cannot get friends!
In v2.0 of the Graph API, calling /me/friends returns the person's friends who also use the app.
In addition, in v2.0, you must request the user_friends permission from each user. user_friends is no longer included by default in every login. Each user must grant the user_friends permission in order to appear in the response to /me/friends. See the Facebook upgrade guide for more detailed information, or review the summary below.
If you want to access a list of non-app-using friends, there are two options:
If you want to let your people tag their friends in stories that they publish to Facebook using your App, you can use the /me/taggable_friends API. Use of this endpoint requires review by Facebook and should only be used for the case where you're rendering a list of friends in order to let the user tag them in a post.
If your App is a Game AND your Game supports Facebook Canvas, you can use the /me/invitable_friends endpoint in order to render a custom invite dialog, then pass the tokens returned by this API to the standard Requests Dialog.
In other cases, apps are no longer able to retrieve the full list of a user's friends (only those friends who have specifically authorized your app using the user_friends permission). This has been confirmed by Facebook as 'by design'.
For apps wanting allow people to invite friends to use an app, you can still use the Send Dialog on Web or the new Message Dialog on iOS and Android.
UPDATE: Facebook have published an FAQ on these changes here: https://developers.facebook.com/docs/apps/faq which explain all the options available to developers in order to invite friends etc.
Although Simon Cross's answer is accepted and correct, I thought I would beef it up a bit with an example (Android) of what needs to be done. I'll keep it as general as I can and focus on just the question. Personally I wound up storing things in a database so the loading was smooth, but that requires a CursorAdapter and ContentProvider which is a bit out of scope here.
I came here myself and then thought, now what?!
The Issue
Just like user3594351, I was noticing the friend data was blank. I found this out by using the FriendPickerFragment. What worked three months ago, no longer works. Even Facebook's examples broke. So my issue was 'How Do I create FriendPickerFragment by hand?
What Did Not Work
Option #1 from Simon Cross was not strong enough to invite friends to the app. Simon Cross also recommended the Requests Dialog, but that would only allow five requests at a time. The requests dialog also showed the same friends during any given Facebook logged in session. Not useful.
What Worked (Summary)
Option #2 with some hard work. You must make sure you fulfill Facebook's new rules: 1.) You're a game 2.) You have a Canvas app (Web Presence) 3.) Your app is registered with Facebook. It is all done on the Facebook developer website under Settings.
To emulate the friend picker by hand inside my app I did the following:
Create a tab activity that shows two fragments. Each fragment shows a list. One fragment for available friend (/me/friends) and another for invitable friends (/me/invitable_friends). Use the same fragment code to render both tabs.
Create an AsyncTask that will get the friend data from Facebook. Once that data is loaded, toss it to the adapter which will render the values to the screen.
Details
The AsynchTask
private class DownloadFacebookFriendsTask extends AsyncTask<FacebookFriend.Type, Boolean, Boolean> {
private final String TAG = DownloadFacebookFriendsTask.class.getSimpleName();
GraphObject graphObject;
ArrayList<FacebookFriend> myList = new ArrayList<FacebookFriend>();
#Override
protected Boolean doInBackground(FacebookFriend.Type... pickType) {
//
// Determine Type
//
String facebookRequest;
if (pickType[0] == FacebookFriend.Type.AVAILABLE) {
facebookRequest = "/me/friends";
} else {
facebookRequest = "/me/invitable_friends";
}
//
// Launch Facebook request and WAIT.
//
new Request(
Session.getActiveSession(),
facebookRequest,
null,
HttpMethod.GET,
new Request.Callback() {
public void onCompleted(Response response) {
FacebookRequestError error = response.getError();
if (error != null && response != null) {
Log.e(TAG, error.toString());
} else {
graphObject = response.getGraphObject();
}
}
}
).executeAndWait();
//
// Process Facebook response
//
//
if (graphObject == null) {
return false;
}
int numberOfRecords = 0;
JSONArray dataArray = (JSONArray) graphObject.getProperty("data");
if (dataArray.length() > 0) {
// Ensure the user has at least one friend ...
for (int i = 0; i < dataArray.length(); i++) {
JSONObject jsonObject = dataArray.optJSONObject(i);
FacebookFriend facebookFriend = new FacebookFriend(jsonObject, pickType[0]);
if (facebookFriend.isValid()) {
numberOfRecords++;
myList.add(facebookFriend);
}
}
}
// Make sure there are records to process
if (numberOfRecords > 0){
return true;
} else {
return false;
}
}
#Override
protected void onProgressUpdate(Boolean... booleans) {
// No need to update this, wait until the whole thread finishes.
}
#Override
protected void onPostExecute(Boolean result) {
if (result) {
/*
User the array "myList" to create the adapter which will control showing items in the list.
*/
} else {
Log.i(TAG, "Facebook Thread unable to Get/Parse friend data. Type = " + pickType);
}
}
}
The FacebookFriend class I created
public class FacebookFriend {
String facebookId;
String name;
String pictureUrl;
boolean invitable;
boolean available;
boolean isValid;
public enum Type {AVAILABLE, INVITABLE};
public FacebookFriend(JSONObject jsonObject, Type type) {
//
//Parse the Facebook Data from the JSON object.
//
try {
if (type == Type.INVITABLE) {
//parse /me/invitable_friend
this.facebookId = jsonObject.getString("id");
this.name = jsonObject.getString("name");
// Handle the picture data.
JSONObject pictureJsonObject = jsonObject.getJSONObject("picture").getJSONObject("data");
boolean isSilhouette = pictureJsonObject.getBoolean("is_silhouette");
if (!isSilhouette) {
this.pictureUrl = pictureJsonObject.getString("url");
} else {
this.pictureUrl = "";
}
this.invitable = true;
} else {
// Parse /me/friends
this.facebookId = jsonObject.getString("id");
this.name = jsonObject.getString("name");
this.available = true;
this.pictureUrl = "";
}
isValid = true;
} catch (JSONException e) {
Log.w("#", "Warnings - unable to process Facebook JSON: " + e.getLocalizedMessage());
}
}
}
Facebook has revised their policies now. You can’t get the whole friendlist anyway if your app does not have a Canvas implementation and if your app is not a game. Of course there’s also taggable_friends, but that one is for tagging only.
You will be able to pull the list of friends who have authorised the app only.
The apps that are using Graph API 1.0 will be working till April 30th, 2015 and after that it will be deprecated.
See the following to get more details on this:
User Friends
Facebook Application Development FAQ
In Swift 4.2 and Xcode 10.1:
If you want to get the friends list from Facebook, you need to submit your app for review in Facebook. See some of the Login Permissions:
Login Permissions
Here are the two steps:
1) First your app status is must be in Live
2) Get required permissions form Facebook.
1) Enable our app status live:
Go to the apps page and select your app
https://developers.facebook.com/apps/
Select status in the top right in Dashboard.
Submit privacy policy URL
Select category
Now our app is in Live status.
One step is completed.
2) Submit our app for review:
First send required requests.
Example: user_friends, user_videos, user_posts, etc.
Second, go to the Current Request page
Example: user_events
Submit all details
Like this submit for all requests (user_friends , user_events, user_videos, user_posts, etc.).
Finally submit your app for review.
If your review is accepted from Facebook's side, you are now eligible to read contacts, etc.
As Simon mentioned, this is not possible in the new Facebook API. Pure technically speaking you can do it via browser automation.
this is against Facebook policy, so depending on the country where you live, this may not be legal
you'll have to use your credentials / ask user for credentials and possibly store them (storing passwords even symmetrically encrypted is not a good idea)
when Facebook changes their API, you'll have to update the browser automation code as well (if you can't force updates of your application, you should put browser automation piece out as a webservice)
this is bypassing the OAuth concept
on the other hand, my feeling is that I'm owning my data including the list of my friends and Facebook shouldn't restrict me from accessing those via the API
Sample implementation using WatiN:
class FacebookUser
{
public string Name { get; set; }
public long Id { get; set; }
}
public IList<FacebookUser> GetFacebookFriends(string email, string password, int? maxTimeoutInMilliseconds)
{
var users = new List<FacebookUser>();
Settings.Instance.MakeNewIeInstanceVisible = false;
using (var browser = new IE("https://www.facebook.com"))
{
try
{
browser.TextField(Find.ByName("email")).Value = email;
browser.TextField(Find.ByName("pass")).Value = password;
browser.Form(Find.ById("login_form")).Submit();
browser.WaitForComplete();
}
catch (ElementNotFoundException)
{
// We're already logged in
}
browser.GoTo("https://www.facebook.com/friends");
var watch = new Stopwatch();
watch.Start();
Link previousLastLink = null;
while (maxTimeoutInMilliseconds.HasValue && watch.Elapsed.TotalMilliseconds < maxTimeoutInMilliseconds.Value)
{
var lastLink = browser.Links.Where(l => l.GetAttributeValue("data-hovercard") != null
&& l.GetAttributeValue("data-hovercard").Contains("user.php")
&& l.Text != null
).LastOrDefault();
if (lastLink == null || previousLastLink == lastLink)
{
break;
}
var ieElement = lastLink.NativeElement as IEElement;
if (ieElement != null)
{
var htmlElement = ieElement.AsHtmlElement;
htmlElement.scrollIntoView();
browser.WaitForComplete();
}
previousLastLink = lastLink;
}
var links = browser.Links.Where(l => l.GetAttributeValue("data-hovercard") != null
&& l.GetAttributeValue("data-hovercard").Contains("user.php")
&& l.Text != null
).ToList();
var idRegex = new Regex("id=(?<id>([0-9]+))");
foreach (var link in links)
{
string hovercard = link.GetAttributeValue("data-hovercard");
var match = idRegex.Match(hovercard);
long id = 0;
if (match.Success)
{
id = long.Parse(match.Groups["id"].Value);
}
users.Add(new FacebookUser
{
Name = link.Text,
Id = id
});
}
}
return users;
}
Prototype with implementation of this approach (using C#/WatiN) see https://github.com/svejdo1/ShadowApi. It is also allowing dynamic update of Facebook connector that is retrieving a list of your contacts.
Try /me/taggable_friends?limit=5000 using your JavaScript code
Or
try the Graph API:
https://graph.facebook.com/v2.3/user_id_here/taggable_friends?access_token=
If you are still struggling with this issue on a development mode.
Follow the same process as mentioned below:
create a test app of your main app,
create test users, automatically install app for test users and assign them 'user_friend' permission.
Add your test users as a friend with each other.
I followed the same process after going through alot of research and finally it worked.
In the Facebook SDK Graph API v2.0 or above, you must request the user_friends permission from each user in the time of Facebook login since user_friends is no longer included by default in every login; we have to add that.
Each user must grant the user_friends permission in order to appear in the response to /me/friends.
let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()
fbLoginManager.loginBehavior = FBSDKLoginBehavior.web
fbLoginManager.logIn(withReadPermissions: ["email","user_friends","public_profile"], from: self) { (result, error) in
if (error == nil) {
let fbloginresult : FBSDKLoginManagerLoginResult = result!
if fbloginresult.grantedPermissions != nil {
if (fbloginresult.grantedPermissions.contains("email")) {
// Do the stuff
}
else {
}
}
else {
}
}
}
So at the time of Facebook login, it prompts with a screen which contain all the permissions:
If the user presses the Continue button, the permissions will be set. When you access the friends list using Graph API, your friends who logged into the application as above will be listed
if ((FBSDKAccessToken.current()) != nil) {
FBSDKGraphRequest(graphPath: "/me/friends", parameters: ["fields" : "id,name"]).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil) {
print(result!)
}
})
}
The output will contain the users who granted the user_friends permission at the time of login to your application through Facebook.
{
data = (
{
id = xxxxxxxxxx;
name = "xxxxxxxx";
}
);
paging = {
cursors = {
after = xxxxxx;
before = xxxxxxx;
};
};
summary = {
"total_count" = 8;
};
}
I had a working app with Facebook & Email Login feature, since I upgrade the Firebase console (only, the sdk has not been update).
The app release before the Firebase 3.0 was working before, but it is not anymore able to sign/log with Facebook after the console has been upgraded.
What I have done:
1 - Upgraded the Firebase console
Because of Firebase & Facebook console update, I also had to put the Oauth Callback to the Facebook App
2 - Pasted the Firebase Facebook OAuth Callback to the Facebook console (before it was void) `https://xxx.firebaseapp.com/__/auth/handler``
The Exception:
The firebase Auth listener trigger a Firebase Error :
Invalid authentication credentials provided. and Facebook :
{"providerErrorInfo":{"code":400,"message":"Unsuccessful debug_token
response from Facebook: {\"error\":{\"message\":\"(#100) You must
provide an app access token or a user access token that is an owner or
developer of the
app\",\"type\":\"OAuthException\",\"code\":100,\"fbtrace_id\":\"DG4lLRJHFBS\"}}"}}
The FirebaseError Code:
In the decompiled code of the FirebaseAndroidSdk, the error object is:
0 = {java.util.LinkedHashMap$LinkedEntry#22680} "code" ->
"INVALID_CREDENTIALS"
1 = {java.util.LinkedHashMap$LinkedEntry#22681}
"message" -> "Invalid authentication credentials provided."
2 = {java.util.LinkedHashMap$LinkedEntry#22682} "details" ->
"{"providerErrorInfo":{"code":400,"message":"Unsuccessful debug_token
response from Facebook: {\"error\":{\"message\":\"(#100) You must
provide an app access token or a user access token that is an owner or
developer of the app\",\"type\":\"OAuthException\",\"code\":100,\"fbtrace_id\":\"BtB3JF2qmku\"}}"}}"
with the decompiled code:
private void makeAuthenticationRequest(String urlPath, Map<String, String> params, AuthResultHandler handler) {
final AuthenticationManager.AuthAttempt attempt = this.newAuthAttempt(handler);
this.makeRequest(urlPath, HttpRequestType.GET, params, Collections.emptyMap(), new RequestHandler() {
public void onResult(Map<String, Object> result) {
Object errorResponse = result.get("error");
String token = (String)Utilities.getOrNull(result, "token", String.class);
if(errorResponse == null && token != null) {
if(!AuthenticationManager.this.attemptHasBeenPreempted(attempt)) {
AuthenticationManager.this.authWithCredential(token, result, attempt);
}
} else {
FirebaseError error = AuthenticationManager.this.decodeErrorResponse(errorResponse);
AuthenticationManager.this.fireAuthErrorIfNotPreempted(error, attempt);
}
}
public void onError(IOException e) {
FirebaseError error = new FirebaseError(-24, "There was an exception while connecting to the authentication server: " + e.getLocalizedMessage());
AuthenticationManager.this.fireAuthErrorIfNotPreempted(error, attempt);
}
});
}
At AuthListener level, the firebaseError code : -20
https://www.firebase.com/docs/java-api/javadoc/com/firebase/client/FirebaseError.html
The specified authentication credentials are invalid.
The Facebook Error Code:
code 400
Nothing relevant found here : https://developers.facebook.com/docs/graph-api/using-graph-api/#errors
The code for Authing:
public void authWithFirebase(final String provider, Map<String, String> options) {
if (options.containsKey(AUTH_OPTIONS_ERROR)) {
EventBus.getDefault().post(new MessageToDisplayEvent(options.get(AUTH_OPTIONS_ERROR), true));
} else {
if (provider.equalsIgnoreCase(AUTH_PROVIDER_TWITTER)) {
// if the provider is twitter, we must pass in additional options, so use the options endpoint
ref.authWithOAuthToken(provider, options, new AuthResultHandler(provider));
} else {
// if the provider is not twitter, we just need to pass in the oauth_token
ref.authWithOAuthToken(provider, options.get(AUTH_OPTIONS_TOKEN), new AuthResultHandler(provider));
}
}
}
TOKEN Validity:
From the code above, the Token is confirmed valid since :
https://graph.facebook.com/app?access_token=%7Byour_access_token%7D return a valid JSON
And the Facebook Tool AccessToken https://developers.facebook.com/tools/debug/accesstoken return a still valid TOKEN
What changed from user point of view:
Now, When I click on the FacebookLoginButton, I have a new dialog that ask "connection as %FacebookUserName", with 2 buttons ("Unconnect" & "Cancel")
I posted a bug report at Firebase, but I even do not know if this is Facebook or Firebase, any help, advise for exploring new issue surface or solution is welcome.
In Facebook Developper Console, switch-off the option about the "app key that is integrated in the client".
For me this changed the behavior. I will give more information as far I get from Firebase/Facebook
Here is a French Screenshot to help you setting up Facebook:
I'm trying to add a new account (after a Facebook login + server
validation) in AccountManager. The flow for this case is like this:
User login with Facebook
I got the details after the login is done and I validate them against the data I have on my server
If everything is ok, the server send back an auth_token (JWT token)
Having user's details and the auth_token I'm creating an account via AccountManager and once it is created, I set the authToken for
it.
On next login when the user will re-open the app I call getAuthToken which first try to get the cached authToken by calling peekAuthToken().
The problem
At point 5, peekAuthToken returns null but it shouldn't because I already set the autToken for that account.
Code
public static Bundle handleUserLogin(Context context, User user) {
SharedPreferences mPrefs = context.getSharedPreferences(Constants.PREFS_NAME, Context.MODE_PRIVATE);
AccountManager am = AccountManager.get(context);
Account account = new Account(user.getEmail(), ACCOUNT_TYPE);
Account[] accounts = am.getAccountsByType(ACCOUNT_TYPE);
boolean isNewAccount = true;
for (int i = 0; i < accounts.length; i++) {
if (user.getEmail().equalsIgnoreCase(accounts[i].name) && ACCOUNT_TYPE.equalsIgnoreCase(accounts[i].type)) {
isNewAccount = false;
account = accounts[i];
break;
}
}
if (isNewAccount) {
am.addAccountExplicitly(account, user.getPassword(), null);
accounts = am.getAccountsByType(ACCOUNT_TYPE);
for (int i = 0; i < accounts.length; i++) {
if (user.getEmail().equalsIgnoreCase(accounts[i].name) && ACCOUNT_TYPE.equalsIgnoreCase(accounts[i].type)) {
account = accounts[i];
break;
}
}
}
if (null != user.getPassword()) {
am.setPassword(account, user.getPassword());
}
Cs.error(TAG, "account " + account + " token " + user.getToken());
am.setAuthToken(account, user.getToken(), Authenticator.AUTHTOKEN_TYPE_FULL_ACCESS);
setUserData(user, account, am);
Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
result.putString(AccountManager.KEY_AUTHTOKEN, user.getToken());
mPrefs.edit().putString(Constants.KEY_CURRENT_USER, account.name).commit();
return result;
}
First I thought that maybe the reference to my new account is not the correct one (ex the one from AccountManager) so I search for account again.
Could you give me some indications about what I'm doing wrong or how should I make sure the authToken will be set for an account?
Thank you
I would not rely on explicit setting authToken in place different than in AbstractThreadedAccountAuthenticator's getAuthToken() method.
My guess is that the authToken is not cached yet when you request it.
I suggest just creating account via AccountManager.addAccountExplicitly() and deferring accessing authToken once you really need it.
In AbstractThreadedAccountAuthenticator's getAuthToken() I suggest applying logic like in the project I created a while ago.
Request authToken via AccountManager.peekAuthToken()
If authToken is set in cache then return it.
If not then request it from your server then cache via AccountManager.setAuthToken() and finally return.
Link: https://github.com/dawidgdanski/AccountAuthenticatorExample/blob/master/app/src/main/java/com/authenticator/account/auth/SimpleAuthenticator.java
Hope this will help you out:
https://github.com/dawidgdanski/AccountAuthenticatorExample
I am developing an app where users can log in with Google+. I added the Google+ sign in button and the user can log in without any issues.
Where I am having trouble is in retrieving the friends/ people in circles. This feature is not in the Android API, so I am trying to achieve this with an HTTP request (as documented here)
I set up my application in the developer console with a Public API access Android Key.
When I use an HttpGet with this URL:
https://www.googleapis.com/plus/v1/people/{the user's g+ id}/people/visible?key={my API key from the console}
I get a "keyInvalid" error with a "Bad Request" message.
If I try it without "?key={my key}" I get a "dailyLimitExceedingUnreg" error with message "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
Do you know why my reqest isn't working? What can I do to get it to work?
Okay, it turns out I didn't need to use HTTP get. Thanks for your responses.
This code did the trick:
Plus.PeopleApi.loadVisible(mGoogleApiClient,null).setResultCallback(new ResultCallback<People.LoadPeopleResult>() {
#Override
public void onResult(People.LoadPeopleResult loadPeopleResult) {
if (loadPeopleResult.getStatus().getStatusCode() == CommonStatusCodes.SUCCESS) {
PersonBuffer personBuffer = loadPeopleResult.getPersonBuffer();
try {
int count = personBuffer.getCount();
for (int i = 0; i < count; i++) {
Log.d(TAG, "Person " + i + " name: " + personBuffer.get(i).getDisplayName()+ " - id: " + personBuffer.get(i).getId());
}
} finally {
personBuffer.close();
}
} else {
Log.e(TAG, "Error");
}
}
});
You need to log in to http://console.developers.google.com and get an API key, the instead of ?key= you should put ?key=RANDOMCHARACTERS replacing RANDOMCHARACTERS for the key you got from Google.
If you haven't created a project, first you'll need to create one in that website, after that you should see the list of available API, search for the Google+ API and enable it to get the corresponding key.
People.list is an API method that requires user authentication not application authentication. Basically the only way you can make the request is like this:
GET https://www.googleapis.com/plus/v1/people/me/people/visible?access_token={user access_token}
I want to restrict my API endpoints access only to my android app, but without google_account/password.
I've the choice of those methods : https://developers.google.com/accounts/docs/OAuth2
For test, I succeeded to authenticate my android app to my API with this method: https://cloud.google.com/appengine/docs/python/endpoints/consume_android
==> This method allow you to authenticate your app with combo:
Login/password (Google account)
SHA1 and package name of your android APP
So if someone get my code (Decompiling apk) and modify my android code, they can't access to my API because SHA1 fingerprint of my app will change. (I tested it, and it works =) )
This method works fine, but I don't want Google login/password for authentication..
So I tried this method: https://developers.google.com/accounts/docs/OAuth2ServiceAccount
I successfully authenticate my android app, BUT, if my android code is modified by someone else(So the SHA1 changed), my android app can still connect to my API !! So if someone get my package and decompile it, he'll changed freely code and successfully access to my API..
Here is my API Code:
#ApiMethod( name = "ListCampagnes", httpMethod = ApiMethod.HttpMethod.GET, path="list", clientIds = {CONSTANTES.ANDROID_CLIENT_ID, CONSTANTES.WEB_CLIENT_ID, CONSTANTES.SERVICE_CLIENT_ID, com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID}, audiences = {CONSTANTES.ANDROID_AUDIENCE})
public Collection<Campagne> getCampagnes(#Named("NumPortable")String NumPortable, User user) throws UnauthorizedException {
if (user == null) throw new UnauthorizedException("User is Not Valid");
return CampagneCRUD.getInstance().findCampagne(NumPortable);
}
Here is my android code:
GoogleCredential credentialToAppengine;
try {
String p12Password = "notasecret";
KeyStore keystore = KeyStore.getInstance("PKCS12");
InputStream keyFileStream = getAssets().open("59ce5a08e110.p12");
keystore.load(keyFileStream, p12Password.toCharArray());
PrivateKey key = (PrivateKey)keystore.getKey("privatekey", p12Password.toCharArray());
credentialToAppengine = new GoogleCredential.Builder().setTransport(AndroidHttp.newCompatibleTransport()).setJsonFactory(new JacksonFactory()).setServiceAccountId("301991144702-3v9ikfp4lsmokee1utkucj35847eddvg#developer.gserviceaccount.com").setServiceAccountPrivateKey(key).setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/userinfo.email")).build();
} catch (GeneralSecurityException e) {
e.printStackTrace();
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
Do I try an other method for authenticate my android App ? Or did I missing something in my API code ?
Thanks a looot in advance,
Authenticate Android End point without Google User Account is just impossible ! I tried every ways but still doesn't works !
So here is my way to resolv this problem, without any user interaction (Maybe not the right but that works, and you've got strong authentication (SHA1 + Google Account)):
HERE IS MY ANDROID CODE
Get and Build Valid Credential
//Get all accounts from my Android Phone
String validGoogleAccount = null;
Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
Account[] accounts = AccountManager.get(context).getAccounts();
for (Account account : accounts) {
if (emailPattern.matcher(account.name).matches()) {
//Just store mail if countain gmail.com
if (account.name.toString().contains("gmail.com")&&account.type.toString().contains("com.google")){
validGoogleAccount=account.name.toString();
}
}
}
//Build Credential with valid google account
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this,"server:client_id:301991144702-5qkqclsogd0b4fnkhrja7hppshrvp4kh.apps.googleusercontent.com");
credential.setSelectedAccountName(validGoogleAccount);
Use this credential for secure calls
Campagneendpoint.Builder endpointBuilder = new Campagneendpoint.Builder(AndroidHttp.newCompatibleTransport(), new JacksonFactory(), credential);
HERE IS MY API BACKEND CODE:
API Annotation
#Api(
scopes=CONSTANTES.EMAIL_SCOPE,
clientIds = {CONSTANTES.ANDROID_CLIENT_ID,
CONSTANTES.WEB_CLIENT_ID,
com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID},
audiences = {CONSTANTES.ANDROID_AUDIENCE},
name = "campagneendpoint",
version = "v1"
)
Method code:
public Collection<Campagne> getCampagnes(#Named("NumPortable")String NumPortable, User user) throws UnauthorizedException {
if (user == null) throw new UnauthorizedException("User is Not Valid");
return CampagneCRUD.getInstance().findCampagne(NumPortable);
}
For the moment, it only works on Android (I don't know how we gonna do on IOS..)..
Hope It will help you !