I am trying to use Firebase Admin on a Servlet that is communicating on App Engine (Google Cloud Platform). I am using a Servlet because I need to generate a Token towards to validate my access to another API that I am using.
Did anyone have this successful history using Firebase on a Servlet?
I am using com.google.firebase:firebase-admin:5.2.0
App Engine SDK
com.google.appengine:appengine-java-sdk:1.9.50
com.google.appengine:appengine-api-1.0-sdk:1.9.54
Inside the GET I am taking the Token to access the Merchant API.
String mToken = gateway.clientToken().generate();
JSONObject jsonObject = new JSONObject();
jsonObject.put("nonce", mToken);
resp.getWriter().println(jsonObject.toString());
Inside the POST Firebase Admin Full Privileges.
All firebase is inside of a single try and the Transaction Request is in another try.
FileInputStream serviceAccount = new FileInputStream("/WEB-INF/...json");
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredential(FirebaseCredentials.fromCertificate(serviceAccount))
.setDatabaseUrl("https://MyProject.firebaseio.com/")
.build();
FirebaseApp.initializeApp(options);
IAM already grant the read permission.
Firebase Database Reference with the Transaction
DatabaseReference ref = FirebaseDatabase
.getInstance()
.getReference("price");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String res = (String) dataSnapshot.getValue();
try {
TransactionRequest request = new TransactionRequest()
.amount(new BigDecimal(res)) //Res doesn't show any error but it also doesn't work
.paymentMethodNonce(nonce)
.options()
.submitForSettlement(true)
.storeInVaultOnSuccess(true)
.done();
Result<Transaction> result = gateway.transaction().sale(request);
Thanks.
Firebase Java Admin SDK often gets used inside servlets. You will need to use a manually scaled instance to deploy on App Engine. Firebase database client starts long running threads, and hence cannot operate on the auto-scaled instances (the new GAE java8 runtime seems more lenient in this regard, but I cannot confirm that right now).
I would recommend you to call FirebaseApp.initializeApp() only once per app, ideally inside a ServletContextListener. Then in your servlet, you should be able to perform your transaction as you do now.
Related
I am trying to implement a custom auth flow using Amazon Cognito and lambda scripts. The custom authentication is triggered but the request session array is empty instead of triggering the SRP_a challenge like it does using the javascript sdk. If the SRP_a challenge does not happen, it does not perform the PASSWORD_VERIFIER challenge which is necessary for the username/password being used to authenticate. Note: the custom portion of the authentication happens after the username/password auth happens.
Cognito's documentation says that this flow is possible on javascript, iOS and Android but only provides examples in javascript. Below I have included the the Android code that is doing the authentication.
CognitoHelper cognitoHelper = CognitoHelper.getInstance(getApplicationContext());
username = loginView.getUsername();
//Must use toLowerCase to make the username not case sensitive
cognitoHelper.setUser(username.toLowerCase());
password = loginView.getPassword();
AuthFlowType authFlowType = AuthFlowType.fromValue(String.valueOf(AuthFlowType.CUSTOM_AUTH));
HashMap<String, String> authenticationParameters = new HashMap<>();
authenticationParameters.put(CognitoServiceConstants.AUTH_PARAM_PASSWORD,password);
authenticationParameters.put(CognitoServiceConstants.AUTH_PARAM_USERNAME, username);
SRP_aHelper srp_aHelper = new SRP_aHelper(cognitoHelper.getUserPoolID());
authenticationParameters.put(CognitoServiceConstants.AUTH_PARAM_SRP_A,srp_aHelper.getA().toString(16));
InitiateAuthRequest initiateAuthRequest = new InitiateAuthRequest();
initiateAuthRequest.setClientId(cognitoHelper.getClientID());
initiateAuthRequest.setAuthFlow(authFlowType);
initiateAuthRequest.setAuthParameters(authenticationParameters);
CognitoUser cognitoUser = cognitoHelper.getUserPool().getUser(username);
thisDevice = cognitoUser.thisDevice();
//getCIPClient returns a AmazonCognitoIdentityProviderClient
InitiateAuthResult initiateAuthResult = cognitoHelper.getCipClient().initiateAuth(initiateAuthRequest);
I have written this code but I'm getting an error. How can I get to work?
But the same token works with postman.
Error:
{"message":"The security token included in the request is invalid."}
Code :
public class test extends AppCompatActivity {
private final AWS4Signer signer = new AWS4Signer();
Request<?> aws;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
AWSCredentials credentials = new BasicAWSCredentials("AccessKey", "SecretKey");
aws = generateBasicRequest();
signer.setServiceName("execute-api");
signer.sign(aws, credentials);
new get_aws().execute();
}
private Request<?> generateBasicRequest() {
Request<?> request = new DefaultRequest<Void>("execute-api");
request.addHeader("Content-type", "application/json");
String securityToken = "Session Token";
request.addHeader("X-Amz-Security-Token", securityToken);
request.addHeader("Host", "********.amazonaws.com");
request.addHeader("x-amz-archive-description", "test test");
request.setResourcePath("/");
request.setEndpoint(URI.create("https://******.execute-api.****.amazonaws.com/data/all"));
return request;
}
private class get_aws extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
BufferedReader in = null;
String data = null;
try {
HttpClient httpclient = new DefaultHttpClient();
HttpGet request = new HttpGet();
request.addHeader("Authorization", aws.getHeaders().get("Authorization"));
request.addHeader("X-Amz-Date",request_aws.getHeaders().get("X-Amz-Date"));
request.addHeader("Content-Type","application/x-www-form-urlencoded");
URI website = new URI("https://********.execute-api.*******.amazonaws.com/data/all");
request.setURI(website);
HttpResponse response = httpclient.execute(request);
in = new BufferedReader(new InputStreamReader(
response.getEntity().getContent()));
String line = in.readLine();
Log.d("line", line);
} catch (Exception e) {
Log.e("log_tag", "Error in http connection " + e.toString());
}
return null;
}
#Override
protected void onPostExecute(Void result) {
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
}
To answer your immediate question, AWS can generate a Java SDK from your API Gateway for you.
Using the generated SDK, you can then pass an AWSCredentialsProvider object into your SDK.
AWSCredentials credentials = new BasicAWSCredentials("AccessKey", "SecretKey");
ApiClientFactory factory = new ApiClientFactory()
.credentialsProvider(credentials);
But...
You should never ship IAM access keys in a shipped application. These credentials can be retrieved by anyone who has installed your application by opening the .apk file.
Those credentials can then be used to access any other AWS actions the associated IAM User has access to in your account. This means anyone with access to the application apk (ie: anyone who can download the app from the app store) has access to your AWS account.
Depending what problem you're trying to solve will dictate the correct solution to the problem.
My Lambda needs an IAM Role to run
This is a fairly common mistake to make with API gateway when people see the "Invoke with caller credentials" option from API Gateway.
Uncheck this box and the Lambda will run with the IAM Role you defined in Lambda.
If requests fail after doing this, you need to make sure API Gateway has permission to invoke your lambda.
Restrict API to the application itself without users
Your application can't keep a secret and you have no user credentials.
You should disable Authorization completely, this is effectively a public API.
Requiring an API Key (and usage plan) to rate limit your API can be useful, but keep in mind this is not a security measure as, again - your application can't keep that key secret.
You want users to log in first (no existing source of users)
This makes sense if your API call is only designed to be called by registered users.
You'll need to configure Cognito User Pools for this. This shouldn't be confused with Cognito Federated Identities - which focuses on a different part of the problem. You can use it to solve this, but trust me - you'll be happier if you don't go down that path.
To get cracking you'll need to take a few steps:
Create a User Pool (detailed settings explained here).
Configure a Cognito Authorizer on your API Gateway.
Create an App Client for your pool. Don't generate a client secret key when you do this.
Integrate with your Android application. There's a prebuilt Android example available from AWS for getting the client side going: AmazonCognitoYourUserPoolsDemo
You want users to log in first (existing source of users)
If you can use SAML or OAuth2.0 / OpenID Connect to authenticate your users, follow the instructions and then configure federation.
If not, this is possibly the time to consider Cognito Federated Identities, specifically using the Developer Authenticated Identities process. But again, I'd really recommend against it.
API Gateway & Cognito is a massive topic. Hopefully the instructions provided are a great entry point to the relevant parts of the documentation.
Have you tried to look at the examples from AWS https://github.com/awslabs/aws-sdk-android-samples with cognito credentials, I found them easier to use, in case you want to use your AccessKey and SecretKey, you can also use something like this
AWSCredentials credentials = new BasicAWSCredentials("AccessKey", "SecretKey");
AmazonS3Client sS3Client = new AmazonS3Client(credentials,Region.getRegion("Region"));
I have been doing extensive research on how to authenticate your client (Android, iOS, web-app) with Cloud Endpoints without requiring your user to use their Google account login the way the documentation shows you.
The reason for this is that I want to secure my API or "lock it down" to only my specified clients. Sometimes I will have an app that does not have a user login. I would hate to pester my user to now sign in just so my API is secure. Or other times, I just want to manage my own users like on a website and not use Google+, Facebook, or whatever else login authentication.
To start, let me first show the way you can authenticate your Android app with your Cloud Endpoints API using the Google Accounts login as specified in the documentation. After that I will show you my findings and a potential area for a solution which I need help with.
(1) Specify the client IDs (clientIds) of apps authorized to make requests to your API backend and (2) add a User parameter to all exposed methods to be protected by authorization.
public class Constants {
public static final String WEB_CLIENT_ID = "1-web-apps.apps.googleusercontent.com";
public static final String ANDROID_CLIENT_ID = "2-android-apps.googleusercontent.com";
public static final String IOS_CLIENT_ID = "3-ios-apps.googleusercontent.com";
public static final String ANDROID_AUDIENCE = WEB_CLIENT_ID;
public static final String EMAIL_SCOPE = "https://www.googleapis.com/auth/userinfo.email";
}
import com.google.api.server.spi.auth.common.User; //import for the User object
#Api(name = "myApi", version = "v1",
namespace = #ApiNamespace(ownerDomain = "${endpointOwnerDomain}",
ownerName = "${endpointOwnerDomain}",
packagePath="${endpointPackagePath}"),
scopes = {Constants.EMAIL_SCOPE},
clientIds = {Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID,
Constants.IOS_CLIENT_ID,
Constants.API_EXPLORER_CLIENT_ID},
audiences = {Constants.ANDROID_AUDIENCE})
public class MyEndpoint {
/** A simple endpoint method that takes a name and says Hi back */
#ApiMethod(name = "sayHi")
public MyBean sayHi(#Named("name") String name, User user) throws UnauthorizedException {
if (user == null) throw new UnauthorizedException("User is Not Valid");
MyBean response = new MyBean();
response.setData("Hi, " + name);
return response;
}
}
(3) In Android call the API method in an Asynctask making sure to pass in the credential variable in the Builder:
class EndpointsAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
private static MyApi myApiService = null;
private Context context;
#Override
protected String doInBackground(Pair<Context, String>... params) {
credential = GoogleAccountCredential.usingAudience(this,
"server:client_id:1-web-app.apps.googleusercontent.com");
credential.setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null));
if(myApiService == null) { // Only do this once
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), credential)
// options for running against local devappserver
// - 10.0.2.2 is localhost's IP address in Android emulator
// - turn off compression when running against local devappserver
.setRootUrl("http://<your-app-engine-project-id-here>/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
// end options for devappserver
myApiService = builder.build();
}
context = params[0].first;
String name = params[0].second;
try {
return myApiService.sayHi(name).execute().getData();
} catch (IOException e) {
return e.getMessage();
}
}
#Override
protected void onPostExecute(String result) {
Toast.makeText(context, result, Toast.LENGTH_LONG).show();
}
}
What is happening is that in your Android app you are showing the Google account picker first, storing that Google account email in you shared preferences, and then later setting it as part of the GoogleAccountCredential object (more info on how to do that here).
The Google App Engine server receives your request and checks it. If the Android Client is one of the ones you specified in the #Api notation, then the server will inject the com.google.api.server.spi.auth.common.User object into your API method. It is now your responsibility to check if that User object is null or not inside your API method. If the User object is null, you should throw an exception in your method to prevent it from running. If you do not do this check, your API method will execute (a no-no if you are trying to restrict access to it).
You can get your ANDROID_CLIENT_ID by going to your Google Developers Console. There, you provide the package name of your Android App and the SHA1 which generates for you an android client id for you to use in your #Api annotation (or put it in a class Constants like specified above for usability).
I have done some extensive testing with all of the above and here is what I found:
If you specify a bogus or invalid Android clientId in your #Api annotation, the User object will be null in your API method. If you are doing a check for if (user == null) throw new UnauthorizedException("User is Not Valid"); then your API method will not run.
This is surprising because it appears there is some behind the scenes validation going on in Cloud Endpoints that check whether the Android ClientId is valid or not. If it is invalid, it won't return the User object - even if the end user logged in to their Google account and the GoogleAccountCredential was valid.
My question is, does anyone know how I can check for that type of ClientId validation on my own in my Cloud Endpoints methods? Could that information be passed around in an HttpHeader for example?
Another injected type in Cloud Endpoints is the javax.servlet.http.HttpServletRequest. You can get the request like this in your API method:
#ApiMethod(name = "sayHi")
public MyBean sayHi(#Named("name") String name, HttpServletRequest req) throws UnauthorizedException {
String Auth = req.getHeader("Authorization");//always null based on my tests
MyBean response = new MyBean();
response.setData("Hi, " + name);
return response;
}
}
But I am not sure if the necessary information is there or how to get it.
Certainly somewhere there must be some data that tells us if the Client is an authorized and specified one in the #Api clientIds.
This way, you could lock-down your API to your Android app (and potentially other clients) without ever having to pester your end users to log in (or just create your own simple username + password login).
For all of this to work though, you would have to pass in null in the third argument of your Builder like this:
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
Then in your API method extract whether or not the call came from an authenticated client, and either throw an exception or run whatever code you wanted to.
I know this is possible because when using a GoogleAccountCredential in the Builder, somehow Cloud Endpoints knows whether or not the call came from an authenticated client and then either injects its User object into the API method or not based on that.
Could that information be in the header or body somehow? If so, how can I get it out to later check if it is there or not in my API method?
Note: I read the other posts on this topic. They offer ways to pass in your own authentication token - which is fine - but your .apk will still not be secure if someone decompiles it. I think if my hypothesis works, you will be able to lock-down your Cloud Endpoints API to a client without any logins.
Custom Authentication for Google Cloud Endpoints (instead of OAuth2)
Authenticate my "app" to Google Cloud Endpoints not a "user"
Google Cloud Endpoints without Google Accounts
EDIT:
We used Gold Support for the Google Cloud Platform and have been talking back and forth with their support team for weeks. This is their final answer for us:
"Unfortunately, I haven't had any luck on this. I've asked around my
team, and checked all of the documentation. It looks like using OAuth2
is your only option. The reason is because the endpoint servers handle
the authentication before it reaches your app. This means you wouldn't
be able to develop your own authentication flow, and would get results
much like what you were seeing with the tokens.
I would be happy to submit a feature request for you. If you could
provide a little more information about why the OAuth2 flow doesn't
work for your customers, I can put the rest of the information
together and submit it to the product manager."
(frowny face) - however, maybe it is still possible?
I have implemented Endpoint Auth using a custom header "Authorization" and it works just fine. In my case this token is set after login but should work all the same with your app. Check your tests because the value should be there.
The way to retrieve that header is indeed:
String Auth = req.getHeader("Authorization");
You could take it a step further and define your own implementations of an Authenticator and apply it to your secure API calls.
So you don't have any user specific info, but just want to ensure that only your app is able to communicate with your backend...
This is what i think,
change
#Api(name = "myApi", version = "v1",
namespace = #ApiNamespace(ownerDomain = "${endpointOwnerDomain}",
ownerName = "${endpointOwnerDomain}",
packagePath="${endpointPackagePath}"),
scopes = {Constants.EMAIL_SCOPE},
clientIds = {Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID,
Constants.IOS_CLIENT_ID,
Constants.API_EXPLORER_CLIENT_ID},
audiences = {Constants.ANDROID_AUDIENCE})
{
...
}
to
#Api(name = "myApi", version = "v1",
namespace = #ApiNamespace(ownerDomain = "${endpointOwnerDomain}",
ownerName = "${endpointOwnerDomain}",
packagePath="${endpointPackagePath}"),
scopes = {Constants.EMAIL_SCOPE},
clientIds = {Constants.ANDROID_CLIENT_ID},
audiences = {Constants.ANDROID_AUDIENCE})
{
...
}
The Client ID is generated from the signature of your app. It can't be replicated. If you only allow your endpoints to accept requests from the Android App, your problem would be solved.
Tell me if this works.
Faced the same problem to find a solution to call my API safely from my endpoints, without using Google Account. We can't decompile an IOS App (Bundle), but decompile an Android App is so simple..
The solution I found is not perfect but do the job pretty good:
On android APP, I just create an constant String variable, named APIKey, with simply content (For example "helloworld145698")
Then I encrypt it with sha1, next md5, and finally sha1 (Order and frequency of encryption up to you) and store the variable on SharedPref (For Android) in private mode (Do this action on an random class in your App) It's this result encrypted I authorize on my Backend !
On my backend, I just add a parameter (named token for exemple) on every request
Example:
#ApiMethod(name = "sayHi")
public void sayHi(#Named("name") String name, #Named("Token") String token) {
if (token == tokenStoreOnAPIServer) {
//Allow it
} else {
//Refuse it and print error
}
}
On android, active ProGuard for obfuscated your code. It will be really unreadable for anyone who tried to decompile your app (Reverse engineering is really hardcore)
Not THE perfect secure solution, but it works, and it will be really really (really) difficult to find the real API key for anyone who try to read your code after decompilation.
Authentication and app engine, there is a lot to be read about it, but a lot seems to be outdated!
Even the google pages https://developers.google.com/appengine/docs/java/endpoints/consume_android#making-authenticated-calls
Here, they talk about 'GoogleAccountCredential.usingAudience', but nowadays, you should use GoogleAuthUtil (as far as I know, please correct me if I'm wrong).
I am trying to set up an app engine as a backend to my Android app (and in future, my iOS app).
I am using Android Studio, used the 'new module' and chose app engine with cloud messaging there.
I created a simple endpoint, and have a function there, here is some code:
public class ReviewEndpoint {
// Make sure to add this endpoint to your web.xml file if this is a web application.
private static final Logger LOG = Logger.getLogger(ReviewEndpoint.class.getName());
/**
* This method gets the <code>Review</code> object associated with the specified <code>id</code>.
* #param id The id of the object to be returned.
* #return The <code>Review</code> associated with <code>id</code>.
*/
#ApiMethod(name = "getReview")
public Review getReview(#Named("id") Long id) {
// Implement this function
Review r = new Review();
r.setData("test!");
As you can see, this is nicely generated by Android Studio. I implemented some stuf like creating the 'review' object and return it at the end.
On the Android side, I can do this:
ReviewEndpoint.Builder b = new ReviewEndpoint.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null);
ReviewEndpoint ep = b.build();
Review review = ep.getReview(1L).execute();
data = review.getData();
and yes, I get 'test!' :)
Now, I want to have this authenticated. I want to know which user wrote what, so I thought I am going to use GMail account and Facebook later.
Here I'm stuck. I am able to get a token from the user on Android:
token = GoogleAuthUtil.getToken(MainScreenActivity.this, mAccount.name, "oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.profile");
then you are able to add this token as credential to the request:
Credential cr = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(token);
ReviewEndpoint.Builder b = new ReviewEndpoint.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), cr);
Then in the app engine I tried to get the user info, but how?
Will it be supplied as 'bearer'? How do I get this bearer token? Should I then do API request to get the data on the server?
this does not work:
OAuthService service = OAuthServiceFactory.getOAuthService();
try {
User user = service.getCurrentUser();
can anyone give me a heads up?
So finally, today, I found out how to do it! I had questions on Stackoverflow on this before and never had an answer, but these to sites gave me the answer:
https://developers.google.com/appengine/docs/java/endpoints/auth
https://developers.google.com/appengine/docs/java/endpoints/consume_android
The first shows what needs to be done on the app engine side. The second page will tell you how to get the credentials. I was quite close. I am not sure if the adjusting of the build.gradle file mentioned in the second link is necessary. What I added to the App Engine:
#Api(name = "reviewEndpoint", version = "v1", ...<<some more stuff here >>
scopes = {Constants.EMAIL_SCOPE},
clientIds = {Constants.WEB_CLIENT_ID, Constants.ANDROID_CLIENT_ID},
audiences = {Constants.ANDROID_AUDIENCE})
and then get the credentials:
// Initialize the scope using the client ID you got from the Console.
final String scope = "server:client_id:" + Constants.WEB_CLIENT_ID;
credential = GoogleAccountCredential.usingAudience(activity,scope);
You have to add the e-mail address of the user:
credential.setSelectedAccountName("some-mail-address#gmail.com");
you can get the e-mail address using the account picker (also example shown when you follow the link)
and next. you do a call to the endpoint, using the credential, I think Play Services will validate the user, because if I use an e-mail that is not logged in on the device, it will not work. The following code will throw an GoogleAuthIOException :
ReviewEndpoint.Builder b = new ReviewEndpoint.Builder(
AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), id_token);
ReviewEndpoint ep = b.build();
Review review;
review = ep.getReview(1L).execute();
for testing, I've put the e-mail address I get at the server side as a string in the review object, and there it gave me the e-mail address instead of the user object being null. Ow! I forgot to tell you, you need a user argument on the app engine side. Even though you do not see the 'user' argument in the 'getReview' call above, it will be added by App Engine.
So this is how my getReview looks now:
#ApiMethod(name = "getReview")
public Review getReview(#Named("id") Long id, User user) {
// Implement this function
Review r = new Review();
r.setData("user == " + (user == null ? "NULL " : user.toString()));
Hope this will help someone
in a nutshell:
Can I work with the Google Play Android Developer API from server-side without providing any app in the play store?
Background:
I'm working on a project which provides apps with monthly subscriptions. The coresponding data of each subscription (purchase token, date etc) gets stored in the backend database.
Now I want to create a cronjob that iterates through each of these datasets.And for each subscription I'd like to contact the Google API to retrieve the information if the subscription is still valid or not, and update our database corresponding to the responsed status.
For the backend logic I use the google-api-java-client library.
To either cancel or verify subscriptions I need to authenticate myself with OAuth2 before.
Been there, done that.
new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes("https://www.googleapis.com/auth/androidpublisher") // $1
.setServiceAccountPrivateKeyFromP12File(new File(filePath))
.setClientSecrets(CLIENT_ID, CLIENT_SECRET) // $2
.build();
$1: I don't know if the given account scope is valid. Because I just could find this value in a very few examples, but neither in this overview nor in the google playground
$2 I guess this is necessary, even though I found a lot of example which did not provide this information.
But, unfortunately, I can't see any differences when I provide invalid data (like wrong email or private key).
Questions
How can i verify that the GoogleCredential is correct?
May I just see it in the next steps, like contacting ie the androidpublisher API?
In the next step I try to get purchase status of a subscription:
Androidpublisher publisher = new Androidpublisher.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName(GOOGLE_PRODUCT_NAME) // $1
.build();
Androidpublisher.Purchases purchases = publisher.purchases();
Androidpublisher.Purchases.Get get = purchases.get("android.test.purchased", "monthly001", "mytoken"); // $2
SubscriptionPurchase subscripcion = get.execute();
$1: My dummy product name from the API console -> API Access
$2: Beside the fact, that the androidpush API does not allow contacting it via service accounts, but only via web server applications auth flow, I don't have any clue what to insert in the parameter of the get- method.
Here's the API:
https://developers.google.com/android-publisher/v1/purchases/get
Questions
What is the package name and what is the subscriptionId in this context?
Where do I get/set these values?
After reading this document I know there is a way to to deal with fake/static responses. But I can't read anywhere if this is also possible for subscriptions, or just for in-app-billings on mobile devices only.
I'm wondering anyway why/if there is any easy way of developing with a sandbox or s.th. simliar.
I still have the feeling that I'm just missing a big part to understand how the things should work.
Maybe someone of you can give me a hint how to proceed at this place or may say me where i'm wrong.
Kind regards,
Christopher
I could now figure out most of my previous understanding problems.
=1= GENERATE AUTHORIZATION URL
String authorizeUrl = new GoogleAuthorizationCodeRequestUrl(googleClientId,callbackUrl,"https://www.googleapis.com/auth/androidpublisher").build()
// See why: http://stackoverflow.com/questions/8433990/when-authenticating-with-oauth-and-youtube-always-get-error-invalid-grant-on
authorizeUrl += "&approval_prompt=force&access_type=offline"
=2= AUTHENTICATE
Since the server-webflow is not working for the androidpublisher API the customer must now call the URL generated in (1) manually.
=3= CALLBACK
The google callback should process the next steps. The callback contains the parameter "code" which we have to use.
=4= REQUEST AUTH-TOKEN
// Build the HTTP parameter
Map<String,String> params = [:]
params.put("grant_type", "authorization_code")
params.put("code", code.encodeAsURL())
params.put("client_id", customer.googleClientId.encodeAsURL())
params.put("client_secret", customer.googleClientSecret.encodeAsURL())
params.put("redirect_uri", getCallbackUrl().encodeAsURL())
// Send the POST request
// This action might throw an exception in case any parameter were wrong, invalid or not specified.
String result = HttpRequestHandler.sendRequest("https://accounts.google.com/o/oauth2/token", params);
JSONElement jsonResult = JSON.parse(result)
// Map result
OAuth2Result oAuth2Result = new OAuth2Result()
oAuth2Result.accessToken = jsonResult.getAt("access_token")
oAuth2Result.refreshToken = jsonResult.getAt("refresh_token")
oAuth2Result.ttlSeconds = Integer.parseInt(jsonResult.getAt("expires_in").toString())
oAuth2Result.tokenType = jsonResult.getAt("token_type")
=5= REQUEST REFRESH TOKEN
// Build the HTTP parameter
Map<String,String> params = [:]
params.put("grant_type", "refresh_token")
params.put("refresh_token", this.customer.googleRefreshToken.encodeAsURL())
params.put("client_id", customer.googleClientId.encodeAsURL())
params.put("client_secret", customer.googleClientSecret.encodeAsURL())
// Send the POST request
// This action might throw an exception in case any parameter were wrong, invalid or not specified.
String result = HttpRequestHandler.sendRequest("https://accounts.google.com/o/oauth2/token", params);
JSONElement jsonResult = JSON.parse(result)
// Map result
OAuth2Result oAuth2Result = new OAuth2Result()
oAuth2Result.accessToken = jsonResult.getAt("access_token")
oAuth2Result.refreshToken = jsonResult.getAt("refresh_token")
oAuth2Result.ttlSeconds = Integer.parseInt(jsonResult.getAt("expires_in").toString())
oAuth2Result.tokenType = jsonResult.getAt("token_type")