Skip to content

Instantly share code, notes, and snippets.

@nikhiljha
Created July 29, 2016 04:35
Show Gist options
  • Star 60 You must be signed in to star a gist
  • Fork 13 You must be signed in to fork a gist
  • Save nikhiljha/52d45ca69a8415c6990d2a63f61184ff to your computer and use it in GitHub Desktop.
Save nikhiljha/52d45ca69a8415c6990d2a63f61184ff to your computer and use it in GitHub Desktop.
Retrofit2/OkHttp3 Cookies (Drag and Drop, One Size Fits 99%)
// Original written by tsuharesu
// Adapted to create a "drop it in and watch it work" approach by Nikhil Jha.
// Just add your package statement and drop it in the folder with all your other classes.
import android.content.Context;
import android.preference.PreferenceManager;
import android.util.Log;
import java.io.IOException;
import java.util.HashSet;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
* This interceptor put all the Cookies in Preferences in the Request.
* Your implementation on how to get the Preferences may ary, but this will work 99% of the time.
*/
public class AddCookiesInterceptor implements Interceptor {
public static final String PREF_COOKIES = "PREF_COOKIES";
// We're storing our stuff in a database made just for cookies called PREF_COOKIES.
// I reccomend you do this, and don't change this default value.
private Context context;
public AddCookiesInterceptor(Context context) {
this.context = context;
}
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
HashSet<String> preferences = (HashSet<String>) PreferenceManager.getDefaultSharedPreferences(context).getStringSet(PREF_COOKIES, new HashSet<String>());
// Use the following if you need everything in one line.
// Some APIs die if you do it differently.
/*String cookiestring = "";
for (String cookie : preferences) {
String[] parser = cookie.split(";");
cookiestring = cookiestring + parser[0] + "; ";
}
builder.addHeader("Cookie", cookiestring);
*/
for (String cookie : preferences) {
builder.addHeader("Cookie", cookie);
}
return chain.proceed(builder.build());
}
}
// Original written by tsuharesu
// Adapted to create a "drop it in and watch it work" approach by Nikhil Jha.
// Just add your package statement and drop it in the folder with all your other classes.
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import java.io.IOException;
import java.util.HashSet;
import okhttp3.Interceptor;
import okhttp3.Response;
public class ReceivedCookiesInterceptor implements Interceptor {
private Context context;
public ReceivedCookiesInterceptor(Context context) {
this.context = context;
} // AddCookiesInterceptor()
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (!originalResponse.headers("Set-Cookie").isEmpty()) {
HashSet<String> cookies = (HashSet<String>) PreferenceManager.getDefaultSharedPreferences(context).getStringSet("PREF_COOKIES", new HashSet<String>());
for (String header : originalResponse.headers("Set-Cookie")) {
cookies.add(header);
}
SharedPreferences.Editor memes = PreferenceManager.getDefaultSharedPreferences(context).edit();
memes.putStringSet("PREF_COOKIES", cookies).apply();
memes.commit();
}
return originalResponse;
}
}
// By Nikhil Jha
// License MIT
// DON'T ADD THIS FILE TO YOUR OTHER CLASSES
// THIS IS AN EXAMPLE TO TEACH YOU HOW TO MAKE REQUESTS WITH THE MAGIC COOKIES
OkHttpClient client = new OkHttpClient();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addInterceptor(new AddCookiesInterceptor(context)); // VERY VERY IMPORTANT
builder.addInterceptor(new ReceivedCookiesInterceptor(context)); // VERY VERY IMPORTANT
client = builder.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("my-base") // REQUIRED
.client(client) // VERY VERY IMPORTANT
.addConverterFactory(GsonConverterFactory.create())
.build(); // REQUIRED
Your.ThingyClass = retrofit.create(Your.ThingyClass.class);
Call<...> call = yourclass.somecall(new yourclass(example, example));
@l0l3r
Copy link

l0l3r commented Sep 20, 2016

Thanks.

@rakshitsoni02
Copy link

thanks a ton bro !!

Really helpful.

@Savrov
Copy link

Savrov commented Nov 1, 2016

Bro, u just saved me with 36-44 lines from AddCookiesInterceptor class. Thank you very much!

@vicky851
Copy link

i have a problem....when i enter wrong password this code return the old cookie

@smihajlovski
Copy link

Thank you!

@iosifnicolae2
Copy link

Thank you very much!

@aefn
Copy link

aefn commented May 17, 2017

Hi,
I have a problem with this in AddCookiesInterceptor.java :
for (String cookie : preferences) { builder.addHeader("Cookie", cookie); }

I used Log.v to see that it works or not. it shows everything in the log but my request does not have the cookies.
even I change it to something like this:
int i =0; for (String cookie : preferences) { builder.addHeader("header"+i, cookie); Log.v("OkHttp", "Adding Header: " + cookie); // This is done so I know which headers are being added; this interceptor is used after the normal logging of OkHttp i++; }
to see what is happening and again I don't get these values in my header.
could you help me how to solve this problem?

other things work perfectly and thank you for your detailed files.

@arvi
Copy link

arvi commented May 30, 2017

Thank you so muuuuuch! For some reason, answer from SO https://stackoverflow.com/a/36865953/2098493 didn't work for me and also https://github.com/franmontiel/PersistentCookieJar.

Your solution persists server request cookie even if app is destroyed! :) I'm so happy. Finally found the right one.


Update:
For some reason, this solution has issues with a nodejs project using passport. The socket.handshake.session does not store passport user even if user is already logged in. I had to use another persistent cookie store solution.

Now, this is what really worked for my project. https://gist.github.com/lezorich/8f3f3a54f07515881581.

@spookyuser
Copy link

spookyuser commented Jul 16, 2017

