in the blow code, whats is transport and jsonFactory ? (I do not understand)
https://developers.google.com/identity/sign-in/android/backend-auth#using-a-google-api-client-library
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
...
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport /**Here**/, jsonFactory /**Here**/)
.setAudience(Arrays.asList(CLIENT_ID))
// If you retrieved the token on Android using the Play Services 8.3 API or newer, set
// the issuer to "https://accounts.google.com". Otherwise, set the issuer to
// "accounts.google.com". If you need to verify tokens from multiple sources, build
// a GoogleIdTokenVerifier for each issuer and try them both.
.setIssuer("https://accounts.google.com")
.build();
// (Receive idTokenString by HTTPS POST)
GoogleIdToken idToken = verifier.verify(idTokenString);
if (idToken != null) {
Payload payload = idToken.getPayload();
// Print user identifier
String userId = payload.getSubject();
System.out.println("User ID: " + userId);
// Get profile information from payload
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
// Use or store profile information
// ...
} else {
System.out.println("Invalid ID token.");
}
Since all the other answers are blah blah blah, here's a short answer:
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
GoogleIdTokenVerifier verifier =
new GoogleIdTokenVerifier.Builder(new NetHttpTransport(), new GsonFactory());
The GoogleIdTokenVerifier.Builder returns a GoogleIdTokenVerifier that will make a request to the tokeninfo endpoint with the transport you give it and use the JSONFactory to create a parser to parse the response.
Here is an example of an authenticator for a Cloud Endpoints project that uses the GoogleIdTokenVerifier.Builder
public class GoogleAuthenticator implements Authenticator {
private static final Logger log = Logger.getLogger(GoogleAuthenticator.class.getName());
private static final JacksonFactory jacksonFactory = new JacksonFactory();
// From: https://developers.google.com/identity/sign-in/android/backend-auth#using-a-google-api-client-library
// If you retrieved the token on Android using the Play Services 8.3 API or newer, set
// the issuer to "https://accounts.google.com". Otherwise, set the issuer to
// "accounts.google.com". If you need to verify tokens from multiple sources, build
// a GoogleIdTokenVerifier for each issuer and try them both.
GoogleIdTokenVerifier verifierForNewAndroidClients = new GoogleIdTokenVerifier.Builder(UrlFetchTransport.getDefaultInstance(), jacksonFactory)
.setAudience(Arrays.asList(CRLConstants.IOS_CLIENT_ID, CRLConstants.ANDROID_CLIENT_ID_RELEASE, CRLConstants.ANDROID_CLIENT_ID_DEBUG))
.setIssuer("https://accounts.google.com")
.build();
GoogleIdTokenVerifier verifierForOtherClients = new GoogleIdTokenVerifier.Builder(UrlFetchTransport.getDefaultInstance(), jacksonFactory)
.setAudience(Arrays.asList(CRLConstants.IOS_CLIENT_ID, CRLConstants.ANDROID_CLIENT_ID_RELEASE, CRLConstants.ANDROID_CLIENT_ID_DEBUG))
.setIssuer("accounts.google.com")
.build();
// Custom Authenticator class for authenticating google accounts
#Override
public User authenticate(HttpServletRequest request) {
String token = request.getHeader("google_id_token");
if (token != null) {
GoogleIdToken idToken = null;
try {
idToken = verifierForNewAndroidClients.verify(token);
if(idToken == null) idToken = verifierForOtherClients.verify(token);
if (idToken != null) {
GoogleIdToken.Payload payload = idToken.getPayload();
// Get profile information from payload
String userId = payload.getSubject();
String email = payload.getEmail();
return new GoogleUser(userId, email);
} else {
log.warning("Invalid Google ID token.");
}
} catch (GeneralSecurityException e) {
log.warning(e.getLocalizedMessage());
} catch (IOException e) {
log.warning(e.getLocalizedMessage());
}
}
return null;
}
}
You need to select transport according to the platform on which you are running the code.
Quoting from the documentation
Implementation is thread-safe, and sub-classes must be thread-safe. For maximum efficiency, applications should use a single globally-shared instance of the HTTP transport.
The recommended concrete implementation HTTP transport library to use depends on what environment you are running in:
Google App Engine: use com.google.api.client.extensions.appengine.http.UrlFetchTransport.
com.google.api.client.apache.ApacheHttpTransport doesn't work on App Engine because the Apache HTTP Client opens its own sockets (though in theory there are ways to hack it to work on App Engine that might work).
com.google.api.client.javanet.NetHttpTransport is discouraged due to a bug in the App Engine SDK itself in how it parses HTTP headers in the response.
Android:
For maximum backwards compatibility with older SDK's use newCompatibleTransport from com.google.api.client.extensions.android.http.AndroidHttp (read its JavaDoc for details).
If your application is targeting Gingerbread (SDK 2.3) or higher, simply use com.google.api.client.javanet.NetHttpTransport.
Other Java environments
com.google.api.client.javanet.NetHttpTransport is based on the HttpURLConnection built into the Java SDK, so it is normally the preferred choice.
com.google.api.client.apache.ApacheHttpTransport is a good choice for users of the Apache HTTP Client, especially if you need some of the configuration options available in that library.
Documentation Link: https://developers.google.com/api-client-library/java/google-http-java-client/reference/1.19.0/com/google/api/client/http/HttpTransport?is-external=true
If you blindly follow the 2nd answer to the question, you will get the exception Caused by: java.lang.ClassNotFoundException: com.google.appengine.api.urlfetch.HTTPMethod
JacksonFactory is deprecated. So this works.
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(new NetHttpTransport(), new GsonFactory())
.setAudience(Arrays.asList(CRLConstants.IOS_CLIENT_ID, CRLConstants.ANDROID_CLIENT_ID_RELEASE, CRLConstants.ANDROID_CLIENT_ID_DEBUG))
.setIssuer("accounts.google.com")
.build();
Related
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).
We have some API Gateway endpoints. Each endpoint is protected with a Custom Authorizer (CA). This CA inspect the HTTP call and verify the presence of the Authorization header. This header must contains an OpenId Connect token (a simple JWT token), so the CA can inspect it and make some checks and validations.
When we use Postman to invoke the endpoint, it works without a problem, since we can setup the right headers.
The problems start to raises when we use the generated Android SDK to make the same call, since each attempt to make the call sends an AWS4 signature as header. We can figure out how to send Authorization header with JWT.
We get what we need by extending ApiClientFactory class and adding the header in an explicit ways:
public class CustomApiClientFactory extends ApiClientFactory {
private String LOGIN_NAME = "a.provider.com";
private AWSCredentialsProvider provider;
#Override
public ApiClientFactory credentialsProvider(AWSCredentialsProvider provider) {
this.provider = provider;
return super.credentialsProvider(provider);
}
#Override
ApiClientHandler getHandler(String endpoint, String apiName) {
final Signer signer = provider == null ? null : getSigner(getRegion(endpoint));
// Ensure we always pass a configuration to the handler
final ClientConfiguration configuration = new ClientConfiguration();
return new ApiClientHandler(endpoint, apiName, signer, provider, null, configuration) {
#Override
Request<?> buildRequest(Method method, Object[] args) {
Request<?> request = super.buildRequest(method, args);
request.addHeader("Authorization", "Bearer " + ((CognitoCachingCredentialsProvider) provider).getLogins().get(LOGIN_NAME));
return request;
}
};
}
}
Even if it works, it sounds to me like a workaround. There is some known best practice in facing this problem?
API Gateway allows the request to be defined for each method just add a HTTP request header in the method request and the generated SDK will have a field to specify the header.
Console:
Generated Android SDK:
#com.amazonaws.mobileconnectors.apigateway.annotation.Operation(path = "/companies", method = "GET")
CompanyList companiesGet(
#com.amazonaws.mobileconnectors.apigateway.annotation.Parameter(name = "x-vendor-authorization", location = "header")
String xVendorAuthorization,
#com.amazonaws.mobileconnectors.apigateway.annotation.Parameter(name = "continuationToken", location = "query")
String continuationToken,
#com.amazonaws.mobileconnectors.apigateway.annotation.Parameter(name = "pageSize", location = "query")
String pageSize,
#com.amazonaws.mobileconnectors.apigateway.annotation.Parameter(name = "properties", location = "query")
String properties);
SDK call:
client.companiesGet("abc123AuthToken", "continueToken1", "20", "prop1");
I am trying to get expiry time or status of subscription to ensure if user is paying regularly for my item or not . When i query using
Purchase monthlySubscription = inv.getPurchase("itemName");
or
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
It returns following data
{
"packageName":"com.abcPackage",
"productId":"auto1week",
"purchaseTime":1453369299644,
"purchaseState":0,
"developerPayload":"PAY_LOAD",
"purchaseToken":"TOKEN",
"autoRenewing":true
}
The problem is , purchaseTime remains same after several weeks which is supposed to be change after every purchase.
I tried google Play developers API
https://developers.google.com/android-publisher/#subscriptions
but i am having a hard time implementing it on my android device .
I will be grateful if someone can guide me step by step process to get this data on android device.
https://developers.google.com/android-publisher/api-ref/purchases/subscriptions
Any help in this regard will be highly appreciated.
Not sure if this will help, but here is my server side code (in java) that connects to the developer API and returns the expiration of the subscription.
I created a Service Account in the Google Developer Console, and followed the somewhat obtuse instructions to create a key file in src/resources/keys.json. APPLICATION_NAME is the package name of my app, and PRODUCT_ID is the subscription ID from the Google PLAY developer console.
Sorry it's not really 'step by step' as you asked for, but I also am doing verification on the server side instead of on the client. I suppose on the client you could do some sort of soft-verification by checking purchaseState == 0 (1=cancelled, 2=refunded), and autoRenewing==true. You may get stuck there if they cancel though, since you are still supposed to provide service through the duration of the subscription.
public static Long doSomeWork(String token){
log.debug("Google Validation: Doing some work:" + token);
try{
// Creating new Trusted Transport
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
// Getting Auth Creds
Credential cred = getAuthCredential();
// Building Android Publisher API call
AndroidPublisher ap = new AndroidPublisher.Builder(httpTransport, JSON_FACTORY, cred)
.setApplicationName(APPLICATION_NAME).build();
// Get Subscription
AndroidPublisher.Purchases.Subscriptions.Get get = ap.purchases().subscriptions().get(
APPLICATION_NAME,
PRODUCT_ID,
token);
SubscriptionPurchase subscription = get.execute();
log.debug(subscription.toPrettyString());
log.debug("DONE (not null)");
return subscription.getExpiryTimeMillis();
} catch(IOException ex) {
ex.printStackTrace();
} catch (GeneralSecurityException ex2) {
ex2.printStackTrace();
}
log.debug("DONE (failure) (0)");
return 0L;
}
private static Credential getAuthCredential(){
log.debug("getAuthCredential");
try{
//Read the credentials from the keys file. This file is obtained from the
// Google Developer Console (not the Play Developer Console
InputStream is = GoogleReceiptValidation.class.getClassLoader().getResourceAsStream("keys.json");
String str = IOUtils.toString(is);
is.close();
JSONObject obj = new JSONObject(str);
InputStream stream = new ByteArrayInputStream(obj.toString().getBytes());
//This is apparently "beta functionality".
GoogleCredential creds = GoogleCredential.fromStream(stream);
creds = creds.createScoped(Collections.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER));
return creds;
} catch (IOException ex){
ex.printStackTrace();
} catch (JSONException ex2){
ex2.printStackTrace();
}
log.debug("No Creds found - returning null");
return null;
}
I have successfully created the api in cloud endpoint and deployed it in app engine. Its in python. also I have generated library and have imported in my project.Its in my import
import com.appspot.corner_fresh.fresh_api.FreshApi;
How can i access methods in my api. I have methods like fresh_api.user.insert and fresh_api.user.detail. I could not get any detailed documentation of python endpoint.
I tried this
final HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = new JacksonFactory();
FreshApi.Builder builder = new FreshApi.Builder( transport, jsonFactory, null );
builder.setApplicationName( appName );
FreshApi service = builder.build();
try {
Json response = service.fresh_api.users.list( ).execute();
Toast.makeText(getApplicationContext(), response.toString(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
Toast.makeText(getApplicationContext(), "sexyfghfd", Toast.LENGTH_SHORT).show();
}
but, first I couldn't understand it. Then, what to pass as appName in builder.setApplicationName( appName );
also, its not recognising methodName in
response = service.fresh_api.users.list( ).execute();
how to execute api and get response. Thanks
For your second question, try to use:
service.fresh_api().users.list( ).execute();
Because fresh_api is not an attribute, but a method. If you use Android Studio, you can press Ctrl + Space and see the list of available method and operations.
For the first question, it may refer to your AppEngine backend application id. But you don't have to set that. The most important is your Client Key for Android, and the Endpoint root URL.
I am tryng to use the Google IO 2013 code sample, specifically the SocialStreamFragment to display google plus post based on a hashtag search.
Steps done:
Integrated SocialStreamFragment.java class and associated clasees, etc, in my project.
In the Console API , I have enabled Google+ API for my project
In the Console API I generated a Simple API Access key for Android Apps, configuring my app's package and debug/prod keys as allowed app.
I copied the Simple Access Key as Config.API_KEY.
However the code below to get the Activities/Posts fails with IOException : 403, "Access Not Configured".
public List<Activity> loadInBackground() {
mIsLoading = true;
// Set up the HTTP transport and JSON factory
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new GsonFactory();
// Set up the main Google+ class
Plus plus = new Plus.Builder(httpTransport, jsonFactory, null)
.setApplicationName(NetUtils.getUserAgent(getContext()))
.setGoogleClientRequestInitializer(
new CommonGoogleClientRequestInitializer(Config.API_KEY))
.build();
ActivityFeed activities = null;
try {
activities = plus.activities().search(mSearchString)
.setPageToken(mNextPageToken)
.setOrderBy("recent")
.setMaxResults(MAX_RESULTS_PER_REQUEST)
.setFields(PLUS_RESULT_FIELDS)
.execute();
mHasError = false;
mNextPageToken = activities.getNextPageToken();
} catch (IOException e) {
e.printStackTrace();
mHasError = true;
mNextPageToken = null;The
}
return (activities != null) ? activities.getItems() : null;
}
What am I missing or doing wrong?
My project has also in API Console configured Client IDs for installed applications. Can this be a problem?
Have you got the Google+ API enabled in the API console?