I'm trying to get posts from facebook page using Retrofit but I can't pass Access token and every time I get an error io.reactivex.exceptions.OnErrorNotImplementedException: HTTP 400 Bad Request
This is my code:
RetroAPI:
#GET("{page_id}/feed")
fun getPosts(#Path("page_id") pageId : String,
#Header("access_token") authToken : AccessToken)
: Observable<Posts>
Set Access Token:
AccessToken.setCurrentAccessToken(AccessToken("AppToken", "AppID","userID",null,null,null,null,null))
Get data:
var pagePosts : Observable<Posts> = facebook.getPosts("pageID", AccessToken.getCurrentAccessToken())
pagePosts.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe({result ->
var a : Posts = result
var b : List<Data> = result.data
Log.d("Posts A","${a.data[1].id}")
Log.d("Data B", "$b")
})
Set RetrofitAPI:
private val facebook : RetroAPI
init{
val retrofit = Retrofit.Builder()
.baseUrl("https://graph.facebook.com/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
facebook = retrofit.create(RetroAPI::class.java)
}
Normally access_token is sent as a query param to authenticate your request.
However you can also send a header in the form
Authorization: Bearer XXX
But AFAIK sending access_token as a header is unsupported.
Related
In my application I want send some data to server and for this I used Retrofit library.
I should send data such as below:
{
"hours": ["22:05","19:57"]
}
I write below codes for Api services:
#POST("profile")
suspend fun postSubmitDrug(#Header("Authorization") token: String, #Body body: RequestBody):Response<ProfileData>
And write below codes info fragment for send data to server:
private lateinit var requestBody: RequestBody
var hourMinuteList = mutableListOf<String>()
val multiPart = MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("_method", "post")
multiPart.addFormDataPart("hours[]", parentActivity.hourMinuteList.toString())
requestBody = multiPart.build()
But send this list such as below:
{
"hours": [22:05,19:57]
}
How can I fix it and send list of string to server?
Use com.google.gson, and when you create your retrofitCLient call .addConverterFactory(create(GsonBuilder()))
create data model, ex:
data class RequestBody(
val hours: ArrayList<String>? = null
)
and just call your endpoint
#POST("profile")
suspend fun postSubmitDrug(#Header("Authorization") token: String, #Body body: RequestBody):Response<ProfileData>
and this is all, gson will automatically serialize your data to json
I am developing new android app where I am making post login request but I have confused I am using koin dependcy injection how can I pass refresh token and access token after successfully response. in order to making a call
interface MeloApi {
#Headers("Content-Type: application/json")
#POST("/login")
suspend fun makeLogin(#Body loginModel: LoginModel) : Response<LoginModel>
}
below my login model
data class LoginModel(val username:String, val password:String)
// following my base url
object Constants {
const val BASE_URL = "https://api.getmelo.app/"
}
when I making post request in the body I am sending following format
{
"username": "username",
"password": "password"
}
and it is returning
following format
{
"refreshToken": "refreshtoken",
"accessToken": "accesstoken",
"status": "success",
"user": {
"username": "username",
"email": "email",
"id": "5E038B1F-BE2F-477B-9E87-69411D0D622B",
"kind": "adult",
"details": {
"firstName": "Test",
"lastName": "User",
"id": "12DEA70B-1146-46D3-A57B-B47730BC8C4B"
},
"status": "active"
}
}
below my networkModule.kt class where I want to pass refresh token and access token
val appModule = module {
single {
val httpInterceptor = HttpLoggingInterceptor()
httpInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
Retrofit.Builder()
.client(
OkHttpClient.Builder()
.addInterceptor()
.addInterceptor(httpInterceptor).build()
)
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
.create(MeloApi::class.java)
}
}
My question is how can I pass refresh token and access token in header my network koin module
First of all, you have a mistake in your request definition.
suspend fun makeLogin(#Body loginModel: LoginModel) : Response<LoginModel>
According to the JSON response you posted, you are not getting LoginModel as response, but rather a model with accessToken and refreshToken. Let's name it:
data class AuthTokenEntity(
val accessToken: String,
val refreshToken: String,
// the rest of the model according to the JSON
)
So change your makeLogin to:
suspend fun makeLogin(#Body loginModel: LoginModel) : AuthTokenEntity
Now, I assume you are trying to implement the OAuth flow, and you store the tokens locally (either DB or SharedPreferences). To pass access token to your requests, create an Interceptor that attaches the authorization header with the access token to each outgoing request. Check my example.
Now, if it's really OAuth that you are implementing, I am quite sure there is a different request to refresh an expired access token. You use the refresh token for that request only, you don't pass refresh token to any other requests. If I am right, you'll have to add a new method into your MeloApi (the parameter type depends on the request's definition):
suspend fun refreshToken(refreshToken: String) : AuthTokenEntity
Then, you need to create an Authenticator that will call refreshToken in case the access token expires and server returns HTTP 401. Check my example
Finally, you provide the Interceptor and the Authenticator to your Retrofit via Koin as follow:
val appModule = module {
single { MyAuthenticator() }
factory { MyInterceptor() }
single {
val httpInterceptor = HttpLoggingInterceptor()
httpInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
Retrofit.Builder()
.client(
OkHttpClient.Builder()
.addInterceptor(httpInterceptor)
.addInterceptor(get<MyInterceptor>())
.authenticator(get<MyAuthenticator>())
.build()
)
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
.create(MeloApi::class.java)
}
}
For more comprehensive examples, check the snippets I created for this question.
I'm trying to add AUTH Token to Retrofit
I'm saving token on preferences Datastore
but when I trying to retrieve it show null
enter image description here
My code like this and its working.
private fun getHeaderInterceptor():Interceptor{
return Interceptor { chain ->
val request =
chain.request().newBuilder()
.header("Cookie", cookie!!)
.build()
chain.proceed(request)
}
}
Full code: https://gist.github.com/Cr3bain/adf61caaa9da3291e1739c9bf0f334d8
I do a post request to Firebase push notifications:
#FormUrlEncoded
#POST("https://fcm.googleapis.com/fcm/send")
suspend fun createPushNotifications(
#Header("Authorization") Authorization: String,
#Field("to") to: String,
#Field("data") data: String
): Response<ResponseBody>
// Create Retrofit
val retrofit = Retrofit.Builder()
.baseUrl(urlApp)
.addConverterFactory(GsonConverterFactory.create())
.build()
// Create Service
val service = retrofit.create(notificationAPI::class.java)
var data:String = "{\"body\":\"value\",\"title\":\"Collapsing A\"}"
// Do the POST request and get response
val response = service.createPushNotifications(FireBaseKey,deviceId, data)
Question : I get back a wrong format data payload
Message
data payload:{data={"body":"value","title":"Collapsing A"}}
But what i need is a payload in this format:
{data: {"body":"value","title":"Collapsing A"}}
I am working on an user app for a local charitable organization, and need to access their API. The API is from wild apricot, and this is the documentation for making a token request:
Authentication tokens are obtained from Wild Apricot's authentication service, which is located at https://oauth.wildapricot.org. This service adheres to oAuth 2.0.
This is the access option I need to implement:
-----------------In order to obtain access token with API key, you have to make the following request:
POST /auth/token HTTP/1.1
Host: oauth.wildapricot.org
Authorization: Basic BASE64_ENCODED("APIKEY:YOUR_API_KEY")
Content-type: application/x-www-form-urlencoded
grant_type=client_credentials&scope=auto
-------------------------------So. finally your request will look like:
POST /auth/token HTTP/1.1
Host: oauth.wildapricot.org
Authorization: Basic QVBJS0VZOm85c2U4N3Jnb2l5c29lcjk4MDcwOS0=
Content-type: application/x-www-form-urlencoded
grant_type=client_credentials&scope=auto
I am attempting to make this call with retrofit2, and an okhttp3 interceptor, and getting a bad request response (I am very much new and learning, and have not been able to get anything other response than a 400 bad request (when I use "/auth/token" as the endpoint), or a 404 not found (when I use "/auth/token HTTP/1.1" as the endpoint). If someone could tell me where exactly I am messing this up It would be greatly appreciated, the code I have tried is below.
Interface:
interface WAApiCall {
#POST("auth/token")
fun callPost(#Body body:String ): Call<AuthToken>
}
Call Service:
object WAApiCallService {
private const val API_KEY = "xxxxxxxxIxHavexAxValidxKeyxxxx"
private const val BASE_URL = "https://oauth.wildapricot.org/"
private val AUTH = "Basic" + Base64.encodeToString(API_KEY.toByteArray(), Base64.NO_WRAP)
private const val CONTENT_TYPE = "application/x-www-form-urlencoded"
private var api:WAApiCall? = null
private fun getWAApi(context: Context) : WAApiCall {
if(api==null){
val OkHttpClient = OkHttpClient.Builder()
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BASIC
OkHttpClient.addInterceptor{chain ->
val request = chain.request()
Log.d("CALL", request.body.toString())
val newRequest = request.newBuilder()
.addHeader("Host", "oauth.wildapricot.org")
.addHeader("Authorization", AUTH )
.addHeader("Content-type", CONTENT_TYPE)
.method(request.method, request.body)
.build()
chain.proceed(newRequest)
}
api = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(OkHttpClient.build())
.build()
.create(WAApiCall::class.java)
}
return api!!
}
fun call(context: Context) =
getWAApi(context)
}
Function in Main Activity to make the call:
fun testRequest(){
val call = WAApiCallService.call(this)
call.callPost("grant_type=client_credentials&scope=auto")
.enqueue(object: Callback<AuthToken>{
override fun onFailure(call: Call<AuthToken>, t: Throwable) {
Log.i("FAILURE", t.localizedMessage)
}
override fun onResponse(call: Call<AuthToken>, response: Response<AuthToken>) {
Log.i("SUCCESS", "TOKEN = ${response.body().toString()}")
Log.i("SUCCESS", "${response}")
val token = response.body()?.accessToken
Log.i("SUCCESS", "TOKEN = $token")
}
})
}
Error message:
I/SUCCESS: TOKEN = null
I/SUCCESS: Response{protocol=http/1.1, code=400, message=Bad Request, url=https://oauth.wildapricot.org/auth/token}
I think that I am just not understanding how to implement this type of request in some basic way, I could not get it to work in Postman either. I understand that I need to send the credentials to the authentication server, and receive an access token, that will expire and need to be refreshed, and that It will be included in each actual API endpoint call, I guess I'm just missing something crucial in the most important step of that process (getting the actual token, I am imagining it is a simple, forehead slapping kind of misunderstanding on my part?). The wild apricot API is on swagger hub, and I am able to gain access through that UI, with my API key, and see the responses, so I know that it is valid.
Your client credentials request looks mostly all good. The only thing I can see that looks wrong is no space character in the AUTH header between 'Basic' and the encoded credential.
If that doesn't work, could you trace the HTTP request and verify that you are sending the message you think you are.
Thank you for that observation, it led me to figuring out what ultimately was wrong in my initial attempt. After adding that space, I traced the request and found that It was actually sending two headers for content type.
The fix for that was to set the header in the retrofit call from the interface:
interface WAApiCall {
#POST("auth/token")
fun callPost(#Body Body: okhttp3.RequestBody, #Header("Content-type") type: String): Call<AuthToken>
}
As you can see the body is also slightly different, the call was getting through but was returning:
"unsupported_grant_type".
I was passing a raw string as the body parameter, which was including the quotation marks in the request. The solution there was to pass the okhttp3.Request body type rather than a raw string, in the function that makes the actual call it looks like this:
val body: "grant_type=client_credentials&scope=auto&obtain_refresh_token=true"
val requestBody = RequestBody.create("text/plain".toMediaTypeOrNull(),body)
val call = WAApiCallService.call(this)
call.callPost(requestBody,"application/x-www-form-urlencoded")
.enqueue(object: Callback<AuthToken>{
With those changes the call succeeds and my long running headache is over.