I am trying to make use of the twitter api, and am setting up a handler to deal with twitter api requests.
To do this I am using an HTTPUrlConnection and following the Twitter api docs
I've managed to get authenticated using the 3-legged OAuth process, but am stuck when actually trying to make requests with the oauth token.
Here is an example of what my auth headers look like:
Accept=*/*,
Connection=close,
User-Agent=OAuth gem v0.4.4,
Content-Type=application/x-www-form-urlencoded,
Authorization=
OAuth oauth_consumer_key=****&
oauth_nonce=bbmthpoiuq&
oauth_signature=*****%3D&
oauth_signature_method=HMAC-SHA1&
oauth_timestamp=1570586135&
oauth_token=*****&
oauth_version=1.0,
Host=api.twitter.com
for each header in the auth header I add it to my HTTP GET call like this:
urlConnection.setRequestProperty(header.key, header.value)
Note that Authorization points to one string
OAuth oauth_consumer_key=****&oauth_nonce=bbmthpoiuq&oauth_signature=*****%3Doauth_signature_method=HMAC-SHA1oauth_timestamp=1570586135&oauth_token=*****&oauth_version=1.0,Host=api.twitter.com
The following params are collected as follows:
oauth_consumer_key is my application API key
oauth_signature is computed by the hmacSign function below
oauth_token is the "oauth_token" received in the response from /oauth/access_token
The hmacSign function:
private fun hmacSign(requestType: RequestType, url: String, params: Map<String, String>): String {
val type = "HmacSHA1"
val key = "$API_SECRET&$tokenSecret"
val value = makeURLSafe("${requestType.string}&$url${getURLString(params.toList().sortedBy { it.first }.toMap())}")
val mac = javax.crypto.Mac.getInstance(type)
val secret = javax.crypto.spec.SecretKeySpec(key.toByteArray(), type)
mac.init(secret)
val digest = mac.doFinal(value.toByteArray())
return makeURLSafe(Base64.encodeToString(digest, NO_WRAP))
}
private fun makeURLSafe(url: String) : String {
return url.replace("/", "%2F")
.replace(",", "%2C")
.replace("=", "%3D")
.replace(":", "%3A")
.replace(" ", "%2520")
}
protected fun getURLString(params: Map<String, Any>) : String {
if (params.isEmpty()) return ""
return params.toList().fold("?") { total, current ->
var updated = total
updated += if (total == "?")
"${current.first}=${current.second}"
else
"&${current.first}=${current.second}"
updated
}
}
In the GET call I'm referring to, tokenSecret would be the oauth secret received from /oauth/access_token
After i make the call I get a 400: Bad Request
Is there anything obvious I'm doing wrong?
Update: By putting the params at the end of the url like ?key=value&key2=value2... instead of a 400 I get a 401. So I'm not sure which is worse, or which is the right way to do it.
Okay, finally got it working
So using the suggestion in the comments, I downloaded postman and copied all my info into postman - when i made the request there, I got a 200!
So then i looked back and tried to figure out what was different and there were a few things:
First the hmac sign function was missing an &
New function (added another & after the url):
private fun hmacSign(requestType: RequestType, url: String, params: Map<String, String>): String {
val type = "HmacSHA1"
val key = "$API_SECRET&$tokenSecret"
val value = makeURLSafe("${requestType.string}&$url&${getURLString(params.toList().sortedBy { it.first }.toMap())}")
val mac = javax.crypto.Mac.getInstance(type)
val secret = javax.crypto.spec.SecretKeySpec(key.toByteArray(), type)
mac.init(secret)
val digest = mac.doFinal(value.toByteArray())
return makeURLSafe(Base64.encodeToString(digest, NO_WRAP))
}
Next I noticed my auth header had its params seperated with & but they all should've been replaced with , - i also needed to surround each of my values in ""
So it looked like:
OAuth oauth_consumer_key="****",oauth_nonce="bbmthpoiuq",oauth_signature="*****%3D",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1570586135",oauth_token="*****",oauth_version="1.0",Host="api.twitter.com"
After these changes i started getting 200!
Hopefully this helps someone else (though im sure its unlikely considering how specific these issues were)
Related
I am making an Android app using Kotlin.
I made a .NET Web Api to which I connect to from Kotlin.
My problem is that when I send a request to my Web API, I get an exception "Cleartext HTTP traffic to localhost not permitted".
I have tried modifying my AndroidManifest.xml by adding "android:usesCleartextTraffic="true"" and adding a network_security_config.xml (and referencing it in the manifest android:networkSecurityConfig="#xml/network_security_config").
Nothing works.
My code for sending a request to my Web API:
const val URL = "http://localhost:24517/bar?"
const val USERNAME = "username=";
const val PASSWORD = "password=";
class BarRepo : IRepoBar{
override fun getBarByUser(userName: String, password: String): Bar {
val url = "$URL$USERNAME$userName&$PASSWORD$password"
val apiResponse = URL(url).readText()
val obj = Json.decodeFromString<Bar>(apiResponse)
return obj
}
}
The code breaks at "val apiResponse = URL(url).readText()" with the mentioned exception.
Turns out I had to put my local ip address to access it.
I am trying to GET request for the API but a special character "*" (asterisk sign) breaks my API call and hence it is sent incomplete. Is there any way to escape it?
Instead of this:
https://rest2.bermanngps.cl/BermannRest/api/enviacomando?tk=3cf40d35c4e48b60e007cdc85f1342f5&comando=%24SRVFFFFFF%2C25%2C1*8F&md5pass=4e1ed8ef96fb83a0a30c39b0019fadc7&user=1017&avserie=12977
this is sent:
https://rest2.bermanngps.cl/BermannRest/api/enviacomando?tk=3cf40d35c4e48b60e007cdc85f1342f5&comando=%24SRVFFFFFF%2C25%2C1
I am using the GET request method of retrofit, using the repository to load Query strings dynamically. How would I use the URLEncoder method there?
#GET("enviacomando")
suspend fun getSendComando(
#Query("tk") tk: String,
#Query("comando") comando: String,
#Query("md5pass") md5pass: String,
#Query("user") user: String,
#Query("avserie") avserie: String
): Response<SendComandoResponse>
You can use URLEncoder class for this.
So you need to encode query part from URL with URLEncoder.
String query = URLEncoder.encode("tk=3cf40d35c4e48b60e007cdc85f1342f5&comando=%24SRVFFFFFF%2C25%2C1*8F&md5pass=4e1ed8ef96fb83a0a30c39b0019fadc7&user=1017&avserie=12977", "utf-8");
String url = "https://rest2.bermanngps.cl/BermannRest/api/enviacomando?" + query;
I just changed the conflicting value with the URLEncoder.
token?.let { tk ->
userId?.let { user ->
comando?.comandoComando?.let { it1 ->
mVehiculo?.let { it2 ->
mViewModel.sendComando(
tk = tk,
avserie = it2.avSerie,
user = user,
comando = URLEncoder.encode(it1, "UTF-8"),
md5pass = pass
)
}
}
}
}
Since this method didn't work consistently I tried using #Url to send the request.
Still, many times the request breaks at the "*" (asterisk sign)
I am calling a post API with some parameters. one the parameters is
activatecode="aaaaaa$rr"
when the API call is made, it is sent as
activatecode=aaaaaa%24rr
The $ is encoded as %24. How to avoid this and send the special character as it is?
I am using Retrofit 2.9.0.
I have this service :
interface WordpressService {
#GET("wp-json/wp/v2/posts")
suspend fun getPosts(
#Query("page") page: Int,
#Query("per_page") limit: Int,
#Query("_fields", encoded = true) fields: String = "date,link,title,content,excerpt,author"
): List<Post>
}
Without putting encode = true, I end up with this request :
GET http://example.org/wp-json/wp/v2/posts?page=1&per_page=10&_fields=date%2Clink%2Ctitle%2Ccontent%2Cexcerpt%2Cauthor
With encode = true, I get :
GET http://motspirituel.org/wp-json/wp/v2/posts?page=1&per_page=10&_fields=date,link,title,content,excerpt,author
So in my case, adding encode = true in annotation solved my problem.
As I am newbie in Android, I am building an application in which I need to update layer data on Geo server. For that scenario, I m calling post method along with XML body request.
I need to send data in xml format in request body and need to get response in xml. For that, I tried using XmlSerializer instead of JsonFeature but I got error saying "XmlSerializer is not comapanion object, need to initialize here"
val httpClient = HttpClient {
install(JsonFeature) {
serializer = KotlinxSerializer(Json.nonstrict)
}
}
Thanks in Advance!!
There are no XML related features, but you can use existing Json with the XML serializer
The first thing you need to do is to find an appropriate serializer. I used https://github.com/pdvrieze/xmlutil.
implementation("net.devrieze:xmlutil-android:0.20.0.1")
You also can use other platform dependencies(I used the JVM and multiplatform).
Next you can configure the JsonFeature feature with a custom serializer:
val client = HttpClient {
Json {
serializer = XMLSerializer()
accept(ContentType.Application.Xml)
}
}
Using ContentType.Application.Xml
And finally, add the serializer:
#OptIn(ImplicitReflectionSerializer::class)
class XMLSerializer(private val format: XML = XML()) : JsonSerializer {
override fun read(type: TypeInfo, body: Input): Any {
val text = body.readText()
val deserializationStrategy = format.context.getContextual(type.type)
val mapper = deserializationStrategy
?: type.kotlinType?.let { serializer(it) }
?: type.type.serializer()
return format.parse(mapper, text) ?: error("Failed to parse response of type $type. The result is null.")
}
override fun write(data: Any, contentType: ContentType): OutgoingContent {
val serializer = data::class.serializer() as KSerializer<Any>
val text = format.stringify(serializer, data, null)
return TextContent(text, contentType)
}
}
Here is the full result sample with the server(adopted to run without Android): https://gist.github.com/e5l/3b4d5d704b4d7c6e2a65cf68de8e9ca4
When order_id is used in the JSON of checkout object error is occurred
The error that I am getting is this:
06-23 14:08:44.132 E/PaymentActivity:166: The id provided does not exist
This comes in:
onPaymentError(code: Int, response: String?, data: PaymentData?)
method of PaymentResultWithDataListener
startPayment method is:
private fun startPayment(orderId: Long, paymentGateway: PaymentGateway) {
val checkout = Checkout()
checkout.setImage(R.drawable.lifcare_logo)
checkout.setFullScreenDisable(true)
try {
val options = JSONObject()
options.put("name", "Name")
options.put("description", orderId.toString())
options.put("currency", "INR")
options.put("amount", paymentGateway.amount.times(100))
options.put("order_id", paymentGateway.refTransactionId)
val prefill = JSONObject()
prefill.put("email", "EmailID")
prefill.put("contact", "Number")
prefill.put("method", paymentGateway.subMethod?.name)
options.put("prefill", prefill)
options.put("theme", JSONObject("{color: '#7cb342'}"))
checkout.open(this, options)
} catch (e: Exception) {
Timber.e(e, "Cannot pay right now!!")
}
}
ref_transaction_id is: "ref_transaction_id": "order_AQjijq5Fj4lg8m"
When order_id is not used then the SDK is working fine. order_id somehow is creating the issue.
May i know what number are you using?
It does not work for default number like '9999999999'.
The number should be a valid number.
Try to add notes key in option parameter as given below:
jsonObject.put("order_id", "12345")
jsonObject.put("subscription_id", "50214")
jsonObject.put("user_id", "101")
options.put("notes", jsonObject)