From my understanding to access the gmail API using XOAUTH authentication it is required you have a token and token secret if you wish to use the 3-legged-authentication.
I can obtain the token from the account manager AccountManager.KEY_AUTHTOKEN but I am unsure how to obtain the token secret.
accountManager.getAuthToken(account, "oauth2:https://mail.google.com/", null, MainActivity.this,
new AccountManagerCallback<Bundle>() {
#Override
public void run(final AccountManagerFuture<Bundle> future) {
try {
final String token = future.getResult().getString(
AccountManager.KEY_AUTHTOKEN);
} catch (final Exception e) {
Log.e(TAG, e.getMessage(), e);
}
}
}, null);
Is it possible to obtain the token secret from the account manager or would I have to launch the browser to obtain this information?
From the research that I have concluded this is not possible.
However a possible solution to overcome this problem would to be to use the Signpost oAuth library which enables request tokens to be returned from the browser.
Related
I'm developing an Android app that consumes data from my own REST API server. I want to use Firebase authentication because it allows the user to login using Google, Facebook, Twitter... in a very simple way.
But I'm not sure how to use ID tokens:
Because ID tokens have expiration date, should I call getToken method on every request in the client app, so I'm sure I'm sending a valid token every time?
Should I call verifyIdToken in the server each time I receive a request from the client app?
I don't know what these methods (getToken and verifyIdToken) do under the hood, and because they are asynchronous, I fear they are doing a request to Firebase servers on every call. So I think that making 2 request to Firebase servers in each of my requests is not the way to go...
Both getToken() and VerifyIdToken() are designed to be called for every outgoing/incoming request.
1) Although getToken() is asynchronous, the Firebase Android SDK actually caches the current Firebase user token in local storage. So long as the cached token is still valid (i.e. within one hour since issued), getToken() returns the token immediately. Only when the cached token expires does the SDK fetch a new token from remote Firebase server.
2) VerifyIdToken() is also optimized for performance. It caches the Firebase token public cert (valid for 6 hours) which is used to validate the token signature on local machine. No RPC is involved except for downloading the public cert.
You refresh token each time when is no more valid. And yes, you should verify token on server-side each time. If is no more valid, you send 401 error code with error message (if you want). Verify token is used when you refresh token, and token is append to each request. If you use OkHttp you can create an interceptor that is adding token in header to each request and also can refresh token when error code is 401.
POST https://YOUR_AUTH0_DOMAIN/delegation
Content-Type: 'application/json'
{
"client_id": "YOUR_CLIENT_ID",
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"refresh_token": "your_refresh_token",
"api_type": "app"
}
From what you have explained in the question, I guess you are talking about cross client resource access using Google sign in. And specifically you seem to be interested in obtaining the Id token once and use it without having to obtain on each subsequent API call.
This more or less is synonymous with the offline access mechanism.
In offline access, the Client I.e. the Android app asks for user authorisation for requested scopes. Upon authorisation, instead of issuing an access token, auth server returns a short lived authorisation code which can be used to generate an access token and refresh token.
The client then can pass the authorisation code to the backend over a secure connection. Backend server can retrieve the author token and refresh token and store them in a secure location. The access token is short lived and can be used to access scoped resources for a short time and refreshed from time to time using the refresh token. The refresh token does not expire but can be revoked. If revoked, server app should ask the client app to re-fetch the author code.
Please go through this link which details the complete infrastructure along with the steps to be followed both by client and server app -
https://developers.google.com/identity/protocols/CrossClientAuth
Now coming to your question, you should use a slightly different API to obtain the auth code. Check out this API -
https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignInOptions.Builder.html#requestServerAuthCode(java.lang.String)
Sample code at - https://developers.google.com/identity/sign-in/android/offline-access
Use below code in your application class and regId is the value holder for your device token.
private void checkPlayService() {
// Check device for Play Services APK. If check succeeds, proceed with
// GCM registration.
if (checkPlayServices()) {
GoogleCloudMessaging googleCloudMessaging = GoogleCloudMessaging.getInstance(activity);
regId = getRegistrationId();
if (TextUtils.isEmpty(regId)) {
registerInBackground();
}
} else {
Log.i(TAG, "No valid Google Play Services APK found.");
}
}
private String getRegistrationId() {
String registrationId = sp.getString(Consts.PROPERTY_REG_ID, "");
if (TextUtils.isEmpty(registrationId)) {
Log.i(TAG, "Registration not found.");
return "";
}
// Check if app was updated; if so, it must clear the registration ID
// since the existing regID is not guaranteed to work with the new
// app version.
int registeredVersion = sp.getInt(PROPERTY_APP_VERSION,0);
int currentVersion = getAppVersion();
if (registeredVersion != currentVersion) {
Log.i(TAG, "App version changed.");
return "";
}
return registrationId;
}
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (googleCloudMessaging == null) {
googleCloudMessaging = GoogleCloudMessaging.getInstance(activity);
}
regId = googleCloudMessaging.register(Consts.PROJECT_NUMBER);
msg = "Device registered, registration ID=" + regId;
Log.e("GCMID",msg);
storeRegistrationId(regId);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
Log.i(TAG, msg + "\n");
}
}.execute(null, null, null);
}
private void storeRegistrationId(String regId) {
int appVersion = getAppVersion();
Log.i(TAG, "Saving regId on app version " + appVersion);
sp.edit().putString(Consts.PROPERTY_REG_ID, regId).commit();
sp.edit().putInt(PROPERTY_APP_VERSION, appVersion).commit();
}
The documentation here says the following
Warning: The Google+ Sign-In button and the plus.login scope used by
Google+ Sign-In, are not currently supported for use with the Google+
Domains API. Requests that are made to the Google+ Domains API using
an authentication token granted for the
https://www.googleapis.com/auth/plus.login scope, or generated by the
Google+ Sign-In button, will fail.
So if we need to access Google Plus Domains API how do we do it using GoogleApiClient object in android?
I want a list of a user's circles for which I need to use the Domains API.
Consider using GoogleAuthUtil for Google Plus Domain authentication.
And most importantly: "Domain API will work only with domain email id" (which is not the gmail id).
String scopes = "oauth2:" + "https://www.googleapis.com/auth/plus.me " +
"https://www.googleapis.com/auth/plus.circles.read";
String accountName = "domain_email_id_used_for_login";//fetch from AccountManager or ask the user to enter
String token = "";
try {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this);
token = sharedPref.getString("token", "");
if (!token.equals("")) {
GoogleAuthUtil.clearToken(LoginActivity.this, token);
}
token = GoogleAuthUtil.getToken(LoginActivity.this,
accountName, scopes);
GoogleCredential googleCredential = new GoogleCredential().setAccessToken(token);
PlusDomains plusDomains = new PlusDomains.Builder(new NetHttpTransport(), new JacksonFactory(), googleCredential).setApplicationName("GPlusLab").build();
plusDomains.people().get("me").execute();
return token;
} catch (UserRecoverableAuthException e) {
startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
} catch (GoogleAuthException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
github link to complete example.
First I am able to login with twitter button with twitter4j-core jar version 2.1.11 and able to retrieve access token and secret. But after twitter has stopped the functionality of API version 1 I have added new twitter4j-core jar version 3.0.3 with API version 1.1. Now I am not able to retrieve the access token and access secret.
Here is database method where I have stored my access token and access secret.
public static void save(Context context, final OAuthConsumer oAuthConsumer) {
SharedPreference.storeValue(OAuth.OAUTH_TOKEN, oAuthConsumer.getToken(), context);
SharedPreference.storeValue(OAuth.OAUTH_TOKEN_SECRET, oAuthConsumer.getTokenSecret(), context);
}
Another Database method where I am retrieving the stored values as:
public static AccessToken retrieveStoredAccessToken(Context context)
{
String token = SharedPreference.getValueFromStore(OAuth.OAUTH_TOKEN , context);
String secret = SharedPreference.getValueFromStore(OAuth.OAUTH_TOKEN_SECRET, context);
return new AccessToken(token, secret);
}
Now when user clicks the login button it will redirect to method isAuthenticated() where I am retrieve stored access token and secret
public static boolean isAuthenticated(Context context)
{
try
{
AccessToken accessToken = Database.retrieveStoredAccessToken(context); // String token and secret is received null from databse
Twitter twitter = new TwitterFactory().getInstance();
twitter.setOAuthConsumer(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET);
twitter.setOAuthAccessToken(accessToken);
twitter.getAccountSettings();
return true;
}
catch (TwitterException e)
{
System.out.println("Twitter Authenticated : false");
return false;
}
catch(Exception e)
{
System.out.println("Twitter Authenticated : false");
return false;
}
}
Access token returns Null Exception. I am able to login with twitter but session is not stored because of access token and access secret throws null exception.
following jar file use
1) signpost-commonshttp4-1.2.1.1.jar
2) signpost-core-1.2.1.1.jar
3) twitter4j-core-3.0.3.jar
Finally I have figured my problem.
Just need to change these things:
https://api.twitter.com/oauth/authenticate?force_login=true from http://api.twitter.com/oauth/authenticate
After reading the last Google + news at here and this. How do I get access token after I complete the sign in?
To answer doubts about oauth scope (just to be useful for googlers):
To fully understand, Google-it some about authentication and authorization concepts.
Check if user/password exists is about authentication part.
Scope is required to authorization part: what you are authorized to do or receive in behalf of user. To get a list of scopes allowed, check the OAuth service documentation.
From Google and G+, most common scopes can be found on: https://developers.google.com/+/api/oauth?hl=pt-ZA
For example, to get all possible information from user, you can use the scope:
"openid profile email https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/plus.me"
(the first word refer to protocol, followed by words that ask for fields on response, and desired scopes can be declared toghether with a space separator)
Note: Later, if you try use your access token to request or do anything that you don't asked before with a scope, the service can return an authorization error.
For Google, a good tool you can use to learn about his OAuth service and scope is the OAuth Playground: https://developers.google.com/oauthplayground/
Did you have a look at the API reference?
The class you are probably looking for is com.google.android.gms.auth.GoogleAuthUtil.
It provides, amongst others, the following method:
static String getToken(Context context, String accountName, String
Description:
Authenticates the user and returns a valid Google authentication token, or throws an exception if there was an error getting a token.
Usage:
String token;
try {
token = GoogleAuthUtil.getToken(context, accountName, scope);
} catch (GooglePlayServicesAvailabilityException playEx) {
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(
playEx.getConnectionStatusCode(),
Activity.this,
AUTH_REQUEST_CODE);
// Use the dialog to present to the user.
} catch (UserRecoverableAutException recoverableException) {
Intent recoveryIntent = recoverableException.getIntent();
// Use the intent in a custom dialog or just startActivityForResult.
} catch (GoogleAuthException authEx) {
// This is likely unrecoverable.
Log.e(TAG, "Unrecoverable authentication exception: " + authEx.getMesssage(), authEx);
} catch (IOException ioEx) {
Log.i(TAG, "transient error encountered: " + ioEx.getMessage());
doExponentialBackoff();
}
You need to fetch it using async task.
public void onConnected(Bundle connectionHint) {
// Reaching onConnected means we consider the user signed in.
Log.i(TAG, "onConnected");
// Update the user interface to reflect that the user is signed in.
mSignInButton.setEnabled(false);
mSignOutButton.setEnabled(true);
mRevokeButton.setEnabled(true);
// Retrieve some profile information to personalize our app for the user.
Person currentUser = Plus.PeopleApi.getCurrentPerson(mGoogleApiClient);
AsyncTask<Void, Void, String > task = new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String token = null;
final String SCOPES = "https://www.googleapis.com/auth/plus.login ";
try {
token = GoogleAuthUtil.getToken(
getApplicationContext(),
Plus.AccountApi.getAccountName(mGoogleApiClient),
"oauth2:" + SCOPES);
} catch (IOException e) {
e.printStackTrace();
} catch (GoogleAuthException e) {
e.printStackTrace();
}
return token;
}
#Override
protected void onPostExecute(String token) {
Log.i(TAG, "Access token retrieved:" + token);
}
};
task.execute();
System.out.print("email" + email);
mStatus.setText(String.format(
getResources().getString(R.string.signed_in_as),
currentUser.getDisplayName()));
Plus.PeopleApi.loadVisible(mGoogleApiClient, null)
.setResultCallback(this);
// Indicate that the sign in process is complete.
mSignInProgress = STATE_DEFAULT;
}
Your access token will be stored into token variable.
I need to obtain OAuth2 authentication token to pass it to the server so it can fetch list of Google Reader feeds for the user. Server is .NET - I have no access to it or to it's code but most likely it is using unofficial Reader API
I was able to use Android Account manager to obtain valid token for this purpose with the following code (notice that authTokenType="reader")
Account account = accounts[0];
manager.getAuthToken(account, "reader", null, this, new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> future) {
try {
// If the user has authorized your application to use the tasks API
// a token is available.
String token = future.getResult().getString(AccountManager.KEY_AUTHTOKEN);
// Now you can send the token to API...
cacheManager.putString(GOOGLE_AUTH, token);
GoogleReaderManager.startAddFeedActivity(AddGoogleReaderSourcesActivity.this);
finish();
} catch (OperationCanceledException e) {
Log.e(TAG, "User cancelled", e);
finish();
} catch (Exception e) {
Log.e(TAG, "Failed to obtain Google reader API_KEY", e);
}
}
}, null);
The code above works fine when I send token to the server side .Net app: the app is able to retrieve the list of Reader feeds.
The problem is that this only works for "Google inside" devices. On Nook I have no such luck since there's no way that I was able to find to add Google account to the account manager. So I'm trying to it using OAuth 2 protocol as described here
It works fine as far as obtaining the token: User approves the app from the mobile page which returns the code token which then mobile app exchanges for the Auth token. However this token will not work with the server process. I have a feeling that perhaps I'm using the wrong scope in this URL:
https://accounts.google.com/o/oauth2/auth?response_type=code&scope=https://www.google.com/reader/api/0/subscription/list&redirect_uri=http://localhost&approval_prompt=force&state=/ok&client_id={apps.client.id}
Scopes that I did try in various combinations:
https://www.google.com/reader/api
https://www.google.com/reader/api/0
https://www.google.com/reader/api/0/subscription/list
https://www.google.com/reader/api+https://www.google.com/reader/atom
Here's example of JSON that is returned from get token POST
{"expires_in":3600,
"token_type":"Bearer",
"access_token":"ya29.AHES6ZSEvuUb6Bvd2DNoMnnN_UnfxirZmf_RQjn7LptFLfI",
"refresh_token":"1\/bUwa5MyOtP6VyWqaIEKgfPh08LNdawJ5Qxz6-qZrHg0"}
Am I messing up scope or token type? Not sure how to change a token type. Any other ideas?
P.S. Google account login page asks: Manage your data in Google Reader, that's why I suspect that the scope is wrong
I got it working for https://www.google.com/reader/api/0/subscription/list. So thought of sharing with you.
I have valid access_token:
This is what i tried to resolve it (partially) :
Google provides OAuth 2.o playgound; where they actually simulate all aspects of OAuth 2.0 as well as final API call to fetch data.
I found this very helpful as it clearly shows what is being sent to request.
Here is the URL : https://developers.google.com/oauthplayground/
Using this, i tweaked my api call below and it works :)
public static String getReaderContent(String accessToken){
String url = "https://www.google.com/reader/api/0/subscription/list" ;
HttpClient client = new HttpClient();
GetMethod method = new GetMethod(url);
String response="";
method.setRequestHeader("Authorization", "OAuth "+accessToken);
try {
int statusCode = client.executeMethod(method);
String response= method.getResponseBodyAsString();
System.out.println("response " + responseStr);
} catch (HttpException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
So this works properly fine for getting subscription list; but have not been able to make it work for reader api which you have mentioned in your question.
Let me know if you have got way around google reader API.