set DeviceToken in parse for Android - android

i can't set the device token in Installation table using parse
im using
ParseInstallation installation =ParseInstallation.getCurrentInstallation();
installation.put("GCMSenderId",senderId);
installation.put("pushType","gcm");
installation.put("deviceToken",token);
but when i try to use save i got an exception.
Cannot modify `deviceToken` property of an _Installation object. android
The problem is, the backend use this tokenId to send the push notification for another provider (OneSignal), so im wondering if its any way to write in the deviceToken row (As far as i know this is for iOS only).
i need write in the deviceToke the GCM token i receive.
Thanks

I solved this by adding a Parse Cloud function called setDeviceToken. This way I can use the MasterKey to modify the Installation record.
Parse.Cloud.define("setDeviceToken", function(request, response) {
var installationId = request.params.installationId;
var deviceToken = request.params.deviceToken;
var query = new Parse.Query(Parse.Installation);
query.get(installationId, {useMasterKey: true}).then(function(installation) {
console.log(installation);
installation.set("deviceToken", deviceToken);
installation.save(null, {useMasterKey: true}).then(function() {
response.success(true);
}, function(error) {
response.error(error);
})
}, function (error) {
console.log(error);
})
});
Then, in my Android app's Application.onCreate:
OneSignal.idsAvailable(new OneSignal.IdsAvailableHandler() {
#Override
public void idsAvailable(String userId, String registrationId) {
final ParseInstallation currentInstallation = ParseInstallation.getCurrentInstallation();
if (registrationId != null) {
HashMap<String, Object> params = new HashMap<>(2);
params.put("installationId", currentInstallation.getObjectId());
params.put("deviceToken", registrationId);
ParseCloud.callFunctionInBackground("setDeviceToken", params, new FunctionCallback<Boolean>() {
#Override
public void done(java.lang.Boolean success, ParseException e) {
if (e != null) {
// logException(e);
}
}
});
}
}
});

For future visitors, I solved this a different way, by using the Parse Rest API: http://parseplatform.org/docs/rest/guide/
Using that, you can bypass the regular SDK limitations. Just make a put request to https://yourApp/parse/installtions/{installationObjectIDHere}. make a dictionary and encode it as JSON:
Map<String, String> dictionary = new HashMap<String, String>();
dictionary.put("deviceToken", yourToken);
JSONObject jsonDictionary = new JSONObject(dictionary);
String bodyAsString = jsonDictionary.toString();
RequestBody body = RequestBody.create(JSON, bodyAsString);
then send the request, and that should work.

joey showed the right way to do that, the extra part i added is about the authentication header.
This is what i did:
val JSON = MediaType.parse("application/json; charset=utf-8")
val client = OkHttpClient()
val installationObjectId = ParseInstallation.getCurrentInstallation().objectId
val myAppId = getString(R.string.parse_app_id)
val key = getString(R.string.parse_client_key)
val server = getString(R.string.parse_server_url)
val mRequestUrl = "$server/installations/$installationObjectId"
val dictionary = HashMap<String, String?>()
dictionary["deviceToken"] = myToken
val jsonDictionary = JSONObject(dictionary)
val bodyAsString = jsonDictionary.toString()
val body = RequestBody.create(JSON, bodyAsString)
val request = Request.Builder()
.url(mRequestUrl)
.put(body)
.addHeader("X-Parse-Application-Id", myAppId)
.addHeader("X-Parse-Client-Key", key)
.build()
client.newCall(request).execute()

Related

403 Forbidden. The request signature we calculated does not match signature you provided. Check your key and signing method from Android to Amazon S3