A thing I wanted to add is if you are using this for jsessionid's then you could also add a separate time string to the sharedpreferences every time you save a cookie and just check if the time passed is smaller than the expiry time of the token that way you can avoid a lot of unnecessary logins.

EDIT: Like this

@thushan83
Copy link

Perfect, Thanks a lot

@iamrajendra
Copy link

iamrajendra commented Oct 31, 2017

अच्छे से काम किया कर भाई

@gotev
Copy link

gotev commented Jan 3, 2018

Kotlin version, with helper method to create OkHttp client and Retrofit instance. May be useful ;)

CookieStore.kt

import android.content.Context
import android.preference.PreferenceManager
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import java.io.IOException

private val cookiesKey = "appCookies"

class SendSavedCookiesInterceptor(private val context: Context) : Interceptor {

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val builder = chain.request().newBuilder()
        val preferences = PreferenceManager
                .getDefaultSharedPreferences(context)
                .getStringSet(cookiesKey, HashSet()) as HashSet<String>

        preferences.forEach {
            builder.addHeader("Cookie", it)
        }

        return chain.proceed(builder.build())
    }
}

class SaveReceivedCookiesInterceptor(private val context: Context) : Interceptor {

    @JvmField
    val setCookieHeader = "Set-Cookie"

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val originalResponse = chain.proceed(chain.request())

        if (!originalResponse.headers(setCookieHeader).isEmpty()) {
            val cookies = PreferenceManager
                    .getDefaultSharedPreferences(context)
                    .getStringSet(cookiesKey, HashSet()) as HashSet<String>

            originalResponse.headers(setCookieHeader).forEach {
                cookies.add(it)
            }

            PreferenceManager
                    .getDefaultSharedPreferences(context)
                    .edit()
                    .putStringSet(cookiesKey, cookies)
                    .apply()
        }

        return originalResponse
    }

}

fun OkHttpClient.Builder.setCookieStore(context: Context) : OkHttpClient.Builder {
    return this
            .addInterceptor(SendSavedCookiesInterceptor(context))
            .addInterceptor(SaveReceivedCookiesInterceptor(context))
}

RetrofitHelper.kt

private val httpConnectTimeoutSeconds = 10
private val httpWriteTimeoutSeconds = 10
private val httpReadTimeoutSeconds = 10

val userAgent = "Your User Agent"

fun newHttpClient(context: Context): OkHttpClient {
    val loggingInterceptor = HttpLoggingInterceptor { Log.d("OkHttp", it) }

    loggingInterceptor.level = if (BuildConfig.DEBUG)
        HttpLoggingInterceptor.Level.BODY // debug log level
    else
        HttpLoggingInterceptor.Level.NONE  // release log level

    return OkHttpClient.Builder()
            .followRedirects(true)
            .followSslRedirects(true)
            .retryOnConnectionFailure(true)
            .setCookieStore(context)
            .addInterceptor(loggingInterceptor)
            .addNetworkInterceptor { chain ->
                val request = chain.request().newBuilder()
                        .header("User-Agent", userAgent)
                        .build()
                chain.proceed(request)
            }
            .cache(null)
            .connectTimeout(httpConnectTimeoutSeconds.toLong(), TimeUnit.SECONDS)
            .writeTimeout(httpWriteTimeoutSeconds.toLong(), TimeUnit.SECONDS)
            .readTimeout(httpReadTimeoutSeconds.toLong(), TimeUnit.SECONDS)
            .build()
}

fun <T> newRetrofit(context: Context, baseUrl: String, service: Class<T>): T {

    val retrofit = Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .client(newHttpClient(context))
            .build()

    return retrofit.create(service)
}

Then, for example in your application subclass, you can write:

class App: Application() {

    val apiClient by lazy {
        newRetrofit(this, "https://www.yourserver.com/api", YourAPI::class.java)
    }

    override fun onCreate() {
        super.onCreate()

        Log.i("App start", "App initialization")

        // Do some other initializations
    }
}

@JoaoMotondon
Copy link

Thanks @gotev. Very useful classes.

@makiabuan
Copy link

Hi, it is kinda old so reply here. But I have one question. Do I have to call AddCookiesInterceptor and ReceiveCookiesInterceptor every time I have a query?

@dabitdev
Copy link

@makiabuan the interceptors needs to be added once in the http client. Make sure that all requests are using the same okhttp client.
You do not need to call anything. The interceptor does the work by itself.

@congtieniuh
Copy link

Thank you very much, This work for me.

@mustaphamichael
Copy link

Lifesaver !! Thank you.

@banmarkovic
Copy link

Anyone has problem on Android Pie with these interceptors? It seems that they are not triggered on Android 9, on other versions it works perfectly..

@banmarkovic
Copy link

It seems that the problem was in CertificateFactory.getInstance("X.509", "BC"), X.509 is not found for Android 9. CertificateFactory.getInstance("X509") is the fix.

@sandeepGLS
Copy link

sandeepGLS commented May 29, 2019

Thanks, Worked for me

@dileep57
Copy link

Thanks Bro, its working.. :)

@Sourav242
Copy link

Great help. Thanks Bro.

@aqibshxhzd
Copy link

yo bro thanks ,,, its working, you saved my ass :D

@Dilmurod724
Copy link

Great help. Thanks

@murraygunn
Copy link

I'm using hilt and am struggling to make 'context' available. Any suggestions?

@murraygunn
Copy link

I got it. My problem was trying to use @ApplicationContext as is (@ApplicationContext context: ApplicationContext). It works fine if you leave it as Context (@ApplicationContext context: Context)

I've pulled everything together for a Hilt-based solution at https://stackoverflow.com/questions/67950681/using-cookies-with-retrofit-and-hilt-and-recommended-architecture

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment