OAuth Request token = null in android app - android

i am trying to authenticate something(in this case LinkedIn) using OAuth but the requested token always returns null?
Here is my code below:
public void authenticateAppOauthApi() {
Log.d(TAG, "authenticateAppOauthApi");
OAuthServiceProvider provider = new OAuthServiceProvider(
REQUEST_TOKEN_PATH, AUTHORIZE_PATH, ACCESS_TOKEN_PATH);
OAuthConsumer consumer = new OAuthConsumer(CALLBACK_URL, API_KEY,
SECRET_KEY, provider);
OAuthAccessor accessor = new OAuthAccessor(consumer);
Intent intent = new Intent(Intent.ACTION_VIEW);
Log.d(TAG, "Intent intent = new Intent(Intent.ACTION_VIEW );");
// intent.setData(Uri.parse(url));
String url = accessor.consumer.serviceProvider.userAuthorizationURL
+ "?oauth_token=" + accessor.requestToken + "&oauth_callback="
+ accessor.consumer.callbackURL;
intent.setData(Uri.parse(url));
Log.d(TAG, "intent.setData(Uri.parse(url)); = " + url);
mContext.startActivity(intent);
Log.d(TAG, "finish authenticateApp");
}
I basicaly followed the example here http://donpark.org/blog/2009/01/24/android-client-side-oauth
thanks in advance

you can try this code.
OAuthClient oAuthClient = new OAuthClient(new HttpClient4());
try {
oAuthClient.getRequestToken(accessor);
} catch (IOException e) {
e.printStackTrace();
} catch (OAuthException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}

Just a idea, Is it HTTP URL or HTTPS URL ?
I had some problem to access HTTPS URL, browser & app told that certificate was wrong.
Some Root Certificate are not known by Android.