I am trying to call get api using an AWS signing method but not able to get the response.
Below is my code.
val secretkey = "E+t5/nDf6/NKNJBjbsdjv"
val accesskey = "DJKSBDKSBNKFGNBFG"
val credentials: AWSCredentials = BasicAWSCredentials(accesskey, secretkey)
val API_GATEWAY_SERVICE_NAME = "s3"
val requestAws: Request<*> = DefaultRequest<Any?>(API_GATEWAY_SERVICE_NAME)
val uri = URI.create("https://s3.us-west-2.amazonaws.com/..../../sample")
requestAws.endpoint = uri
requestAws.resourcePath = "https://s3.us-west-2.amazonaws.com/..../../sample"
requestAws.httpMethod = HttpMethodName.GET
val signer = AWS4Signer() signer . setServiceName (API_GATEWAY_SERVICE_NAME)
signer.setRegionName("us-west-2")
signer.sign(requestAws, credentials)
val headers = requestAws.headers
val key: MutableList<String> = ArrayList()
val value: MutableList<String> = ArrayList()
for ((key1, value1) in headers)
{
key.add(key1) value . add (value1)
}
val httpClient = OkHttpClient()
val request: okhttp3.Request = okhttp3.Request.Builder()
.url("https://s3.us-west-2.amazonaws.com/..../../sample")
.addHeader(key[0], value[0])
.addHeader(key[1], value[1])
.addHeader(key[2], value[2])
.addHeader("X-Amz-Content-Sha256",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
.build()
val response: okhttp3.Response = httpClient.newCall(request).execute()
Log.i("LOG", response.body.toString())
Not able to figure out, what I am doing mistake.
Please help me out with this issue.
If you want to create an Android app written in Kotlin and invokes AWS Services, use the AWS SDK for Kotlin.
This SDK has strongly typed Service Clients that you can use in an Android Studio project that lets you invoke a given service. (as opposed to using okhttp3.Request, etc)
For example, here is Kotlin code that invoke SNS using a Strongly typed service client named SnsClient.
// Get all subscriptions.
fun getSubs(view: View) = runBlocking {
val subList = mutableListOf<String>()
val snsClient: SnsClient = getClient()
try {
val request = ListSubscriptionsByTopicRequest {
topicArn = topicArnVal
}
val response = snsClient.listSubscriptionsByTopic(request)
response.subscriptions?.forEach { sub ->
subList.add(sub.endpoint.toString())
}
val listString = java.lang.String.join(", ", subList)
showToast(listString)
} catch (e: SnsException) {
println(e.message)
snsClient.close()
}
}
fun getClient() : SnsClient{
val staticCredentials = StaticCredentialsProvider {
accessKeyId = "<Enter key>"
secretAccessKey = "<Enter key>"
}
val snsClient = SnsClient{
region = "us-west-2"
credentialsProvider = staticCredentials
}
return snsClient
}
TO learn how to use the AWS SDK for Kotlin, see
AWS SDK for Kotlin Developer Guide

android retrofit post request request body as raw JSON

This is my first time using the retrofit in android (Java) and i don't know how to do post api
in postman i use Request Body as raw JSON
{
"description": "aaaaaa",
"reportedpriority_description": "Elevé",
"reportedby": "zz",
"assetnum": "111",
"affectedperson": "ahmed"
}
someone can help me with example of code? it return empty response body
void requestcall(){
try
{
HashMap respMap = new HashMap<>();
respMap.put("Key1", "Value1");
respMap.put("Key2", "Value2");
respMap.put("Key3", "Value3");
String resultString = convertToJson(respMap);
sendToServer(resultString);
}
catch (Exception e)
{ }
}
public static String convertToJson(HashMap<String, String> respMap)
{
JSONObject respJson = new JSONObject();
JSONObject respDetsJson = new JSONObject();
Iterator mapIt = respMap.entrySet().iterator();
try
{
while (mapIt.hasNext()) {
HashMap.Entry<String, String> entry = (HashMap.Entry) mapIt.next();
respDetsJson.put(entry.getKey(), entry.getValue());
}
//change according to your response
respJson.put("RESPONSE", respDetsJson);
}
catch (JSONException e)
{
return "";
}
return respJson.toString();
}
There are two ways you can send a body for Post methods, you can either create a JSON string in the format you want. Or you can simply create a model class with the same field names as your JSON keys, and then initialize values to them while sending a request.
Model class:
class Example {
var description: String? = null
var reportedpriority_description: String? = null
var reportedby: String? = null
var assetnum: String? = null
var affectedperson: String? = null
}
Init values:
val obj = Example()
obj.description = "aaaaaa"
// and so on
You can then pass the object to your POST method.
You can refer to documentations to learn how to code the API methods.
Hope this works for you!

Invalid request missing grant type

I'm doing a post call to my OAUTH2 authentication with Android, trying to recibe my Bearer Token.
I'm using Kotlin language and i'm doing the post request with Volley.
The problem is that when i do my post request with Postman, it work's perfect, but when i do it in the same way using Volley post, my API REST yells: Invalid Request Exception, missing grant type.
And my android yells: Unexpected response code 400 for http://192.168.1.254:8081/oauth/token
Android call:
private fun loginUser() {
var grant_type = "password"
var username = etUsername.text.toString()
var password = etPassword.text.toString()
val credentials = "angularapp"+":"+"12345"
// Post parameters
// Form fields and values
val params = HashMap<String,String>()
params["grant_type"] = grant_type
params["username"] = username
params["password"] = password
val jsonObject = JSONObject(params)
val request = CustomJsonObjectRequestBasicAuth(Request.Method.POST, Network.API_URL_LOGIN,jsonObject,
Response.Listener{ response->
Log.d("RESPONSEEEE", response.toString())
try {
// Parse the json object here
Log.d("Response" ,response.toString())
val intent = Intent(this, PatientsActivity::class.java)
intent.putExtra(Tags.FLOOR.toString(), ((spiFloor?.selectedItemId!!+1)))
startActivity(intent)
}catch (e:Exception){
e.printStackTrace()
}
}, Response.ErrorListener{
Log.d("ERROR", "VOLLEY ERROR")
},credentials
)
// Add the volley request to request queue
VolleySingleton.getInstance(this).addToRequestQueue(request)
}
// Class to make a volley json object request with basic authentication
class CustomJsonObjectRequestBasicAuth(
method:Int, url: String,
jsonObject: JSONObject?,
listener: Response.Listener<JSONObject>,
errorListener: Response.ErrorListener,
credentials:String
)
: JsonObjectRequest(method,url, jsonObject, listener, errorListener) {
private var mCredentials:String = credentials
#Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val headers = HashMap<String, String>()
headers["Content-Type"] = "application/x-www-form-urlencoded"
val auth = "Basic " + Base64.encodeToString(mCredentials.toByteArray(), Base64.NO_WRAP)
headers["Authorization"] = auth
System.out.println(headers.toString())
return headers
}
}

