Retrofit2 Authorization - Global Interceptor for access token - android

I'm trying to use Retrofit2,
I want to add Token to my Header Like this:
Authorization: Bearer Token
but the code below doesn't work:
public interface APIService {
#Headers({"Authorization", "Bearer "+ token})
#GET("api/Profiles/GetProfile?id={id}")
Call<UserProfile> getUser(#Path("id") String id);
}
My server is asp.net webApi. Please help what should I do?

You have two choices -- you can add it as a parameter to your call --
#GET("api/Profiles/GetProfile?id={id}")
Call<UserProfile> getUser(#Path("id") String id, #Header("Authorization") String authHeader);
This can be a bit annoying because you will have to pass in the "Bearer" + token on each call. This is suitable if you don't have very many calls that require the token.
If you want to add the header to all requests, you can use an okhttp interceptor --
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request().newBuilder()
.addHeader("Authorization", "Bearer " + token)
.build();
return chain.proceed(newRequest);
}
}).build();
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(/** your url **/)
.addConverterFactory(GsonConverterFactory.create())
.build();

If you want to add Bearer Token as a Header you can do those types of process.
This is one way to work with Bearer Token
In your Interface
#Headers({ "Content-Type: application/json;charset=UTF-8"})
#GET("api/Profiles/GetProfile")
Call<UserProfile> getUser(#Query("id") String id, #Header("Authorization") String auth);
After that you will call the Retrofit object in this way
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("your Base URL")
.addConverterFactory(GsonConverterFactory.create())
.build();
APIService client = retrofit.create(APIService.class);
Call<UserProfile> calltargetResponse = client.getUser("0034", "Bearer "+token);
calltargetResponse.enqueue(new Callback<UserProfile>() {
#Override
public void onResponse(Call<UserProfile> call, retrofit2.Response<UserProfile> response) {
UserProfile UserResponse = response.body();
Toast.makeText(this, " "+response.body(), Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<UserProfile> call, Throwable t) {
//Toast.makeText(this, "Failed ", Toast.LENGTH_SHORT).show();
}
});
Another Way is using intercept, which is similar the previous Answer. But, that time you just need to modify interface little bit like.
#Headers({ "Content-Type: application/json;charset=UTF-8"})
#GET("api/Profiles/GetProfile")
Call<UserProfile> getUser(#Query("id") String id);
Hope this will work for you.

Based on #iagreen solution kotlin version with different classes and structure suggested by #Daniel Wilson
Make Retrofit instance like this
object RetrofitClientInstance {
private var retrofit: Retrofit? = null
private val BASE_URL = "http://yoururl"
val retrofitInstance: Retrofit?
get() {
if (retrofit == null) {
var client = OkHttpClient.Builder()
.addInterceptor(ServiceInterceptor())
//.readTimeout(45,TimeUnit.SECONDS)
//.writeTimeout(45,TimeUnit.SECONDS)
.build()
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit
}
}
Add ServiceInterceptor class like below
class ServiceInterceptor : Interceptor{
var token : String = "";
fun Token(token: String ) {
this.token = token;
}
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
if(request.header("No-Authentication")==null){
//val token = getTokenFromSharedPreference();
//or use Token Function
if(!token.isNullOrEmpty())
{
val finalToken = "Bearer "+token
request = request.newBuilder()
.addHeader("Authorization",finalToken)
.build()
}
}
return chain.proceed(request)
}
}
Login Interface and data class implementation
interface Login {
#POST("Login")
#Headers("No-Authentication: true")
fun login(#Body value: LoginModel): Call<LoginResponseModel>
#POST("refreshToken")
fun refreshToken(refreshToken: String):
Call<APIResponse<LoginResponseModel>>
}
data class LoginModel(val Email:String,val Password:String)
data class LoginResponseModel (val token:String,val
refreshToken:String)
call this in any activity like this
val service = RetrofitClientInstance.retrofitInstance?.create(Login::class.java)
val refreshToken = "yourRefreshToken"
val call = service?.refreshToken(refreshToken)
call?.enqueue(object: Callback<LoginResponseModel>{
override fun onFailure(call: Call<LoginResponseModel>, t: Throwable) {
print("throw Message"+t.message)
Toast.makeText(applicationContext,"Error reading JSON",Toast.LENGTH_LONG).show()
}
override fun onResponse(call: Call<LoginResponseModel>, response: Response<LoginResponseModel>) {
val body = response?.body()
if(body!=null){
//do your work
}
}
})
for detail this video will be helpful.

This adds your token to the builder and you can change it at any time in case of login/logout.
object ApiService {
var YOUR_TOKEN = ""
private var retrofit: Retrofit = Retrofit.Builder()
.baseUrl("YOUR_URL")
.addConverterFactory(GsonConverterFactory.create())
.client(OkHttpClient.Builder().addInterceptor { chain ->
val request = chain.request().newBuilder().addHeader("Authorization", "Bearer ${YOUR_TOKEN}").build()
chain.proceed(request)
}.build())
.build()
var service: AppAPI = retrofit.create(AppAPI::class.java)
private set
}

You will need to add an Interceptor into the OkHttpClient.
Add a class called OAuthInterceptor.
class OAuthInterceptor(private val tokenType: String, private val accessToken: String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
var request = chain.request()
request = request.newBuilder().header("Authorization", "$tokenType $accessToken").build()
return chain.proceed(request)
}
}
Following that, when you initialise your RetrofitApiService interface, you will need this.
interface RetrofitApiService {
companion object {
private const val BASE_URL = "https://api.coursera.org/api/businesses.v1/"
fun create(accessToken: String): RetrofitApiService {
val client = OkHttpClient.Builder()
.addInterceptor(OAuthInterceptor("Bearer", accessToken))
.build()
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.client(client)
.build()
return retrofit.create(RetrofitApiService::class.java)
}
}
}
Shout out to Java Code Monk, and visit the reference link for more details.
https://www.javacodemonk.com/retrofit-oauth2-authentication-okhttp-android-3b702350

The best approach is to use the new Authenticator API.
class TokenAuthenticator : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
if (response.request.header("Authorization") != null) {
return null
}
return response.request.newBuilder().header("Authorization", "Bearer " + token).build()
}
}
OkHttpClient.Builder().authenticator(TokenAuthenticator()).build()
Reference: https://square.github.io/okhttp/recipes/#handling-authentication-kt-java

Related

When I use retrofit2, the app is just shut down

I used retrofit2 to call API. But, when I called it, my app was just shut down. There are not errors in the Logcat. I googled it, but there is not a solution.
And Retrofit2 and converter-gson version is 2.9.0. I set internet permission in AndroidManifest.xml
MainActivity.kt
private fun loadMembers() {
val retrofit = Retrofit.Builder()
.baseUrl(MemberAPI.base_domain)
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofitService = retrofit.create(MemberInterface::class.java)
retrofitService
.getMember(Constants.api_key)
.enqueue(object: Callback<Member> {
override fun onResponse(call: Call<Member>, response: Response<Member>) {
val members = response.body() as Member
var names = ""
for (member in members.response.body.items.item) {
names += "\n${member.empNm}"
}
binding.text.text = names
}
override fun onFailure(call: Call<Member>, t: Throwable) {
Toast.makeText(baseContext, "실패", Toast.LENGTH_LONG).show()
}
})
}
Interface.kt
interface MemberInterface {
#GET("{api_key}&numOfRows=5&pageNo=1&_type=json")
fun getMember(#Path("api_key") Key: String): Call<Member>
}
Youtube
Video
Try to add an interceptor so you can see all calls logs (headers, body, URLs, etc...). The crash could be related with the parse of the JSON response to the object Member.
Add OkHtpp to your grade dependencies:
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.2"
implementation "com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2"
And after that, when you create your Retrofit instance, add the interceptor, should look something like this:
val httpClient = OkHttpClient.Builder()
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
httpClient.addInterceptor(interceptor)
httpClient.addInterceptor(Interceptor { chain: Interceptor.Chain ->
val original: Request = chain.request()
val request: Request = original.newBuilder()
.header("Content-Type", "application/json")
.method(original.method, original.body)
.build()
chain.proceed(request)
})
val okHttpClient = httpClient.build()
val retrofit = Retrofit.Builder()
.baseUrl(MemberAPI.base_domain)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()

How to read Retrofit response in Repository pattern with Coroutines

I am currently following an MVVM and Repository Architecture tutorial in Android, and because of the abstractions in the tutorial codes I came across, there are some things I don't understand.
Although the code works fine (compiles without any issue), the abstraction makes it hard for me to understand how to handle Retrofit responses.
Repository
suspend fun login(username: String, password: String) { // LiveData<List<AuthModel>> {
withContext(Dispatchers.IO) {
val auth = Network.retrofitInstance?.create(AuthService::class.java)
val i = auth?.doLoginAsync(username, password)?.await()
if (i != null) {
database.authDao.updateAuthInfo(*i.asDatabaseModel())
}
}
//return auth
}
}
Interface
interface ServiceSectionDao{
#POST("login")
fun doLoginAsync(
#Query("user_id") userName: String,
#Query("password") passWord: String
): Deferred<NetworkAuthContainer>
}
Retrofit Object
object Network {
private var retrofit: Retrofit? = null
private val BASE_URL = "http://10.0.2.2:8081/api/"
val retrofitInstance: Retrofit?
get() {
if (retrofit == null) {
var client = OkHttpClient.Builder()
.addInterceptor(RequestInterceptor())
.build()
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()
}
return retrofit
}
Request Interceptor
class RequestInterceptor : Interceptor {
var token = "FmQ54sSLm0H0P83EFU7fB2k9flgs5fXQNBS7EgtH";
fun Token(token: String) {
this.token = token;
}
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
if (request.header("No-Authentication") == null) {
//val token = getTokenFromSharedPreference();
//or use Token Function
if (!token.isNullOrEmpty()) {
val finalToken = "Bearer " + token
request = request.newBuilder()
.addHeader("Authorization", finalToken)
.build()
}
}
return chain.proceed(request)
}
}
What I get from above val i = auth?.doLoginAsync(username, password)?.await() is already a NetworkAuthContainer, I don't have a chance to check for any HTTP status code.
During debug I can see a result object returned by Retrofit, but I am not able to process it because I do not know where in the code I am supposed to read the values should it be on the RequestInterceptor, or Repository(login) and how?
First problem with your code is that you are using deprecated CoroutineCallAdapterFactory, starting from version 2.6 Retrofit now has native support for kotlin suspend functions. so first thing that you should do is update your Retrofit depenency to latest version.
Once you do that, then you will be able to update your doLoginAsync to
#POST("login")
suspend fun doLogin( // use of suspend modifier
#Query("user_id") userName: String,
#Query("password") passWord: String): NetworkAuthContainer // no need to use Deffered
Now to get HTTP status code from response you can update the return type of doLogin to retrofit2.Response<NetworkAuthContainer> and in your repository
val response = auth!!.doLoginAsync(username, password) // This is HTTP response
if (response.code() == 200) {
// To get NetworkAuthContainer call body()
database.authDao.updateAuthInfo(*response.body().asDatabaseModel())
}
else{
// Handle different response codes
}

Special character = gets converted into %3D after calling API using retrofit

I want to pass = sign in request params as string. It coverts into %3D upon calling api.
You need to use custom interceptor as below:
class MyInterceptor: Interceptor {
override fun intercept (chain: Interceptor.Chain): Response {
val request = chain.request()
val unEncodedStringUrl = request.url.toString().replace("%3D", "=")
var newRequest = request.newBuilder().url(unEcodedStringUrl).build()
return chain.proceed(newRequest)
}
}
And then use it for OkHttpClient of the retrofit:
fun getOkHttpClient(
myInterceptor: MyInterceptor,
loggingInterceptor: HttpLoggingInterceptor
): OkHttpClient {
return OkHttpClient().newBuilder().addInterceptor(myInterceptor).also {
if (BuildConfig.DEBUG) {
it.addInterceptor(loggingInterceptor)
}
}.build()
}
Finally, use this OkHttpClient for retrofit as below:
fun getRetrofit(moshi: Moshi): Retrofit {
return Retrofit.Builder()
.baseUrl(StaticConstants.baseUrl)
.client(getOkHttpClient())
.addConverterFactory(
MoshiConverterFactory.create(moshi)
)
.build()
}

I am using Retrofit 2.0. I want to handle all types of network errors and exceptions

my application crashes when I have no internet connection : I am looking for a method that handles any exception form the retrofit instance like server is not found exception Timeout No internet connection
RequestRepository : my repository which contain all my functions
class RequestRepository {
/** suspend function to get the result of token request*/
suspend fun getToken(userLoginModel: UserLoginModel): Response<TokenResponse> {
return ApiService.APILogin.getToken(userLoginModel)
}
ApiService : contain my Retofit instance
object ApiService {
private var token: String = ""
fun setToken(tk: String) {
token = tk
}
private val okHttpClient = OkHttpClient.Builder().connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS).addInterceptor { chain ->
val chainRequest = chain.request()
val requestBuilder = chainRequest.newBuilder()
.addHeader("authorization", "Token $token")
.method(chainRequest.method, chainRequest.body)
val request = requestBuilder.build()
chain.proceed(request)
}.build()
var gson = GsonBuilder()
.setLenient()
.create()
private val retrofit by lazy {
Retrofit.Builder()
.baseUrl(LOGIN_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(okHttpClient)
.build()
}
val API: WebServicesApi by lazy {
retrofit.create(WebServicesApi::class.java)
}
WebServicesApi : my interface which contain my requests
interface WebServicesApi {
/** get the token from the API*/
#POST("user/login/")
suspend fun getToken(#Body userLoginModel: UserLoginModel): Response<TokenResponse>
}
LoginViewModel : my viewModel class
class LoginViewModel(private val repository: RequestRepository) : ViewModel() {
var tokenResponse: MutableLiveData<Response<TokenResponse>> = MutableLiveData()
/** using coroutine in getToken function to get the token */
fun getToken(userLoginModel: UserLoginModel) {
viewModelScope.launch(Dispatchers.IO) {
val tResponse = repository.getToken(userLoginModel)
tokenResponse.postValue(tResponse)
Log.d(TAG, "getToken: ${userLoginModel.password}")
}
}
}
You can add a Interceptor for handle error like this:
class GlobalErrorInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
try {
val response = chain.proceed(request)
if (!response.isSuccessful) {
val statusCode = response.code
when (statusCode) {
//Your handle status code in here
}
}
return response
} catch (ex: IOException) {
// You can replace my code with your exception handler code
return Response.Builder().request(chain.request()).protocol(Protocol.HTTP_1_1)
.message("Can't connect!").code(500).body(
ResponseBody.create(
"application/json; charset=utf-8".toMediaTypeOrNull(),
""
)
).build()
}
}
}
And you must add this class to OkHttpBuider:
val httpBuilder = OkHttpClient.Builder()
......
httpBuilder.addInterceptor(GlobalErrorInterceptor())

Retrofit Interceptor not adding query param to URL

I'm trying to add apikey in the URL using custom interceptor but it's not adding the params in the URL so response body is null.
CustomInterceptor
class CustomInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val url = chain.request().url().newBuilder()
.addQueryParameter("apiKey", API_KEY)
.build()
val request = chain.request().newBuilder()
.url(url)
.build()
return chain.proceed(request)
}
}
Client
class Client {
companion object {
const val API_KEY = "123123"
private const val apiUrl = "https://www.omdbapi.com/"
fun <T> create(service: Class<T>): T {
val client = OkHttpClient.Builder()
.addInterceptor(CustomInterceptor())
.build()
return Retrofit.Builder()
.baseUrl(apiUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
.create(service)
}
}
}
IMovie
interface IMovie {
#GET("/")
fun searchMovie(#Query("s") query: String): Call<SearchResult>
}
After sending the request the response body is null and this is the
Actual URL:- https://www.omdbapi.com/?s=Man
Expected URL:- https://www.omdbapi.com/?s=Man&apikey=123123
First create a new httpUrl instance from the existing request adding your query parameter and value:
var request = chain.request()
val httpUrl = request.url().newBuilder().addQueryParameter("token", authToken).build()
Then update the request:
request = request.newBuilder().url(httpUrl).build()
and proceed with it:
return chain.proceed(request)
When you recall the request from the chain (the one you proceed with after manipulation) you are getting the unmodified request again.

Categories

Resources