I'm making an app that should allow the user to register through its google account. I want to retrieve automatically as many profile infos as I can. I found this very interesting example, which would allow me to get many infos (see step 4 of that demo). Now, how do I use it on android? I saw many examples of how to get the authentication token with the AccountManager with Oauth2 (example), but I don't know what to do from there to make those calls and retrieve those infos. Also in that example the code is in javascript and I don't know how to port it properly to java...
I have already done the google dev console registration stuff.
Are Oauth2 and OpenID the same thing? If not, do I have to use either one OR the other?
Ok, done. As expected, I found all the infos in the docs, and using Google's Oauth2 Playground helped to understand what to send to https://www.googleapis.com/oauth2/v1/userinfo in order to receive the profile data.
In the end, it turns out we don't need to create a client ID in google's dev console to do this.
Now, to the code. The activity:
public class MainActivity extends Activity {
public Activity mContext;
private AccountManager accountManager;
private final String SCOPES = "oauth2:https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile";
private String authToken;
private GetProfileDataTask googleTask;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
mContext = this;
accountManager = AccountManager.get(mContext);
//other stuff here...
}
public void getProfileData() {
accountManager.getAuthTokenByFeatures(
"com.google",
SCOPES,
null, mContext, null, null,
new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> future) {
try {
Bundle bundle = future.getResult();
//bundle.getString(AccountManager.KEY_ACCOUNT_NAME);
//bundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
} catch (Exception e) {
System.out.println("getAuthTokenByFeatures() cancelled or failed:");
e.printStackTrace();
authToken = "failure";
}
if(!authToken.equals("failure")) {
googleTask = new GetProfileDataTask();
googleTask.execute(authToken);
}
}
}, null);
}
}
The AsyncTask that gets the data:
public class GetProfileDataTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... tokens) {
RestTemplate restTemplate = new RestTemplate(false);
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
String json = null;
try {
//the response is of type "application/json"
json = restTemplate.getForObject(
"https://www.googleapis.com/oauth2/v1/userinfo" +
"?access_token={token}" +
"&access_token_type=bearer",
String.class,
tokens[0]); //this is the authToken from before, obv
} catch(RestClientException er) {
Log.e("GetProfileDataTask", er.toString(), er);
json = null;
}
return json;
}
#Override
protected void onPostExecute(String asyncResult) {
if(asyncResult != null)
//do something with your data, for example deserialize it
else
//do something else
}
}
The received json is like this:
{
"family_name": "Smith",
"name": "John Smith",
"picture": "https://lh3.googleusercontent.com/-randomlettersandnumbers/AAAAAAAAAAI/AAAAAAAAAAA/morerandomlettersandnumbers/photo.jpg",
"locale": "it",
"gender": "male",
"email": "youremail#whatever.itis",
"link": "https://plus.google.com/133780085840848123456",
"given_name": "John",
"id": "133780085840848123456",
"verified_email": true
}
Since you want to allow user sign in your app via their Google accounts, you can use OpenID, and Google supports it.
Note: If you provide a “sign-in with Google” feature, we recommend using Google+ Sign-In.
If you just want get usr's info in Google on behalf of users, you can just use Oauth2. Refer to Google'a official documents, I think they are detailed, authoritative and easy to get along.
As this doc says:
5.Obtain user information from the ID token
An ID token is a cryptographically signed JSON object encoded in base 64. Normally, it is critical that you validate an ID token before you use it, but since you are communicating directly with Google over an intermediary-free HTTPS channel and using your client secret to authenticate yourself to Google, you can be confident that the token you receive really comes from Google and is valid.
So in a word, read these docs carefully and you'll get be clear about how to accomplish your app.
Related
i'm new with android and i need to do a connection with server with oauth 2.0 i looked in internet and found just example how to dowit with google or github but my need is to connect with my own server i have the clientId clientSecret and the scope all i need is to get the token correctly
i hope my question is clear
thank you
this what i have donne
AccountManager am = AccountManager.get(Authentification.this);
Bundle options = new Bundle();
options.putSerializable("numero", numero);
am.getAuthToken(
null,
"whrite",
options,
this,
new OnTokenAcquired(),
null);
private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
#Override
public void run(AccountManagerFuture<Bundle> result) {
// Get the result of the operation from the AccountManagerFuture.
try{
Bundle bundle = result.getResult();
// The token is a named value in the bundle. The name of the value
// is stored in the constant AccountManager.KEY_AUTHTOKEN.
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
System.out.println("================>>>>"+token);
}catch(Exception e){
}
}
}
I would start with the AppAuth code sample. My blog post
has step by step instructions on how to run it.
Once it is working reconfigure it to point to your own Authorization Server.
I'm building an app that uses Google Cloud Speech.
I have a Google Service account key in my app, and I use it to call the API.
It works well when used by one user, but does not work when multiple users use it at the same time.
For example, only one user is available or all are unavailable.
The rights of the service account key are project owner.
I think it's a service account key issue...
How do I fix it?
private class AccessTokenTask extends AsyncTask<Void, Void, AccessToken> {
#Override
protected AccessToken doInBackground(Void... voids) {
final SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
String tokenValue = prefs.getString(PREF_ACCESS_TOKEN_VALUE, null);
long expirationTime = prefs.getLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, -1);
// Check if the current token is still valid for a while
if (tokenValue != null && expirationTime > 0) {
if (expirationTime > System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION_TOLERANCE) {
return new AccessToken(tokenValue, new Date(expirationTime));
}
}
final InputStream stream = mContext.getResources().openRawResource(R.raw.credential);
try {
final GoogleCredentials credentials = GoogleCredentials.fromStream(stream).createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
prefs.edit()
.putString(PREF_ACCESS_TOKEN_VALUE, token.getTokenValue())
.putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, token.getExpirationTime().getTime())
.apply();
return token;
} catch (IOException e) {
Log.e(TAG, "Failed to obtain access token.", e);
}
return null;
}
#Override
protected void onPostExecute(AccessToken accessToken) {
mAccessTokenTask = null;
final ManagedChannel channel = new OkHttpChannelProvider()
.builderForAddress(GOOGLE_API_HOSTNAME, GOOGLE_API_PORT)
.nameResolverFactory(new DnsNameResolverProvider())
.intercept(new GoogleCredentialsInterceptor(new GoogleCredentials(accessToken)
.createScoped(SCOPE)))
.build();
mApi = SpeechGrpc.newStub(channel);
// Schedule access token refresh before it expires
if (mHandler != null) {
mHandler.postDelayed(mFetchAccessTokenRunnable,
Math.max(accessToken.getExpirationTime().getTime() - System.currentTimeMillis() - ACCESS_TOKEN_FETCH_MARGIN, ACCESS_TOKEN_EXPIRATION_TOLERANCE));
}
}
}
This code is the code that calls 'credential.json' file on Android and gets 'Access token'.
The server for this app is python and communicates via http.
https://github.com/GoogleCloudPlatform/android-docs-samples/tree/master/speech/Speech
The description in the link above tells you to delegate the authentication to the server.
I want to write that part with python code.
What should I do?
In the link you provided in the description, they suggest you to read first the basic authentication concepts document. In your case, use a service account for the Android application.
I understand that you have already been able to provide end user credentials to a Google Cloud Platform API, as for example Cloud Speech API.
If you want to authenticate multiple users to your application you should use instead Firebase authentication. The link contains a brief explanation and a tutorial.
There are several Python client libraries for GCP that you can use, depending on what operations do you want to perform on the server. And regarding Python authentication on the server side, this documentation shows how the authentication for Google Cloud Storage works (have this example in mind as a reference).
I was looking on the Firebase SDK and I found that FirebaseUser has a method called
getProviderData()
that we can use to get some information about the provider in case that we are login with Google or Facebook.
But let's say I want to retrieve the FirstName and LastName instead of DisplayName also the Facebook email and some specific information that is not implemented on UserInfo.
I found a solution for Facebook which is requiring an additional rest call.
GraphRequest request = GraphRequest.newMeRequest(AccessToken.getCurrentAccessToken(), new GraphRequest.GraphJSONObjectCallback() {
#Override
public void onCompleted(JSONObject object, GraphResponse response) {
try {
jsonResponseMap.put(USER_EMAIL, object.getString("email"));
jsonResponseMap.put(USER_FIRST_NAME, object.getString("first_name"));
jsonResponseMap.put(USER_LAST__NAME, object.getString("last_name"));
listener.onResult(jsonResponseMap);
} catch (JSONException e) {
e.printStackTrace();
listener.onError(e.getMessage());
}
}
});
Bundle parameters = new Bundle();
parameters.putString("fields","id, email, first_name, last_name");
request.setParameters(parameters);
request.executeAsync();
So the big question is that why make another call when Firebase is providing the json already.
Researching a little bit I found that Firebase SDK is implementing a class called
zzf which implement UserInfo and returning the json using
String getRawUserInfo().
Using the debugger I got this json from Google (Facebook has similar format):
Google
{
"azp": "86867144690-7vvjul4p96e143413484c5h9e.apps.googleusercontent.com",
"aud": "86813434690-o49lqnvo8c1232315rdrmi1ej9.apps.googleusercontent.com",
"sub": "103026921153548850697",
"email": "Steve.Jhon#gmail.com",
"email_verified": true,
"exp": 1520122390,
"iss": "https://accounts.google.com",
"iat": 1520128790,
"name": "Jhon Steve",
"picture": "https://lh6.googleusercontent.com/-4-WnMz5Fqoo/AAAAAAAAAAI/AAAAAAAAAAA/AGi4132322313SfRlc412L5tRvZJsOA/s96-c/photo.jpg",
"given_name": "Jhon",
"family_name": "Steve",
"locale": "en"
}
With this information, I want to ask you guys that...
How can I cast the zzf class to retrieve the proper raw information from the provider?
If you want additional user info after sign-in/sign-up, you can get it from AdditionalUserInfo on AuthResult.
It provides a getProfile API which returns a map<String, Object> with all provide related profile info.
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'm building an Android app that connects to an Amazon S3 bucket and retrieves mp3 files stored within. This is my first time using Google Sign-in, and it's for a (hopefully) production app, and I want to do it properly.
I've followed all the directions here and have successfully received an ID Token by calling GoogleSignInAccount.getIdToken().
I have then used Amazon's directions for OpenID Connect providers here and used this code:
// Initializing the Amazon Cognito credentials provider
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider (
getApplicationContext(),
"us-east-1:220fe85c-fcc9-4ecc-b923-1357e1380fde", // Example Identity Pool ID
Regions.US_EAST_1 // Example Region
);
Map<String, String> logins = new HashMap<String, String>();
logins.put("accounts.google.com", idToken);
credentialsProvider.setLogins(logins);
to login. However, nothing is showing up in my Identity Pool. I'm wondering whether it's some confusion on my part in regards to which Client ID I am using. When I created the project on the Google Developer console, I received two ID's. One for a Web Application, and one for Android.
As per Google's instructions here, I passed the Web client ID to the requestIdToken method when I created the GoogleSignInOptions object, and the Android ID to the Identity Pool, like this:
I removed all the other numbers after the hyphen, as the example calls for a smaller ID, but for the record, neither version works. The original was like:
1034544032360-77XXXkoq9XXkdXXsj82uhdXXXbqii6t2.apps.googleusercontent.com
Except when I test my app, It seems to be successful, no errors are thrown, but no new identities are logged in my identity pool.
What am I missing? I would really appreciate a nudge in the right direction.
Is that code you gave the extent of your logging in? The Cognito Android SDK gets credentials lazily, setting logins alone won't do anything. Try adding a credentialsProvider.refresh(); after that.
Okay, I solved it finally; there were a few things I missed.
Firstly, as Jeff Bailey mentioned, I wasn't calling credentialsProvider.refresh() after I had set the login token, like this:
private void setCredentials(String token) {
Map<String, String> logins = new HashMap<>();
logins.put("accounts.google.com", token);
credentialsProvider.withLogins(logins);
}
However, that method requires a network request, so that had to be called from an Async task.
Secondly, I used different code to get an ID token from Google, instead of GoogleSignInAccount.getIdToken. See below:
private class GetAndSetGoogleToken extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... params) {
try {
GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext());
AccountManager am = AccountManager.get(getApplicationContext());
Account[] accounts = am.getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
token = GoogleAuthUtil.getToken(getApplicationContext(), accounts[0].name,
"audience:server:client_id:" + serverClientId);
} catch(GoogleAuthException ex) {
Log.d(TAG, "GoogleAuthException has been thrown by GetAndSetGoogleToken!");
} catch(IOException ex2) {
Log.d(TAG, "IOException has been thrown by GetAndSetGoogleToken!");
}
return token;
}
#Override
protected void onPostExecute(String token) {
// Passing the ID Token as an Extra to the Intent and starting a new Activity.
goToNextActivity(token);
super.onPostExecute(token);
}
}
Finally, I hadn't modified my IAM Trust Policies to recognise accounts.google.com as a trusted entity. Once doing so, they looked like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": [
"cognito-identity.amazonaws.com",
"accounts.google.com" // I needed to add this
]
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "us-east-1:xxxx2e4a-4cf6-4121-aa16-xxxx53374a49"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "authenticated"
}
}
}
]
}
Having done all that, it worked fine.
Hope this helps someone; it doesn't seem to be a well documented use-case unfortunately.