I want to decrypt server data where can I decrypt it in android using retrofit
before using enc/dec I get this data from server
{
"success": true,
"secret": "NVARWBA4MAGSAW2F"
}
When I hit this API
#Headers("Content-Type: application/json")
#POST("user")
suspend fun addUser(#Body addUser: AddUser): Response<com.pryze.repository.model.User>
and after using enc/dec in response from server only I get encrypted text
'JAdS9hy168A2fG6FVTyzmFY739iawyk9qZ/yynRLtFTtE9nXxHyEas5ZrLzpl9IhpdgD27RpPBS5HsFHnVParg=='
and my app is crushed due to illegal response how can I solve this where I can put my dec code to first decrypt it then assign that to the response.
please try this code in your inspector class for Encryption and Decryption both.
override fun intercept(chain: Interceptor.Chain): Response {
if (!isInternetAvailable()){
throw NoInternetException("Make sure you have an active data connection")
}
try {
val aesUtil = AesUtil(keySize, iterationCount)
var request: Request = chain.request()
val buffer = Buffer()
request.body?.writeTo(buffer)
val strOldBody: String = buffer.readUtf8()
val encText = aesUtil.encrypt(salt,iv,passphrase,strOldBody)
val mediaType: MediaType? = "text/plain; charset=utf-8".toMediaTypeOrNull()
val strNewBody: String = encText
val body: RequestBody = RequestBody.create(mediaType, strNewBody)
request = request.newBuilder().addHeader("authorization", "Bearer " + t.getToken("JWT")).header("Content-Length", body.contentLength().toString()).method(request.method, body).build()
var req= chain.proceed(request)
var enc_data=""+req.body?.string().toString()
enc_data = aesUtil.decrypt(salt,iv,passphrase,enc_data)
return req.newBuilder().body(ResponseBody.create(req.body?.contentType(), enc_data)).build()
}
catch (e:ServiceConfigurationError){
}
catch (e: SSLHandshakeException){
throw NoInternetException("Request Time Out")
}
catch (e: ConnectException){
throw NoInternetException("Request Time Out")
}
catch (e: SocketTimeoutException){
throw NoInternetException("Make sure you have an active data connection")
}
catch (e:UnknownHostException){
throw NoInternetException("Make sure you have an active data connection")
}
catch (e: ErrnoException){
throw NoInternetException("Request Time Out")
}
throw NoInternetException("Request Time Out")
}
Related
I'm using Retrofit2 to get data in my Android applications. It looks like this:
interface ApiChatService {
#GET("ncs-chat-web/rest/v1/message")
suspend fun getChatMessages(#Header("Authorization") jwtToken: String, #Query("page") page: Long, #Query("count") count: Int): Response<List<ChatMessageApi>>
}
I call this Retrofit function this way:
override suspend fun getChatMessages(
jwtToken: String,
page: Long
): OperationResult<List<ChatMessageApi>> {
return try {
val response: Response<List<ChatMessageApi>> =
apiChatService.getChatMessages(normalizeJwtToken(jwtToken), page = page, count = MESSAGE_PAGE_SIZE)
if (response.isSuccessful) {
OperationResult(operationResult = Result.OK, resultObject = response.body())
} else {
Log.d("ApiDatasourceImpl.getChatMessages", response.errorBody()?.string()?: "Empty error message")
OperationResult(
operationResult = Result.ERROR,
operationInfo = response.errorBody()?.string()
)
}
} catch (e: Exception) {
Log.d("ApiDatasourceImpl.getChatMessages", e.localizedMessage ?: "Empty error message")
OperationResult(operationResult = Result.ERROR, operationInfo = e.localizedMessage)
}
}
In my Android code I got response code 500 with message "Internal server error"
When I call this request in Postman with such URL
https://my-server.com/ncs-chat-web/rest/v1/message?count=10&page=1
I got 200 code and expected payload.
I'm wondering is there any way to get URL which create Retrofit based on my interface function?
I am using api with oauth2 in my application. This code is provided by the api developers, but it was written for java and, as I understand it, httpclient is not supported on android. Alternatively, I think okhttp can be used. Please help me to write similar code for okhttp. I'm not familiar with okhttp and may be stuck here for a long time
class DiagnosisClient(
userName: String,
password: String,
authServiceUrl: String,
language: String,
healthServiceUrl: String?
) {
var token: AccessToken? = null
private val language: String
private val healthServiceUrl: String?
private val httpclient: CloseableHttpClient
#Throws(Exception::class)
private fun LoadToken(username: String, password: String, url: String) {
val keySpec = SecretKeySpec(
password.toByteArray(),
"HmacMD5"
)
var computedHashString = ""
computedHashString = try {
val mac: Mac = Mac.getInstance("HmacMD5")
mac.init(keySpec)
val result: ByteArray = mac.doFinal(url.toByteArray())
val encoder = BASE64Encoder()
encoder.encode(result)
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
throw Exception("Can not create token (NoSuchAlgorithmException)")
} catch (e: InvalidKeyException) {
e.printStackTrace()
throw Exception("Can not create token (InvalidKeyException)")
}
val httpPost = HttpPost(url)
httpPost.setHeader("Authorization", "Bearer $username:$computedHashString")
token = try {
val response: CloseableHttpResponse = httpclient.execute(httpPost)
val objectMapper = ObjectMapper()
if (response.getStatusLine().getStatusCode() !== HttpStatus.SC_OK) {
RetrieveException(response, objectMapper)
}
val accessToken: AccessToken = objectMapper.readValue(
response.getEntity().getContent(),
AccessToken::class.java
)
accessToken
} catch (e: ClientProtocolException) {
e.printStackTrace()
throw Exception("Can not create token (ClientProtocolException)")
} catch (e: IOException) {
e.printStackTrace()
throw Exception("Can not create token (IOException)")
}
}
#Throws(Exception::class)
private fun RetrieveException(response: CloseableHttpResponse, objectMapper: ObjectMapper) {
val errorMessage: String = objectMapper.readValue(
response.getEntity().getContent(),
String::class.java
)
System.out.println(
"Resposne with status code: " + response.getStatusLine().getStatusCode()
.toString() + ", error message: " + errorMessage
)
throw Exception(errorMessage)
}
init {
httpclient = HttpClients.createDefault()
this.healthServiceUrl = healthServiceUrl
this.language = language
LoadToken(userName, password, authServiceUrl)
}
}
```
I am attempting to make a sync call that needs to complete before proceeding with storing a user in the cloud. I believe the issue is within the RequestBody as it looks like it is just a byte array. Below is the code:
val client = OkHttpClient()
val mediaType: MediaType? = "application/json".toMediaTypeOrNull()
val body: RequestBody =
RequestBody.create(mediaType, "{\"type\":\"DEFAULT\",\"name\":\"lkjlkj\"}")
val request: Request = okhttp3.Request.Builder()
.url("https://api.example.com/endpoint")
.post(body)
.addHeader("Accept", "application/json")
.addHeader("Content-Type", "application/json")
.addHeader(
"Authorization",
"Bearer SK-xxxxxx-4QAXH"
)
.build()
Toast.makeText(this#RegisterActivity, "Entering Call",Toast.LENGTH_SHORT).show()
val response: Unit = client.newCall(request).execute().use {
Toast.makeText(this#RegisterActivity, "sent call, awaiting response",Toast.LENGTH_SHORT).show()
if (it.isSuccessful){
val content = JSONObject(it.body.toString())
desiredString = content.getJSONArray("desiredStringField").toString()
Toast.makeText(this#RegisterActivity, desiredString,Toast.LENGTH_SHORT).show()
}
if (!it.isSuccessful){
Toast.makeText(this#RegisterActivity, "failed",Toast.LENGTH_SHORT).show()
}
}
The code doesn't crash but it seems the call never completes, as it never makes it into the it.isSuccessful or !it.isSuccessful. Perhaps its a bad formed call somehow. Please help if you can.
Try to enqueue the request and manage the response using a Callback:
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
if (!response.isSuccessful){
Toast.makeText(this#RegisterActivity, "failed",Toast.LENGTH_SHORT).show()
return
}
try {
val content = JSONObject(response.body?.string() ?: "")
desiredString = content.getJSONArray("desiredStringField").toString()
Toast.makeText(this#RegisterActivity, desiredString,Toast.LENGTH_SHORT).show()
} catch (e: JSONException) {
// Error parsing JSON object
}
}
override fun onFailure(call: Call, e: IOException) {
Toast.makeText(this#RegisterActivity, "failed",Toast.LENGTH_SHORT).show()
}
}
I want to stream twitter tweets continuously with twitter stream api with retrofit and without any 3rd party libraries. When i try to call the api " https://api.twitter.com/2/tweets/search/stream " I'm only getting the result first time. How to stream it?
I have finally achieved it using this snippet
override suspend fun streamTweets(): Flow<Resource<TweetResponseModel>> {
return flow {
val client: OkHttpClient = OkHttpClient().newBuilder().addInterceptor(
BasicAuthInterceptor(getAPIKey(), getAPISecretKey())
).build()
val request: Request = Request.Builder()
.url(TWITTER_STREAM_URL)
.method("GET", null)
.build()
val response: okhttp3.Response = client.newCall(request).execute()
val source = response.body?.source()
val buffer = Buffer()
while (!source!!.exhausted()) {
response.body?.source()?.read(buffer, 8192)
val data = buffer.readString(Charset.defaultCharset())
try {
val tweetResponseModel: TweetResponseModel =
Gson().fromJson(data, TweetResponseModel::class.java)
emit(Resource.Success(tweetResponseModel))
} catch (e: Exception) {
Log.e("jsonException", data)
}
}
}.flowOn(ioDispatcher)
}
How can I refresh my token using authenticator? I need the refresh token method to return the token or null when I get 401 in my api call.
class SupportInterceptor() : Interceptor, Authenticator {
/**
* Interceptor class for setting of the headers for every request
*/
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
request = request?.newBuilder()
?.addHeader("Content-Type", "application/json")
?.addHeader("app-id", "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
?.build()
return chain.proceed(request)
}
/**
* Returns a request that includes a credential to satisfy an authentication challenge in
* [response]. Returns null if the challenge cannot be satisfied.
*
* The route is best effort, it currently may not always be provided even when logically
* available. It may also not be provided when an authenticator is re-used manually in an
* application interceptor, such as when implementing client-specific retries.
*/
override fun authenticate(route: Route?, response: Response): Request? {
var requestAvailable: Request? = null
try {
return runBlocking {
when (val tokenResponse = refreshToken()) {
is Success -> {
// userPreferences.saveAccessTokens(
// tokenResponse.value.access_token!!,
// tokenResponse.value.refresh_token!!
// )
response.request.newBuilder()
.header("Authorization", "Bearer ${tokenResponse.value.access_token}")
.build()
}
else -> null
}
}
// requestAvailable = response?.request?.newBuilder()
//// ?.addHeader("Authorization", "Bearer $token")
// ?.build()
// return requestAvailable
} catch (ex: Exception) {
}
return requestAvailable
}
suspend fun refreshToken(): Either<Failure, String?> {
return withContext(Dispatchers.IO) {
try {
val PREFS_NAME = "userPref"
val sharedPref: SharedPreferences =
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
val refreshToken = sharedPref.getString(MyConstants.KEY_REFRESH_TOKEN, "")
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(baseURL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val api: TokenRefreshApi = retrofit.create(TokenRefreshApi::class.java)
val response = api.refreshAccessToken(refreshToken).execute()
// val call: Call<LogIn> = api.refreshAccessToken(refreshToken)
when (response.isSuccessful) {
false -> Either.Left(response.errorResponse())
true -> {
val editor: SharedPreferences.Editor = sharedPref.edit()
editor.putString(
MyConstants.KEY_ACCESS_TOKEN,
response.body()!!.access_token
)
editor.putString(
MyConstants.KEY_REFRESH_TOKEN,
response.body()!!.refresh_token
)
editor!!.apply()
response.body()!!.access_token
}
}
} catch (e: Exception) {
Timber.e("searchTasks: $e")
Either.Left(Failure.UnknownError)
}
}
}
}
I would first clarify the industry standard behavior for reliable clients:
Client tries an API request with an access token
If client receives a 401 it attempts to silently refresh the access token and retry the API request with the new token
If there are technical problems avoid redirecting the user to re-authenticate
Here is some plain Kotlin code of mine that does this.
Looks to me like Retrofit's Authenticator interface makes this easier, and will do the retry for you. Your code looks mostly correct and similar to mine but without the manual checks for 401s:
You need to test 401s though, and one way to do this is to add arbitrary characters to an access token during development, to simulate expiry
In case someone is having difficulty same like me.
override fun authenticate(route: Route?, response: Response): Request? {
var requestAvailable: Request? = null
try {
return runBlocking {
val tokenResponse = getNewToken()
if (!tokenResponse.isNullOrEmpty()) {
response.request.newBuilder()
.header("Authorization", "Bearer ${tokenResponse}")
.build()
} else {
null
}
}
} catch (ex: Exception) {
}
return requestAvailable
}
private fun getNewToken(): String? {
val PREFS_NAME = "userPref"
val sharedPref: SharedPreferences =
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
val refreshToken: String = sharedPref.getString("refresh_token", "").orEmpty()
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.BASE_API_URL_CONSUMER)
.addConverterFactory(GsonConverterFactory.create())
.build()
val hashMap = HashMap<String, String>()
hashMap.put("refresh_token", refreshToken)
val call = retrofit.create(TokenRefreshApi::class.java).refreshAccessToken(hashMap)
val authTokenResponse = call?.execute()?.body()
if (authTokenResponse != null) {
val editor: SharedPreferences.Editor = sharedPref.edit()
editor.putString(
"access_token",
authTokenResponse!!.access_token
)
editor.putString(
"refresh_token",
authTokenResponse!!.refresh_token
)
editor!!.apply()
return authTokenResponse!!.access_token
} else {
return null
}
}