Always getting 401 when using android device but not in Postman - android

I am sending my token as Authorization in Retrofit but I always get a 401 code. But if I use the same token in Postman, I can get access. I know I am able to access the webapi because I can Login just fine and able to get the token from the Web Api. Please see my code below:
ApiService Interface
#POST("consolidated/sample")
fun sample(#Header("Authorization") token: String): Call<ResponseBody>
Calling the Service
private fun pushTransactionsToWebApi() {
val vApiService = ApiServiceBuillder.buildService(ApiService::class.java)
CoroutineScope(Main).launch {
var token = SharedDataManager.getInstance(context!!).applicationToken
var tokenArr = token!!.split(':')
responseFromApi = tokenArr[1] ==> I use this so I can remove the word "token" at the beginning of the token string
token = "Bearer ${responseFromApi}"
Log.i("TAG", "${token}") ==> ####
val call = vApiService.sample(token)
if(!call.isExecuted) {
call.enqueue(object : Callback<ResponseBody>{
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
responseFromApi = t.message
}
override fun onResponse(
call: Call<ResponseBody>,
response: Response<ResponseBody>
) {
if(response.isSuccessful){
Toast.makeText(context, "We are OK", Toast.LENGTH_LONG).show()
} else {
progressDialog!!.dismiss()
Toast.makeText(context, "We are NOT OK", Toast.LENGTH_LONG).show()
}
}
})
}
}
}
### => Result in my Log.i()
2020-04-08 13:03:09.235 14185-14185/com.kotlin.ambulantlcs I/TAG:
Bearer
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4ODgwNSIsInVzZXJJZCI6IjEiLCJmaXJzdE5hbWUiOiJKdWFuIiwibWlkZGxlTmFtZSI6IkEuIiwibGFzdE5hbWUiOiJEZWxhIENydXoiLCJ0cmFuc2FjdGlvbktleSI6IjJkNjZlYzMxLWI5M2ItNDI2ZC1hMzJlLTM0Yjc4OWE4M2E3OCIsInJldmVudWVEYXRlIjoiMjIvMDMvMjAyMCAyOjI0OjM0IFBNIiwic2hpZnQiOiIyIiwic29zSWQiOiIxMjM0NTYiLCJzb2RJZCI6IjY4IiwicGxhemEiOiI4MDMiLCJoYXNEZXBhcnRlZCI6IkZhbHNlIiwianRpIjoiNjhkMDdmNzEtMThiYy00NmQwLTg3YzEtY2MxMjk4YjgxZDkwIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjpbIlVzZXIiLCJBZG1pbiJdLCJleHAiOjE1ODY0MDg1NzUsImlzcyI6Imh0dHA6Ly8xOTIuMTY4LjEuNDo1MDAwIn0.m1mZw79KLIxq4pZPmBRbN7TjILvhvbUIJOCWDEM8I-k"}
If I paste this in my Postman, I can get access
What do I need to do? Thank you!

From our conversation in the comments, it seems like you're getting a json {"token": "..."} from SharedDataManager.getInstance(context!!).applicationToken. This explains why when you split in : you get printed in the log "..."}.
There are a lot of ways to deserialize json in Android. Here are some options. I think the vanilla way is something like:
val root = JSONObject(SharedDataManager.getInstance(context!!).applicationToken)
val token = root.getString("token")
With this you'll have the token in token.
However, if you already have a json library you could use it. For example, with gson you could do something like:
data class TokenData(
#SerializedName("token")
val token: String)
val token = Gson().fromJson(
SharedDataManager.getInstance(context!!).applicationToken,
TokenData::class.java)
You can now use token.
With Moshi using the kotlin gen library - com.squareup.moshi:moshi-kotlin-codegen - you can define the above model like:
#JsonClass(generateAdapter = true)
data class TokenData(
#Json(name = "token")
val token: String)
// Then get it like:
val token = Moshi.Builder()
.build()
.adapter(TokenData::class.java)
.fromJson(SharedDataManager.getInstance(context!!).applicationToken)
These are just some options. There's also the popular Jackson. Pick the one that suits best your needs. Hope this helps

Remove " " quotes from token
make sure that keys must be same
pass token like as:
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4ODgwNSIsInVzZXJJZCI6IjEiLCJmaXJzdE5hbWUiOiJKdWFuIiwibWlkZGxlTmFtZSI6IkEuIiwibGFzdE5hbWUiOiJEZWxhIENydXoiLCJ0cmFuc2FjdGlvbktleSI6IjJkNjZlYzMxLWI5M2ItNDI2ZC1hMzJlLTM0Yjc4OWE4M2E3OCIsInJldmVudWVEYXRlIjoiMjIvMDMvMjAyMCAyOjI0OjM0IFBNIiwic2hpZnQiOiIyIiwic29zSWQiOiIxMjM0NTYiLCJzb2RJZCI6IjY4IiwicGxhemEiOiI4MDMiLCJoYXNEZXBhcnRlZCI6IkZhbHNlIiwianRpIjoiNjhkMDdmNzEtMThiYy00NmQwLTg3YzEtY2MxMjk4YjgxZDkwIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjpbIlVzZXIiLCJBZG1pbiJdLCJleHAiOjE1ODY0MDg1NzUsImlzcyI6Imh0dHA6Ly8xOTIuMTY4LjEuNDo1MDAwIn0.m1mZw79KLIxq4pZPmBRbN7TjILvhvbUIJOCWDEM8I-k

Related

Retrofit adding extra slashes when I upload data

I'm developing an Android app using Retrofit to connect to a Spring Boot server.
When I update data, there are extra slashes and double quotes on the server.
This is the output of POST method. "open"
This is the output of PUT method. "\"open\""
I read a similar article and I'm guessing I encode twice, but I don't know where I'm doing it. Please help me.
This is the service class of Android.
#PUT("/posts/close/update/{id}")
fun updateClose(#Path("id") id: Long, #Body close: String): Call<ResponseBody>
This is the view.
onClick = {
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://*****.com")
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
thread {
try {
val service: PostService =
retrofit.create(PostService::class.java)
service.updateClose(6, "open")
.enqueue(object : Callback<ResponseBody> {
override fun onResponse(
call: Call<ResponseBody>,
response: Response<ResponseBody>
) {
Log.d("Response is", "${response.body()}")
}
override fun onFailure(
call: Call<ResponseBody>,
t: Throwable
) {
Log.d("Hi", "error")
}
})
} catch (e: Exception) {
Log.d("response-weather", "debug $e")
}
}
This is the repository of Spring Boot.
#Modifying
#Transactional
#Query("UPDATE posts SET close = :close where post_id = :id", nativeQuery = true)
fun updateClose(#Param("id") id: Long, #Param("close") close: String)
Thank you very much.
There is nothing wrong with the data or the android side.
Strings in JSON must be written in double quotes. For more info refer this page.
Your JSON data is {"name": "Ken", "uid": "12345"}
In order to use double quotes inside a string you have to escape it via a backslash. For more info refer this question.
That's the reason for the extra backslashes.
I tried to load the json string via python and it worked like a charm. Attaching screenshot for reference. So any backend you would be using will be able to parse the JSON String.
Finally, I got the codes which work fine.
Service Class of Android.
#PUT("/posts/close/update/{id}")
fun updateClose(#Path("id") id: Long, #Query("close") close: String): Call<ResponseBody>
Controller class of Spring Boot. Before, I used #RequestBody instead of #RequestParam.
#PutMapping("/posts/close/update/{id}")
fun updateClose(#PathVariable id: Long, #RequestParam close: String) = postService.updateClose(id, close)

How to use the OpenAPI generators retrofit stub with android kotlin client? Response 501 "Not Implemented"

I want to create a REST-API between an Android client and a Spring Boot server.
I created an OpenAPI 3.0 specification and used the CLI generator from https://openapi-generator.tech to create client and server stubs.
The server part works as intended when accessing it with other clients.
For the client side I used the generator for Kotlin with Retrofit2 via the parameter --additional-properties=library=jvm-retrofit2.
What I get is:
A ModelApi interface, defining my endpoint
A Model class, containing my model
An infrastructure package, containing ApiClient, ResponseExt, Serializer, CollectionFormats and a few *Adapter classes
The generated model class (shortened):
data class MapModel (
#Json(name = "id")
val id: kotlin.Long? = null,
#Json(name = "description")
val desc: String? = null
)
The API interface:
interface MapModelApi {
#GET("mapModel")
fun mapModelGet(): Call<kotlin.collections.List<MapModel>>
#DELETE("mapModel/{mapModelId}")
fun mapModelMapModelIdDelete(#Path("mapModelId") mapModelId: kotlin.Int): Call<Unit>
#GET("mapModel/{mapModelId}")
fun mapModelMapModelIdGet(#Path("mapModelId") mapModelId: kotlin.Int): Call<MapModel>
#PUT("mapModel/{mapModelId}")
fun mapModelMapModelIdPut(#Path("mapModelId") mapModelId: kotlin.Int, #Body mapModel: MapModel): Call<Unit>
#POST("mapModel")
fun mapModelPost(#Body mapModel: MapModel): Call<Unit>
#PUT("mapModel")
fun mapModelPut(#Body mapModel: MapModel): Call<Unit>
}
To do a GET request on the element 0, i tried this in my Activity:
val apiClient = ApiClient()
val mapObjectService = apiClient.createService(MapModelApi::class.java)
val call = mapObjectService.mapModelMapModelIdGet(0)
call.enqueue(object : Callback<MapModel> {
override fun onFailure(
call: Call<MapModel>,
t: Throwable
) {
Log.v("retrofit", "call failed")
t.printStackTrace()
}
override fun onResponse(
call: Call<MapModel>,
response: Response<MapModel>
) {
if (response.isSuccessful) {
val mapModel = response.body()
println(mapModel?.id)
} else {
val statusCode = response.code()
println("Http Code: $statusCode")
}
}
})
When I execute this I get a response, but it is always a 501 response "Not Implemented".
How can I fix this? What is missing in the code?
The server is the problem. The GET request returned a body with example data. I have overseen, that the request code sent by the server was not 200, but 501.

Retrofit2 OAuth2 with Client ID and Client secret key in Android - 401 Unauthorized error

When i try to get token with client id, client secret key and authorization code with help of retrofit. I'm getting 401 UnAuthorized error
MainActivity.kt
if (appLinkData != null && appLinkData.toString().startsWith("https://abc.abconline.com/plugin/callback")) {
val code = appLinkData.getQueryParameter("code")
// Toast.makeText(this, code, Toast.LENGTH_SHORT).show()
val builder = Retrofit.Builder().baseUrl("https://abc.abcOnline.com")
.addConverterFactory(GsonConverterFactory.create()).build()
val client = builder.create(restClient::class.java)
client.getAccessToken(
clientId,
clientSecret,
code,
"Authorization"
).enqueue(object : Callback<AccessToken> {
override fun onFailure(call: Call<AccessToken>, t: Throwable) {
Toast.makeText(applicationContext, "Falied: $t.message", Toast.LENGTH_SHORT)
.show()
}
override fun onResponse(call: Call<AccessToken>, response: Response<AccessToken>) {
Toast.makeText(
applicationContext,
response.body().toString(),
Toast.LENGTH_SHORT
).show()
}
RestClient.kt
interface restClient {
#POST("/oauth2/token")
#FormUrlEncoded
fun getAccessToken(
#Field("client_id") clientId: String,
#Field("client_secret") clientSecret: String,
#Field("code") code: String,
#Field("grant_type") grantType: String,
#Field("redirect_uri") redirectUri: String
): Call<AccessToken>
}
How I can fix it?
I am spending a lot of time to figure out...
Thank you
It's not the problem with Retrofit, Server is returning its response 401
means call is successful but there might be a problem with "Credentials" or insufficient " Privileges", Use POSTMAN for testing if the response is Succesful then implement it.
have a look at 401-unauthorized-error
hope it helps.
As a general point, integrating Open Id Connect into a mobile app is not easy. There are many tricky aspects related to usability and reliability. I think you should be looking for these qualities:
The ability to view API and OAuth messages
Use a mainstream and respected security library
If it helps I have some resources here that may help you to compare against, and a code sample you can run. Maybe you can adapt Retrofit around this type of solution?
Kotlin AppAuth Sample
Android Code Sample Write Up
Android HTTPS Debugging Write Up

Singleton that will hold an instance of Retrofit

So I have created a login that will take an username and password input from the user, encode it with Base64 in order to create a token in the format: ("Authorization", AUTH) where AUTH = "Basic " + Base64 encoding of user and password. This is sent via Headers.
So, in the end, it looks like this: Authorization: Basic XXXXXX, where XXXXXX is the user token.
And then it will check whether or not that user exists in the database via an API request.
I am using Retrofit and OkHttp3 in the same class as RetrofitClient and this class is responsible for using the API and adding those Headers.
Later, I use the RetrofitClient class on the Login Activity.
What I need to do now, is make this "token" available to all the other activities by creating a Singleton that will store the data of the Retrofit after a successful login. But I do not know how to do this.
I started learning Kotlin and Android 3 weeks ago.
Here is my code:
GET_LOGIN.kt
interface GET_LOGIN {
#GET("login")
fun getAccessToken() : Call<String>
}
RetrofitClient.kt
class RetrofitClient {
fun login(username:String, password:String){
val credentials = username + ":" + password
val AUTH = "Basic " + Base64.encodeToString(credentials.toByteArray(Charsets.UTF_8), Base64.DEFAULT).trim()
retrofit = init(AUTH)
}
// Initializing Retrofit
fun init(AUTH: String) : Retrofit{
// Creating the instance of an Interceptor
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
// Creating the OkHttp Builder
val client = OkHttpClient().newBuilder()
// Creating the custom Interceptor with Headers
val interceptor = Interceptor { chain ->
val request = chain?.request()?.newBuilder()?.addHeader("Authorization", AUTH)?.build()
chain?.proceed(request)
}
client.addInterceptor(interceptor) // Attaching the Interceptor
//client.addInterceptor(logging) // Attaching the Interceptor
// Creating the instance of a Builder
val retrofit = Retrofit.Builder()
.baseUrl("https://srodki.herokuapp.com/") // The API server
.client(client.build()) // Adding Http Client
.addConverterFactory(GsonConverterFactory.create()) // Object Converter
.build()
return retrofit
}
lateinit var retrofit : Retrofit
fun providesGetLogin(): GET_LOGIN = retrofit.create(GET_LOGIN::class.java)
}
LoginActivity.kt
var RetrofitClient : RetrofitClient = RetrofitClient()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
loginBtn.setOnClickListener {
val user = userTxt.text.toString()
val pass = passTxt.text.toString()
if (validateLogin(user, pass)){
login(user, pass)
}
}
}
fun validateLogin(user: String, pass: String): Boolean {
if (user == null || user.trim().isEmpty()){
Toast.makeText(this, "Missing Username or Password", Toast.LENGTH_SHORT).show()
return false
}
if (pass == null || pass.trim().isEmpty()){
Toast.makeText(this, "Missing Username or Password", Toast.LENGTH_SHORT).show()
return false
}
return true
}
fun login(user: String, pass: String) {
RetrofitClient.login(user, pass)
val apiLogin = RetrofitClient.providesGetLogin().getAccessToken()
apiLogin.enqueue(object : Callback<LoginResponse> {
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
if(response.isSuccessful){
if(response.body()?.code == 0){
Toast.makeText(this#LoginActivity, "Login Successful!", Toast.LENGTH_SHORT).show()
val intent = Intent(this#LoginActivity, List_usersActivity::class.java)
startActivity(intent)
} else {
Toast.makeText(this#LoginActivity, "Login Failed.", Toast.LENGTH_SHORT).show()
}
}
}
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
Toast.makeText(this#LoginActivity, "Login Failed.", Toast.LENGTH_SHORT).show()
}
})
}
}
first and foremost, please use camel case on java and kotlin. We have standards in java and kotlin on programming. And i can see that you are trying to do DI, but, thats not how you do it in Android.
Anyways, you could do this a couple of ways without even using a singleton but by saving it on a storage. Options are Shared Preferences, Local Storage and SQLite. But, if you insist on using a singleton. You can do it like this:
object MySingleton { // This is how you declare singletons in kotlin
lateinit var token: String;
}
EDIT
So, from your comment, it looked like you need to store the token. You could start by using sharedpreferences(database would be better) and store the token there. I assume you don't know how to so here is an example:
val sp = SharedPreferences("sp", 0);
sp.edit().putString("token", theTokenVariable); // not sure of this function
sp.edit().apply(); // you could use commit if you dont mind sharedpreferences to lag your screen(if it ever will)
Now how do you get the token from retrofit? The only way i could help you right now is that you could retrieve the response body from the response variable you receive from onResponse of the retrofit call. From there it is your problem mate. I don't know how your response is formatted, how it should be retrieved etc. A recommendation would be to format it as JSON.

Retrieve JSON response from rxjava/retrofit POST request

I've created a POST request using rxjava and retrofit that successfully hits my backend server and logs the user in (I get a 201 response in my console, all good). However, I want to then retrieve the users access token that is returned, but when I try to access the rxjava result, it just gives me the object I passed to it. Nowhere can I find out how to get the json success response. I have also verified there is in fact a response in Postman, so it's something with how I make this call.
Here is my Retrofit portion
#Headers("Content-Type: application/json")
#POST("api/v1/login")
fun loginTask(#Body credentials: UserLogin)
: Observable<UserLogin>
And the correspoinding API function
class ApiFunctions(val apiService: LunchVoteApi) {
fun provideHello(): io.reactivex.Observable<Hello> {
return apiService.helloMessage()
}
fun loginTask(email: String, password: String): io.reactivex.Observable<UserLogin> {
val credentials: UserLogin = UserLogin(email, password)
return apiService.loginTask(credentials)
}
}
The UserLogin model that is deserialized by Gson
data class UserLogin(
#SerializedName("email") val email: String,
#SerializedName("password") val password: String
)
And finally the call in my LoginActivity
val loginTask = ApiProvider.provideLoginTask()
override fun doInBackground(vararg params: Void): Boolean? {
// TODO: attempt authentication against a network service.
try {
// Simulate network access.
// Thread.sleep(2000)
compositeDisposable.add(
loginTask.loginTask(mEmail, mPassword)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe ({
result ->
System.out.println(result.toString())
}, { error ->
System.out.println(error)
})
)
} catch (e: InterruptedException) {
return false
}
The confusion comes when I try to access the result -> portion of the compositeDisposable call. It just prints out the UserLogin object. Am I missing something here? Thanks.
Turns out I was returning my UserLogin type instead of a pojo object with an access token property.
Changing my retrofit call to #Headers("Content-Type: application/json")
#POST("api/v1/login")
fun loginTask(#Body credentials: UserLogin)
: Observable<AccessToken>
And creating a new model
data class AccessToken(
#SerializedName("accessToken") val email: String
)
I am now able to print out the token. Thanks to #john-oreilly

Categories

Resources