How to POST multipart/form-data using Fuel for Kotlin?

I need to send a POST request to a server. I'm supposed to pass some parameters and an image. I am able to do this from Postman, but I can't do this on my Android app (latest SDK) using Fuel.
This is the code I'm using:
val formData = listOf("name" to "name")
val (_, _, result) = Fuel.upload("http://10.0.2.2:3000/test", parameters = formData)
.source { request, url -> imageFile } // type is File
.responseObject<CustomResponse>()
This is the postman screenshot:
I don't have access to the backend code, just some logs. It seems the request body is empty and the file also doesn't get uploaded. How can I do this? I'm at a loss.
I also tried passing the parameters as a jsonBody, this does submit the body, but the content type is not multipart/form-data and the image is still missing.
This JS code works:
let formData = new FormData();
formData.append('name', 'name');
formData.append('image', this.file);
axios.post(`${API_URL}/test`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(console.log).catch(console.log)
Edit: I also tried passing the file as a DataPart, still nothing.
After some struggle I found out what would work:
val file = FileDataPart.from("path_to_your_file", name = "image")
val (_, _, result) = Fuel.upload("http://10.0.2.2:3000/test")
.add(file)
.responseObject<CustomResponse>()
I didn't need name-name part in my case but I would try to add InlineDataPart
After some try finally I found the solution.
Try this I think it will help.
val params = listOf("email" to "test#email.com", "pass" to "123456")
Fuel.upload("http://your-api-url.com/login", Method.POST, params)
.responseString { _, _, result ->
when (result) {
is Result.Failure -> {
print(result.getException().toString())
}
is Result.Success -> {
val data = result.get()
print(data)
}
}
}
I managed to send a POST request to a server. I passed a parameter and an image.
//Prepare POST body
val postBody = listOf("name" to "name")
//Call the API.
val (_, _, result) = Fuel.upload("http://10.0.2.2:3000/test", Method.POST , postBody)
.add(BlobDataPart(myInputStream, name = "image", filename = "default.jpg", contentType = "image/jpeg"))
.responseString()
//If failed, then print exception. If successful, then print result.
when (result) {
is Result.Failure -> {
println(result.getException())
}
is Result.Success -> {
println(result.get())
}
}
You can read the related documentation here.

How to get access token after user is signed in from Gmail in Android?

I am following Google Sign in for Android. Now I can get the idToken but my back end server that I have used earlier is expecting access Token as I was using Google+ Login earlier. Now I don't want to alter my server side. But still how can I use Google Sign in and get the access Token in my android app so that I can validate my user to my back end server.
I was using GooglePlay Service 7.5.0 previously and now I am using GooglePlay Service latest 8.3.0.
For your requirements, you can use the following code:
Firstly, make sure you have a valid Web OAuth 2.0 Client ID:
<!-- Server Client ID. This should be a valid Web OAuth 2.0 Client ID obtained
from https://console.developers.google.com/ -->
<string name="server_client_id">...e4p8.apps.googleusercontent.com</string>
Then inside Activity class:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
// For sample only: make sure there is a valid server client ID.
validateServerClientID();
// [START configure_signin]
// Configure sign-in to request offline access to the user's ID, basic
// profile, and Google Drive. The first time you request a code you will
// be able to exchange it for an access token and refresh token, which
// you should store. In subsequent calls, the code will only result in
// an access token. By asking for profile access (through
// DEFAULT_SIGN_IN) you will also get an ID Token as a result of the
// code exchange.
String serverClientId = getString(R.string.server_client_id);
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(new Scope(Scopes.DRIVE_APPFOLDER))
.requestServerAuthCode(serverClientId)
.requestEmail()
.build();
// [END configure_signin]
// Build GoogleAPIClient with the Google Sign-In API and the above options.
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
}
private void getAuthCode() {
// Start the retrieval process for a server auth code. If requested, ask for a refresh
// token. Otherwise, only get an access token if a refresh token has been previously
// retrieved. Getting a new access token for an existing grant does not require
// user consent.
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
startActivityForResult(signInIntent, RC_GET_AUTH_CODE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_GET_AUTH_CODE) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
Log.d(TAG, "onActivityResult:GET_AUTH_CODE:success:" + result.getStatus().isSuccess());
if (result.isSuccess()) {
// [START get_auth_code]
GoogleSignInAccount acct = result.getSignInAccount();
String authCode = acct.getServerAuthCode();
// Show signed-in UI.
mAuthCodeTextView.setText(getString(R.string.auth_code_fmt, authCode));
updateUI(true);
// TODO(user): send code to server and exchange for access/refresh/ID tokens.
// [END get_auth_code]
} else {
// Show signed-out UI.
updateUI(false);
}
}
}
You can see the entire code at the following ServerAuthCodeActivity.java
The result, if you use that sample, looks like the following screenshot:
Then, you can follow the steps mentioned at the Google's documentation below (from step #3. Send the auth code to your app's backend using HTTPS POST):
Google Sign-In for Android - Enabling Server-Side Access
UPDATE: from the comments, if you want to get access token directly from android client app, please use the following sample code (replaced with your client_id, client_secret and the auth code)
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new FormEncodingBuilder()
.add("grant_type", "authorization_code")
.add("client_id", "812741506391-h38jh0j4fv0ce1krdkiq0hfvt6n5amrf.apps.googleusercontent.com")
.add("client_secret", "{clientSecret}")
.add("redirect_uri","")
.add("code", "4/4-GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8")
.build();
final Request request = new Request.Builder()
.url("https://www.googleapis.com/oauth2/v4/token")
.post(requestBody)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(final Request request, final IOException e) {
Log.e(LOG_TAG, e.toString());
}
#Override
public void onResponse(Response response) throws IOException {
try {
JSONObject jsonObject = new JSONObject(response.body().string());
final String message = jsonObject.toString(5);
Log.i(LOG_TAG, message);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
Please use compile 'com.squareup.okhttp:okhttp:2.6.0' (ver 3-RC1 will have different classes)
With a sucessful response, you will have the following info in logcat:
I/onResponse: {
"expires_in": 3600,
"token_type": "Bearer",
"refresh_token": "1\/xz1eb0XU3....nxoALEVQ",
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQxMWY1Ym......yWVsUA",
"access_token": "ya29.bQKKYah-........_tkt980_qAGIo9yeWEG4"
}
BNK has it spot on for the most part. The Activity class is the same as BNKs answer only with adding the OkHttp part once you get the GoogleSignInAccount in the onActivityResult() method.
But I was still getting errors with the OkHttp request part. Finally after a bit of testing(and part luck) around in Postman, I found that I was missing the id_token parameter. The OkHttp request was missing one parameter i.e the id_token. Use the ID token that you get from the GoogleSignInAccount something like this
GoogleSignInAccount acct = result.getSignInAccount();
String idTokenString = acct.getIdToken();
Now use this idTokenString along with all the parameters in the OkHttp part of BNK's answer somewhat like this
...
RequestBody requestBody = new FormEncodingBuilder()
.add("grant_type", "authorization_code")
.add("client_id", "alpha-numeric-string-here.apps.googleusercontent.com")
.add("client_secret", "{clientSecret}")
.add("redirect_uri","")
.add("code", "4/4-alphabetic-string-here")
.add("id_token", idTokenString) // Added this extra parameter here
.build();
...
The response one gets is same as BNKs answer
{
"access_token": "ya29.CjBgA_I58IabCJ...remainingAccessTokenHere",
"token_type": "Bearer",
"expires_in": 3577,
"id_token": "eyJhbGciOiJS...veryLongStringHere"
}
Now send this access_token to your backend server to authenticate just like you used to do during the times of GoogleAuthUtil and PlusAPI.
Hope this helps :) Special thanks to BNK!
Here it`s my approach with Kotlin, (this is my first Answer on StackOverflow, if there is something wrong, missing, or that i can do it better, let me know)
On the Login Actvity
private fun configureGoogleSignIn() {
mGoogleSignInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestServerAuthCode(getString(R.string.server_client_id_oauth))
.requestEmail()
.build()
mGoogleSignInClient = GoogleSignIn.getClient(this, mGoogleSignInOptions)
}
private fun signInWithGoogle() {
val signInIntent: Intent = mGoogleSignInClient.signInIntent
startActivityForResult(signInIntent, RC_SIGN_IN)
}
Make sure to Call configureGoogleSignIn() function on the OnCreate
Then to get the result
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
callbackManager?.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
val tag = "onActivityResult RC_SIGN_IN"
val task: Task<GoogleSignInAccount> = GoogleSignIn.getSignedInAccountFromIntent(data)
try {
val account = task.getResult(ApiException::class.java)
firebaseAuthWithGoogle(account!!)
getIdTokenFromFirebaseAuth()
var acct = GoogleSignIn.getLastSignedInAccount(this)
if (acct != null) {
var personName = acct.displayName
firstName = acct.givenName!!
lastName = acct.familyName!!
userEmail = acct.email!!
authCode = acct.serverAuthCode!! //THIS is what you looking for
googleIdToken2 = acct.idToken!!
Log.d(tag, authCode)
Log.d(tag, googleIdToken2)
var personId = acct.id
//todo pegar foto do google e por no cadastro do usuario
var personPhoto = acct.photoUrl
spinner.visibility = View.GONE
getGoogleAccessToken()
}
} catch (e: ApiException) {
spinner.visibility = View.GONE
infoToUserTextView.text = getString(R.string.ops_we_had_a_problem)
}
}
}
Then make a Call To Google API (i`m using Retrofit), using this interface make :
#FormUrlEncoded
#POST
fun getAccessTokenGoogle(
#Url url: String,
#Field("grant_type") grant_type: String,
#Field("client_id") client_id: String,
#Field("client_secret") client_secret: String,
#Field("redirect_uri") redirect_uri: String,
#Field("code") authCode: String,
#Field("id_token") id_token: String
):Call<GoogleSignInAccessTokenDataClass>
Here it`s the GoogleSignInAccessTokenDataClass
data class GoogleSignInAccessTokenDataClass(
val access_token: String,
val expires_in: Int,
val id_token: String,
val token_type: String
)
Make the Call on the Login Activity
private fun getGoogleAccessToken(){
val call = RetrofitGet().userInfoGson().getAccessTokenGoogle(
grant_type = "authorization_code", client_id = getString(R.string.server_client_id_oauth),
client_secret = getString(R.string.server_client_secret_oauth), redirect_uri = "",
authCode = authCode, id_token =googleIdToken2, url = googleTokenUrl
)
call.enqueue(object : Callback<GoogleSignInAccessTokenDataClass>{
val tag = "getGoogleAccessToken"
override fun onFailure(call: Call<GoogleSignInAccessTokenDataClass>, t: Throwable) {
Log.e(tag, t.toString())
}
override fun onResponse(
call: Call<GoogleSignInAccessTokenDataClass>,
response: Response<GoogleSignInAccessTokenDataClass>
) {
if (response.isSuccessful){
val responseBody = response.body()
googleAccessToken = responseBody!!.access_token
Log.d(tag, googleAccessToken)
}else{
try {
val responseError = response.errorBody()!!.string()
Log.e(tag, responseError)
}catch (e:Exception){Log.e(tag, e.toString())}
}
}
})
}
Thanks to #BNK, he has provided the working solution. And here is an official guide how to get 'access token' from 'auth code': https://developers.google.com/identity/protocols/OAuth2WebServer#exchange-authorization-code
Here I want to provide my solution with pure Android SDK classes. In case you do not want to add fancy library just for this purpose:
private String mAccessToken;
private long mTokenExpired;
private String requestAccessToken(GoogleSignInAccount googleAccount) {
if (mAccessToken != null && SystemClock.elapsedRealtime() < mTokenExpired) return mAccessToken;
mTokenExpired = 0;
mAccessToken = null;
HttpURLConnection conn = null;
OutputStream os = null;
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
try {
final URL url = new URL("https://www.googleapis.com/oauth2/v4/token");
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setConnectTimeout(3000);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
final StringBuilder b = new StringBuilder();
b.append("code=").append(googleAccount.getServerAuthCode()).append('&')
.append("client_id=").append(getString(R.string.default_web_client_id)).append('&')
.append("client_secret=").append(getString(R.string.client_secret)).append('&')
.append("redirect_uri=").append("").append('&')
.append("grant_type=").append("authorization_code");
final byte[] postData = b.toString().getBytes("UTF-8");
os = conn.getOutputStream();
os.write(postData);
final int responseCode = conn.getResponseCode();
if (200 <= responseCode && responseCode <= 299) {
is = conn.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
} else {
Log.d("Error:", conn.getResponseMessage());
return null;
}
b.setLength(0);
String output;
while ((output = br.readLine()) != null) {
b.append(output);
}
final JSONObject jsonResponse = new JSONObject(b.toString());
mAccessToken = jsonResponse.getString("access_token");
mTokenExpired = SystemClock.elapsedRealtime() + jsonResponse.getLong("expires_in") * 1000;
return mAccessToken;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
if (isr != null) {
try {
isr.close();
} catch (IOException e) {
}
}
if (br != null) {
try {
br.close();
} catch (IOException e) {
}
}
if (conn != null) {
conn.disconnect();
}
}
return null;
}
Run this method on background thread. Also client_id and client_secret you need to get from Google APIs console.
This is the simplest approach to get accessToken in Android
val httpTransport = AndroidHttp.newCompatibleTransport()
val jsonFactory: JsonFactory = JacksonFactory.getDefaultInstance()
tokenResponse = GoogleAuthorizationCodeTokenRequest(
httpTransport,
jsonFactory,
"https://www.googleapis.com/oauth2/v4/token",
clientId,
clientSecret,
account.serverAuthCode,
"" //optional param (redirect url)
).execute()
run it on background thread
Android use these libraries
implementation 'com.google.android.gms:play-services-auth:19.0.0'
implementation('com.google.api-client:google-api-client-android:1.23.0') {
exclude group: 'org.apache.httpcomponents'
}
In case anyone else is having issues making the final request to grab the access token from google. below is a tested and working approach as of 11-01-2018. Using retrofit2.
First of all, Here's link to google doc about the token exchange endpoint : https://developers.google.com/identity/protocols/OAuth2WebServer#exchange-authorization-code
public interface GoogleService {
#POST("token")
#FormUrlEncoded
#Headers("Content-Type:application/x-www-form-urlencoded")
Call<GoogleAuthData> getToken(
#Field("grant_type") String grantType,
#Field("client_id") String clientId,
#Field("client_secret") String clientSecret,
#Field("redirect_uri") String redirectUri,
#Field("code") String code);
}
Then call it like this :
Call<GoogleAuthData> call = RetroClient.getGoogleService().getToken(
"authorization_code", context.getString(R.string.server_client_id),
context.getString(R.string.server_client_secret), "", authCode);
I found a way to get access token without idToken, code, secret or any requests(like post to "https://www.googleapis.com/oauth2/v4/token").
All you need is only "client id".
Follow this steps:
Use "GoogleSignIn" to get sign in and get the "Account" object.
GoogleSignIn.getClient(
ctx,
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestProfile()
.requestIdToken(KEY.GOOGLE_CLIENT_ID)
.requestServerAuthCode(KEY.GOOGLE_CLIENT_ID, true)
.build())
.let { client ->
client.signOut()
.let { task ->
Observable.create<GoogleSignInClient> { ob ->
task.addOnCompleteListener { ob.onNext(client) }
}
}
}
.flatMap {
ctx.startActivityForResult(it.signInIntent, RC_SIGN_IN)
ctx.activityResultObservable
}
.filter { it.requestCode == RC_SIGN_IN }
.map {
GoogleSignIn
.getSignedInAccountFromIntent(it.data)
.getResult(ApiException::class.java)
}
Here I'm using RxJava to write the code, you can write your code without it.
Within the "Account" object, you can get the access token by using "GoogleAuthUtil".
.flatMap { result ->
Observable.create<AuthData> {
val scope = "oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.profile"
val accessToken = GoogleAuthUtil.getToken(context, result.account, scope)
// now you can use this token
it.onNext(accessToken)
}
}
The function "GoogleAuthUtil::getToken" makes a request, so you cannot run it in UI thread. Now you can send this token to your server. 👍

Categories

Resources