From d4429ca62e8d15c90067241199f58bbbea763dfb Mon Sep 17 00:00:00 2001 From: paul sandjong Date: Thu, 16 Apr 2026 16:55:10 +0100 Subject: [PATCH] Fix: Django session handling --- .../org/openimis/imispolicies/Global.java | 9 +++ .../okhttp/AuthorizationInterceptor.java | 10 ++- .../network/util/OkHttpUtils.java | 9 +++ .../network/util/PersistentCookieJar.java | 63 +++++++++++++++++++ .../repository/LoginRepository.java | 22 ++++++- 5 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/org/openimis/imispolicies/network/util/PersistentCookieJar.java diff --git a/app/src/main/java/org/openimis/imispolicies/Global.java b/app/src/main/java/org/openimis/imispolicies/Global.java index ef2a1631..24536583 100644 --- a/app/src/main/java/org/openimis/imispolicies/Global.java +++ b/app/src/main/java/org/openimis/imispolicies/Global.java @@ -46,6 +46,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.openimis.imispolicies.network.util.PersistentCookieJar; import org.openimis.imispolicies.repository.LoginRepository; import org.openimis.imispolicies.tools.Log; import org.openimis.imispolicies.util.StreamUtils; @@ -99,6 +100,7 @@ public class Global extends Application { private String AppDirectory; private Map SubDirectories; + private PersistentCookieJar cookieJar; private volatile LoginRepository loginRepository; public static Global getGlobal() { return GlobalContext; @@ -116,6 +118,13 @@ public void onCreate() { initSharedPrefsInts(); } + public void setCookieJar(PersistentCookieJar jar) { + this.cookieJar = jar; + } + + public PersistentCookieJar getCookieJar() { + return cookieJar; + } protected boolean isRunningTest() { try { Class.forName("org.robolectric.RobolectricTestRunner"); diff --git a/app/src/main/java/org/openimis/imispolicies/network/okhttp/AuthorizationInterceptor.java b/app/src/main/java/org/openimis/imispolicies/network/okhttp/AuthorizationInterceptor.java index b8a7af2a..4c9d4b3f 100644 --- a/app/src/main/java/org/openimis/imispolicies/network/okhttp/AuthorizationInterceptor.java +++ b/app/src/main/java/org/openimis/imispolicies/network/okhttp/AuthorizationInterceptor.java @@ -3,6 +3,7 @@ import androidx.annotation.NonNull; import org.apache.commons.lang3.StringUtils; +import org.openimis.imispolicies.Global; import org.openimis.imispolicies.MainActivity; import org.openimis.imispolicies.repository.LoginRepository; @@ -12,6 +13,7 @@ import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; +import okhttp3.ResponseBody; public class AuthorizationInterceptor implements Interceptor { private static final String USER_AGENT = "mobile_app"; @@ -38,8 +40,14 @@ public Response intercept(@NonNull Chain chain) throws IOException { builder.addHeader("X-Csrftoken", csrfToken); } Response response = chain.proceed(builder.build()); - if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) { + ResponseBody body = response.peekBody(Long.MAX_VALUE); + String bodyString = body.string(); + if (bodyString.contains("'csrftoken'") || response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) { repository.saveFhirToken(null, null, null); + repository.saveCsrfToken(null); + if (Global.getGlobal().getCookieJar() != null) { + Global.getGlobal().getCookieJar().clear(); + } MainActivity.SetLoggedIn(); } return response; diff --git a/app/src/main/java/org/openimis/imispolicies/network/util/OkHttpUtils.java b/app/src/main/java/org/openimis/imispolicies/network/util/OkHttpUtils.java index d9eda10f..3c061ef7 100644 --- a/app/src/main/java/org/openimis/imispolicies/network/util/OkHttpUtils.java +++ b/app/src/main/java/org/openimis/imispolicies/network/util/OkHttpUtils.java @@ -1,6 +1,11 @@ package org.openimis.imispolicies.network.util; +import static android.content.Context.MODE_PRIVATE; + +import static org.openimis.imispolicies.Global.PREF_NAME; + import android.annotation.SuppressLint; +import android.content.SharedPreferences; import androidx.annotation.NonNull; @@ -30,6 +35,10 @@ public static OkHttpClient getDefaultOkHttpClient() { synchronized (OkHttpUtils.class) { if (client == null) { OkHttpClient.Builder builder = new OkHttpClient.Builder(); + PersistentCookieJar cookieJar = + new PersistentCookieJar(Global.getGlobal().getSharedPreferences(PREF_NAME, MODE_PRIVATE)); + Global.getGlobal().setCookieJar(cookieJar); + builder.cookieJar(cookieJar); HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); interceptor.setLevel(BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.BASIC); builder.addInterceptor(interceptor); diff --git a/app/src/main/java/org/openimis/imispolicies/network/util/PersistentCookieJar.java b/app/src/main/java/org/openimis/imispolicies/network/util/PersistentCookieJar.java new file mode 100644 index 00000000..100c8a6f --- /dev/null +++ b/app/src/main/java/org/openimis/imispolicies/network/util/PersistentCookieJar.java @@ -0,0 +1,63 @@ +package org.openimis.imispolicies.network.util; + +import android.content.SharedPreferences; + +import androidx.annotation.NonNull; + +import okhttp3.Cookie; +import okhttp3.CookieJar; +import okhttp3.HttpUrl; + +import java.util.ArrayList; +import java.util.List; + +public class PersistentCookieJar implements CookieJar { + + private final SharedPreferences prefs; + + public PersistentCookieJar(SharedPreferences prefs) { + this.prefs = prefs; + } + + @Override + public void saveFromResponse(@NonNull HttpUrl url, List cookies) { + + for (Cookie cookie : cookies) { + if ("openimis_session".equals(cookie.name())) { + + prefs.edit() + .putString("session_value", cookie.value()) + .putLong("session_expiry", cookie.expiresAt()) + .apply(); + } + } + } + + @NonNull + @Override + public List loadForRequest(HttpUrl url) { + + String value = prefs.getString("session_value", null); + long expiry = prefs.getLong("session_expiry", -1); + + if (value == null || expiry == -1) { + return new ArrayList<>(); + } + + Cookie cookie = new Cookie.Builder() + .name("openimis_session") + .value(value) + .domain(url.host()) + .path("/") + .expiresAt(expiry) + .build(); + + List cookies = new ArrayList<>(); + cookies.add(cookie); + return cookies; + } + + public void clear() { + prefs.edit().clear().apply(); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/openimis/imispolicies/repository/LoginRepository.java b/app/src/main/java/org/openimis/imispolicies/repository/LoginRepository.java index fe33b4ed..bd6616de 100644 --- a/app/src/main/java/org/openimis/imispolicies/repository/LoginRepository.java +++ b/app/src/main/java/org/openimis/imispolicies/repository/LoginRepository.java @@ -1,5 +1,8 @@ package org.openimis.imispolicies.repository; +import static android.content.Context.MODE_PRIVATE; +import static org.openimis.imispolicies.Global.PREF_NAME; + import android.content.Context; import android.content.SharedPreferences; import android.util.Base64; @@ -13,6 +16,7 @@ import org.openimis.imispolicies.BuildConfig; import org.openimis.imispolicies.Global; import org.openimis.imispolicies.Token; +import org.openimis.imispolicies.network.util.PersistentCookieJar; import org.openimis.imispolicies.tools.Log; import java.util.Date; @@ -37,7 +41,7 @@ public LoginRepository(@NonNull Context context) { public LoginRepository(@NonNull Context context, boolean isPaymentEnabled) { this.isPaymentEnabled = isPaymentEnabled; - prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); + prefs = context.getSharedPreferences(PREFS_NAME, MODE_PRIVATE); if (!prefs.getBoolean(HAS_MIGRATED, false)) { migrateOldTokens(); } @@ -167,7 +171,21 @@ public boolean isLoggedIn() { if (isPaymentEnabled && getRestToken() == null) { return false; } - return getFhirToken() != null; + + PersistentCookieJar cookieJar = Global.getGlobal().getCookieJar(); + long expiry = Global.getGlobal().getSharedPreferences(PREF_NAME, MODE_PRIVATE) + .getLong("session_expiry", 0); + + boolean isLoggedIn = getFhirToken() != null + && expiry > System.currentTimeMillis(); + + if (!isLoggedIn) { + if (cookieJar != null) { + cookieJar.clear(); + } + } + + return isLoggedIn; } public void logout() {