3

I have an android studio application that connects to a nodejs backend server with user authentication. I can log in and register from my app but it does not store a session. So I can not get session based functionality yet.

I need to add functionality to store a session. For this how do I do this with the retrofit interface. I want to log in start a session so I can have user logged in access to other routes on the server.

Or is there another interface for android studio that will allow for cookies and sessions? Retrofit interface


public interface RetrofitInterface {

            
    @POST("/login")
    Call<Login_result> executeLogin(@Body HashMap<String, String> map);
            
    @POST("/signup")
    Call<Void> executeSignup(@Body HashMap<String, String>map);
            
    @POST("/add_data")
    Call<Void> executeAdd_data(@Body HashMap<String, String>map);
            
    @POST("/logout")
    Call<Void> executeLogout(@Body HashMap<String, String>map);
            
    @GET("/test")
    Call<Void> executeTest();

}
**Main code**

```java


/*Updated this*/
    Context context = this;

        retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(new OkhttpClient.builder()
                        .addInterceptor(new ReceivedCookiesInterceptor(context)
                                .addInterceptor(new AddCookiesInterceptor(context)
                                ).build())
                        .addConverterFactory(GsonConverterFactory.create())
                        .build();
        retrofitInterface = retrofit.create(RetrofitInterface.class);

Log in code


    HashMap<String,String> map = new HashMap<>();
                //map.put("email",emailEdit.getText().toString());//
                map.put("username", usernameEdit.getText().toString());
                map.put("password", passwordEdit.getText().toString());

                Call<Login_result> call = 
                retrofitInterface.executeLogin(map);//Run the post
                call.enqueue(new Callback<Login_result>()
                {

                    @Override
                    public void onResponse(Call<Login_result> call, Response<Login_result> response) {
                        if(response.code() == 200)
                        {
                            /*Login_result result = response.body();
                            AlertDialog.Builder builder1 = new AlertDialog.Builder(MainActivity.this);
                            builder1.setTitle(result.getUsernname());
                            builder1.setMessage(result.getEmail());

                            builder1.show();*/
                            Toast.makeText(MainActivity.this, "Logged in", Toast.LENGTH_SHORT).show();


                        }else if(response.code() == 404)
                        {
                            Toast.makeText(MainActivity.this, "Incorrect usernanme or password", Toast.LENGTH_SHORT).show();
                        }
                    }

                    @Override
                    public void onFailure(Call<Login_result> call, Throwable t) {
                        Toast.makeText(MainActivity.this, t.getMessage(),Toast.LENGTH_LONG).show();
                    }
                });
Thomas Morris
  • 794
  • 5
  • 26
  • does this questions ans is switable for you? https://stackoverflow.com/questions/38418809/add-cookies-to-retrofit-2-request – Jyotish Biswas Jul 28 '20 at 09:29

1 Answers1

5

You would need to create an two interceptors and store the cookie information in Shared Preferences

public class ReceivedCookiesInterceptor implements Interceptor {

    private Context context;
    public ReceivedCookiesInterceptor(Context context) {
        this.context = context;
    }

    @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;
    }
}

And then reverse to add cookies to the outgoing request

public class AddCookiesInterceptor implements Interceptor {

    public static final String PREF_COOKIES = "PREF_COOKIES";
    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>());

        Request original = chain.request();
        if(original.url().toString().contains("distributor")){
            for (String cookie : preferences) {
                builder.addHeader("Cookie", cookie);
            }
        }

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

Which then you would need to change your Retrofit instance to the below

        retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(new OkhttpClient.builder()
                        .addInterceptor(new ReceivedCookiesInterceptor(context)
                        .addInterceptor(new AddCookiesInterceptor(context) 
                ).build())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        retrofitInterface = retrofit.create(RetrofitInterface.class);
Scott Johnson
  • 707
  • 5
  • 13
  • I presume the interceptors will be their own class files and just getting imported to the main code when I call my retrofit interface? – Thomas Morris Jul 28 '20 at 09:34
  • @ThomasMorris Yeah – Scott Johnson Jul 28 '20 at 09:35
  • My issue is in my retrofit builder I dont have the class okhttpClient is that an import? – Thomas Morris Jul 28 '20 at 09:36
  • Add these two to your build.gradle ` //okhttp implementation 'com.squareup.okhttp3:okhttp:4.4.0' implementation 'com.squareup.okhttp3:logging-interceptor:4.4.0'` – Scott Johnson Jul 28 '20 at 09:37
  • Still says okhtppClient is missing and need to create class. How do I import it? Also the context i presume needs changing to this? – Thomas Morris Jul 28 '20 at 09:41
  • @ThomasMorris No you need the context to access the Shared Preferences To import add //okhttp implementation 'com.squareup.okhttp3:okhttp:4.4.0' <-- main implementation 'com.squareup.okhttp3:logging-interceptor:4.4.0' <--- interceptors to see your cookies etc, if you wanted – Scott Johnson Jul 28 '20 at 09:55
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/218746/discussion-between-thomas-morris-and-tdiscott). – Thomas Morris Jul 28 '20 at 10:02
  • `android.preference.PreferenceManager` is deprecated in favor of `androidx.preference.PreferenceManager` as mentioned here: https://stackoverflow.com/a/56833739/442665 – jbobbins Dec 17 '22 at 19:11