I had some trouble using one of the OAuth libs I found on the net in my Scala Android app. Instead of finding a way to use the lib I just rolled my own... Not sure if it would work against linkedin (it works well with Yammer, which uses HTTPS). Well, below is the relevant code, mind you I'm pretty new to both Android and Scala so there are probably better ways to accomplish this.
The layout file "R.layout.authorization" is very basic contains with two buttons and a text field in a RelativeLayout.
class Authorization extends Activity {
val client = new DefaultHttpClient()
val reqUrl = "https://www.yammer.com/oauth/request_token"
val authUrl = "https://www.yammer.com/oauth/authorize?oauth_token="
val accessUrl = "https://www.yammer.com/oauth/access_token"
override def onCreate(bundle:Bundle) = {
super.onCreate(bundle)
this.setContentView(R.layout.authorization)
val authButton = findViewById(R.id.authButton).asInstanceOf[Button]
val getCodeButton = findViewById(R.id.getCode).asInstanceOf[Button]
val prefs = getSharedPreferences(PreferenceFile(), 0)
if(prefs.contains("oauth_request_token")) {
authButton.setVisibility(View.VISIBLE)
}
setupListeners(authButton, getCodeButton)
}
private def getAuthVerifier() = {
val authVerifierBox:EditText = Authorization.this.findViewById(R.id.authVerifier).asInstanceOf[EditText]
if(authVerifierBox != null && authVerifierBox.getText() != null) {
authVerifierBox.getText().toString()
} else {
""
}
}
private def setupListeners(authButton:Button, getCodeButton:Button) = {
authButton.setOnClickListener(new View.OnClickListener() {
override def onClick(view:View) = {
retrieveAuthTokenAndSecret()
}
})
getCodeButton.setOnClickListener(new View.OnClickListener() {
override def onClick(view:View) = {
try {
// Retrieve a request token with an async task and then start the browser...
// Use of an implicit definition to convert tuple to an async task.
(() => {
// Task to perform
val reqPost = new HttpPost(reqUrl)
reqPost.setHeader("Authorization", OAuthHeaderBuilder(null,null,null))
reqPost.setHeader("Content-Type", "application/x-www-form-urlencoded")
val reqResp= client.execute(reqPost)
reqResp.getEntity()
},
(entity:HttpEntity) => {
// PostExecute handle result from task...
if(entity != null) {
val reader = new BufferedReader(new InputStreamReader(entity.getContent()))
val line = reader.readLine()
val (oauth_request_token, oauth_token_secret) = OAuthTokenExtractor(line)
// Store request tokens so they can be used when retrieving auth tokens...
val editor = getSharedPreferences(PreferenceFile(), 0).edit()
editor.putString("oauth_request_token", oauth_request_token)
editor.putString("oauth_token_secret", oauth_token_secret)
editor.commit()
// Start browser...
val intent = new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl + oauth_request_token))
startActivity(intent)
val authButton = findViewById(R.id.authButton).asInstanceOf[Button]
authButton.setVisibility(View.VISIBLE)
}
}).doInBackground()
} catch {
case e:Exception => Log.e("ERROR", "ERROR IN CODE:"+e.toString())
}
}
})
}
private def retrieveAuthTokenAndSecret() = {
val authVerifier = getAuthVerifier()
val accessPost = new HttpPost(accessUrl)
val prefs = getSharedPreferences(PreferenceFile(), 0)
val token = prefs.getString("oauth_request_token","")
val secret = prefs.getString("oauth_token_secret","")
accessPost.setHeader("Authorization", OAuthHeaderBuilder(token, secret, authVerifier))
accessPost.setHeader("Content-Type", "application/x-www-form-urlencoded")
val accessResp = client.execute(accessPost)
val entity = accessResp.getEntity()
if(entity != null) {
val reader = new BufferedReader(new InputStreamReader(entity.getContent()))
val builder = new StringBuilder()
val line = reader.readLine()
val (oauth_token, oauth_token_secret) = OAuthTokenExtractor(line)
val result = new Intent()
val editor = getSharedPreferences(PreferenceFile(), 0).edit()
editor.putString("oauth_token", oauth_token)
editor.putString("oauth_token_secret", oauth_token_secret)
editor.commit()
setResult(Activity.RESULT_OK, result)
finish()
}
}
}
The OAuthHeaderBuilder is basically a copy of the Yammer oauth sample code:
object OAuthHeaderBuilder {
// Apply function taken from the Yammer oauth sample
def apply(token:String, secret:String,verifier:String):String = {
val buff = new StringBuilder()
val currentTime = System.currentTimeMillis()
// Hardcoded values for consumer key and secret...
val consumerKey = "<your consumer key here>"
val consumerSecret = "<your consumer secret here>"
buff.append("OAuth realm=\"");
buff.append("\", oauth_consumer_key=\"");
buff.append(consumerKey);
buff.append("\", ");
if (token != null) {
buff.append("oauth_token=\"");
buff.append(token);
buff.append("\", ");
}
buff.append("oauth_signature_method=\"");
buff.append("PLAINTEXT");
buff.append("\", oauth_signature=\"");
buff.append(consumerSecret);
buff.append("%26");
if (secret != null) {
buff.append(secret);
}
buff.append("\", oauth_timestamp=\"");
buff.append(currentTime);
buff.append("\", oauth_nonce=\"");
buff.append(currentTime);
if (verifier != null) {
buff.append("\", ");
buff.append("oauth_verifier=\"");
buff.append(verifier);
}
buff.append("\", oauth_version=\"1.0\"");
return buff.toString();
}
}
And inorder to extract the tokens I made a OAuthTokenExtractor object...
object OAuthTokenExtractor {
def apply(line:String) = {
val token = (line split ("&")).find(x => x.startsWith("oauth_token=")) match {
case Some(oauth_token) => (oauth_token split ("=") )(1)
case _ => ""
}
val secret = (line split ("&")).find(x => x.startsWith("oauth_token_secret=")) match {
case Some(oauth_token_secret) => (oauth_token_secret split ("=") )(1)
case _ => ""
}
(token,secret)
}
}
Hope it helps :)

Better use this article as a reference:
There is a code to get authentication URL in current version of Signpost is:
provider.retrieveRequestToken(CALLBACK_URL);
(and be sure to use CommonsHttpOAuthConsumer and CommonsHttpOAuthProvider)

Related

how to retrieve a link when shared from another app in android?

Hello I am trying to retrieve url, shared from another app and toast it as well as open it in WebViewTab.
But instead id of the app is displayed in toast.
here is my code:
val extras = intent.extras
if (extras != null) {
for (key in activity.intent.extras!!.keySet()) {
CopyKey = key
val value: String? = activity.intent.extras!!.getString(CopyKey)
Toast.makeText(applicationContext, value, Toast.LENGTH_LONG).show()
TabInfo.addTab(value.toString())
}
val url = intent.extras!!.getString("query")
if (url.toString().startsWith("http")) {
TabInfo.addTab(url.toString())
intent.removeExtra("query")
}
}
Thanks in advance
This solved my issue:
val extras = intent.extras
if (extras != null) {
val externalUrl: Uri? = intent?.data //retrieves the shared text from another app.
val url = intent.extras!!.getString("query")
if (url.toString().startsWith("http")) {
TabInfo.addTab(url.toString())
intent.removeExtra("query")
}
else {
TabInfo.addTab(externalUrl.toString())
}
}
Check the detail below code snippets
Note:
Error handling & data null check can be handled separately.
Assuming success case.
val URL_FINDER_REGEX = "((http:\\/\\/|https:\\/\\/|ftp:\\/\\/|file:\\/\\/)?(www.)?(([a-zA-Z0-9-]){2,2083}\\.){1,4}([a-zA-Z]){2,6}(\\/(([a-zA-Z-_\\/\\.0-9#:?=&;,]){0,2083})?){0,2083}?[^ \\n]*)"
fun test(intent: Intent?) {
intent?.let {
it.extras?.let { extras ->
val urlQuery = extras.getString("query")
urlQuery?.let {
val links = getUrlsFromText(urlQuery)
println(links)
// TODO("Your Business logic")
}
}
}
}
fun getUrlsFromText(text: String): ArrayList<URI> {
val availableLinks = ArrayList<URI>()
val matcher: Matcher = Pattern.compile(URL_FINDER_REGEX, Pattern.CASE_INSENSITIVE).matcher(text)
while (matcher.find()) {
try {
val url = URI(matcher.group())
availableLinks.add(url)
} catch (e: Exception) {
println(e)
}
}
return availableLinks
}
This is similar to some old queries.
Ref.
Regular expression to find URLs within a string
Detect and extract url from a string?

I can't find the files I uploaded to Google Drive

I'm trying to upload a file to Google Drive using Google Drive REST API v3. After the upload process is completed, it returns a status code of 200 (successful). But I can't find the files in my Google Drive. Please tell me what am I doing wrong? I will really appreciate if you provide a proper illustration or better still code snippet while helping me with this problem of mine. I am really anticipating your answers.
I have tried following the documentation but I am still getting the same error. I have searched everywhere online and stackoverflow, but none seems to provide the solution to my problem.
here is the code
private val AUTHORIZATION_PARAM = "Authorization"
private val BEARER_VAL = "Bearer "
private val CONTENT_TYPE_PARAM = "Content-Type: "
private val LINE_FEED = "\r\n"
private val APP_FOLDER_ID = "appDataFolder"
fun connectAndStartOperation() {
if (mAuthCode == null) {
signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestProfile()
.requestScopes(Scope(Scopes.DRIVE_APPFOLDER))
.requestIdToken(resources.getString(R.string.gdrive_clientId))
.requestServerAuthCode(resources.getString(R.string.gdrive_clientId))
.build()
mGoogleSignInClient = GoogleSignIn.getClient(this, signInOptions!!)
startActivityForResult(mGoogleSignInClient?.signInIntent, CLOUD_STORAGE)
Log.i("mAuthCode", "false")
} else {
Log.i("mAuthCode", "true")
writeDbToDrive()
mNextGoogleApiOperation = INVALID;
}
}
fun disconnect() {
mGoogleSignInClient?.signOut()
mActivity = null
mNextGoogleApiOperation = INVALID
mAuthCode = null
mAccessToken = null
mTokenExpired = 0
}
override fun onDestroy() {
disconnect()
super.onDestroy()
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == CLOUD_STORAGE) {
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
.addOnSuccessListener(this)
.addOnFailureListener(this)
}
}
override fun onSuccess(googleSignInAccount: GoogleSignInAccount?) {
Log.i("mAuthCode", "Success")
val scope = "oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.profile"
idTokenString = googleSignInAccount?.idToken
mAuthCode = googleSignInAccount?.serverAuthCode
mGoogleSignInAccount = googleSignInAccount
doAsync {
try {
mAccessToken = GoogleAuthUtil.getToken(this#SettingsActivity, mGoogleSignInAccount?.account, scope)
} catch (e: Exception) {
Log.i("Error AccessToken", "${e.message}")
e.printStackTrace()
}
uiThread {
Log.i("AccessTokenMy", "$mAccessToken")
}
}
}
override fun onFailure(p0: java.lang.Exception) {
Log.i("mAuthCode", "Failed")
p0.printStackTrace()
}
private fun writeDbToDrive() {
var conn: HttpURLConnection? = null
var os: OutputStream? = null
val accessToken = requestAccessToken(mGoogleSignInAccount!!)
if (accessToken == null)
return
try {
val boundary = "pb" + System.currentTimeMillis()
val url = URL("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart")
conn = url.openConnection() as HttpURLConnection
conn.requestMethod = "POST"
conn.useCaches = false
conn.doOutput = true
conn.doInput = true
conn.connectTimeout = 5000
conn.setRequestProperty(AUTHORIZATION_PARAM, BEARER_VAL + accessToken!!)
conn.setRequestProperty("Content-Type", "multipart/related; boundary=$boundary")
Log.i("Action", "Parameter set for server")
/////// Prepare data
//val timestamp = SimpleDateFormat("yyyy-MM-dd_HH:mm:ss", Locale.getDefault()).format(Date())
// Prepare file metadata (Change your backup file name here)
val b = StringBuilder()
b.append('{')
.append("\"name\":\"").append(exportedFileName).append('\"')
.append(',')
.append("\"mimeType\":").append("\"text\\/csv\"")
.append(',') //"\"application\\/vnd.google-apps.unknown\""
.append("\"parents\":").append("[\"").append(APP_FOLDER_ID).append("\"]")
.append('}')
val metadata = b.toString()
val data = readFile(File(filePath))
/////// Calculate body length
var bodyLength = 0
// MetaData part
b.setLength(0)
b.append("--").append(boundary).append(LINE_FEED)
b.append(CONTENT_TYPE_PARAM).append("application/json; charset=UTF-8")
.append(LINE_FEED)
b.append(LINE_FEED)
b.append(metadata).append(LINE_FEED)
b.append(LINE_FEED)
b.append("--").append(boundary).append(LINE_FEED)
b.append(CONTENT_TYPE_PARAM).append("text/csv").append(LINE_FEED)
b.append(LINE_FEED)
val beforeFilePart = b.toString().toByteArray(charset("UTF_8"))
bodyLength += beforeFilePart.size
bodyLength += data.size // File
b.setLength(0)
b.append(LINE_FEED)
b.append("--").append(boundary).append("--")
val afterFilePart = b.toString().toByteArray(charset("UTF_8"))
bodyLength += afterFilePart.size
conn.setRequestProperty("Content-Length", bodyLength.toString())
//if (BuildConfig.DEBUG_MODE) DebugHelper.log("LENGTH", bodyLength)
/////// Write to socket
os = conn.outputStream
try {
os!!.write(beforeFilePart)
os!!.write(data)
os!!.write(afterFilePart)
os!!.flush()
} catch (e: Exception) {
e.printStackTrace()
}
val msg = conn.responseMessage
val code = conn.responseCode
if (code == 200) {
Log.i("writeDbToDrive", "Exported Successfully: $code $msg")
} else {
Log.i("writeDbToDrive", "Error: $code $msg")
}
} catch (e: Exception) {
e.printStackTrace()
Log.i("writeDbToDrive", e.message!!)
} finally {
if (os != null) {
try {
os!!.close()
} catch (e: IOException) {
}
}
conn?.disconnect()
}
}
#Throws(IOException::class)
private fun readFile(file: File): ByteArray {
val f = RandomAccessFile(file, "r")
try {
val longlength = f.length()
val length = longlength.toInt()
if (length.toLong() != longlength)
throw IOException("File size >= 10 Mb")
val data = ByteArray(length)
f.readFully(data)
return data
} finally {
f.close()
}
}
private fun requestAccessToken(mGoogleSignInAccount: GoogleSignInAccount): String? {
val scope = "oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.profile"
doAsync {
try {
mAccessToken = GoogleAuthUtil.getToken(this#SettingsActivity, mGoogleSignInAccount?.account, scope)
} catch (e: Exception) {
Log.i("Error AccessToken", "${e.message}")
e.printStackTrace()
}
uiThread {
Log.i("AccessTokenMy", "$mAccessToken")
}
}
return mAccessToken
}
After reading through this Files: create Documentation, I have finally fixed the problem. Unknown to me is that the files where being saved in the AppData folder created by my app. The AppData folder is hidden which can only be accessible by and through my app. For me to be able to save the file to My Drive folder, I removed the part of the metadata
` .append(',')
.append("\"parents\":").append("[\"").append(APP_FOLDER_ID).append("\"]")`
So the metadata part is now like this
val b = StringBuilder()
b.append('{')
.append("\"name\":\"").append(exportedFileName).append('\"')
.append(',')
.append("\"mimeType\":").append("\"text\\/csv\"")
.append('}')
val metadata = b.toString()
Every other thing remains the same

Place Id is returning null in New Place API Likelihood

I have implemented the new Place API and my nearest place list method is given below.
private fun getNearestPlaceList(placesClient: PlacesClient) {
val placeFields = Arrays.asList(Place.Field.NAME)
val request = FindCurrentPlaceRequest.builder(placeFields).build()
placesClient.findCurrentPlace(request).addOnSuccessListener { response ->
placeList.clear()
for (placeLikelihood in response.placeLikelihoods) {
if(!placeLikelihood.place.id.isNullOrEmpty() && !placeLikelihood.place.name.isNullOrEmpty()){
var placeModel = PlaceModel(placeName = placeLikelihood.place.name!!, placeId = placeLikelihood.place.id!!)
placeList.add(placeModel)
}
}
setAdapter(placeList)
}.addOnFailureListener { exception ->
if (exception is ApiException) {
Log.e(TAG, "Place not found: " + exception.statusCode)
}
}
}
In this placeLikelihood.place.id is always returning null. Anyone know, how we can get the place id from likelihood?
You're using
val placeFields = Arrays.asList(Place.Field.NAME)
You need to include Place.Field.ID if you want an ID returned.

Is it possible to set placeholder path params with OkHttp

I have an url http://example.com/{x}/push/{y} and I'm using OkHttp curl it.
final HttpUrl httpUrl = HttpUrl
.parse("http://example.com/{x}/push/{y}")
.newBuilder()
???
.build();
Is it possible to set these {x} and {y} path params?
I can see method like addPathSegment which is somehow related, but not what I want.
Here’s one technique that might help you to get started.
HttpUrl template = HttpUrl.parse("http://example.com/{a}/b/{c}");
HttpUrl.Builder builder = template.newBuilder();
for (int i = 0; i < template.pathSegments().size(); i++) {
String parameter = template.pathSegments().get(i);
String replacement = null;
if (parameter.equals("{a}")) {
replacement = "foo";
} else if (parameter.equals("{c}")) {
replacement = "bar";
}
if (replacement != null) {
builder.setPathSegment(i, replacement);
}
}
HttpUrl url = builder.build();
Maybe HttpUrl.setPathSegment(index, value) can make it look a bit better :D
fun HttpUrl.insertPathSegment(index: Int, pathSegment: String): HttpUrl {
val newPathSegments: ArrayList<String> =
encodedPathSegments().fold(ArrayList()) { acc, oldPathSegment ->
printLog("OkHttp", "insertPathSegment oldPathSegment:$oldPathSegment ")
acc.add(oldPathSegment)
acc
}
return newBuilder().apply {
try {
newPathSegments.add(index, pathSegment)
addEncodedPathSegment("")
newPathSegments.forEachIndexed { index, path ->
printLog("OkHttp", "insertPathSegment setEncodedPathSegment:$index $path ")
setEncodedPathSegment(index, path)
//printLog("OkHttp", "insertPathSegment setPathSegment:$index $path ")
//setPathSegment(index, path)
}
} catch (e: Exception) {
e.printStackTrace()
}
}.build()}
I used the following approach to pass path variables:
inputUrl= http://localhost:8080/getResults/firstName/%s/lastName/%s
HttpUrl url = HttpUrl.get(String.format(inputUrl, "fn","ln"));
Request request = new Request.Builder().url(url).build();

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