diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 9adfcd81be5dfc08ad6ede46e813d0d750393f6e..1c1e3726accbfd644fb93cc96b67bd8154e8063c 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -48,6 +48,8 @@ dependencies {
     implementation("androidx.navigation:navigation-fragment-ktx:$navVersion")
     implementation("androidx.navigation:navigation-ui-ktx:$navVersion")
     implementation("com.google.android.material:material:1.9.0")
+    implementation("com.squareup.retrofit2:retrofit:2.9.0")
+    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
     testImplementation("junit:junit:4.13.2")
     androidTestImplementation("androidx.test.ext:junit:1.1.5")
     androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e105b0df59996aa47658435961e0d68ccf14db54..8beb467f97e42f1076585f61954e3ac9ff8c3419 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,7 +2,10 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools">
 
+    <uses-permission android:name="android.permission.INTERNET" />
+
     <application
+        android:name=".BondowowoApp"
         android:allowBackup="true"
         android:dataExtractionRules="@xml/data_extraction_rules"
         android:fullBackupContent="@xml/backup_rules"
@@ -14,8 +17,12 @@
         tools:targetApi="31">
         <activity
             android:name=".MainActivity"
-            android:label="@string/app_name"
-            android:exported="true">
+            android:exported="false"
+            android:theme="@style/AppTheme.NoActionBar"/>
+        <activity
+            android:name=".LoginActivity"
+            android:exported="true"
+            android:theme="@style/AppTheme.NoActionBar">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/app/src/main/java/com/atm/bondowowo/BondowowoApp.kt b/app/src/main/java/com/atm/bondowowo/BondowowoApp.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c52591accbab03846ed52308e2396d774068895a
--- /dev/null
+++ b/app/src/main/java/com/atm/bondowowo/BondowowoApp.kt
@@ -0,0 +1,41 @@
+package com.atm.bondowowo;
+
+import android.app.Activity
+import android.app.Application
+import android.content.Context
+import android.content.Intent
+import com.atm.bondowowo.utils.BackgroundJWTChecker
+
+class BondowowoApp : Application() {
+
+    private var jwtChecker: BackgroundJWTChecker? = null
+
+    override fun onCreate() {
+        super.onCreate()
+        initBackgroundJWTChecker(this)
+    }
+
+    private fun initBackgroundJWTChecker(context: Context) {
+        jwtChecker = BackgroundJWTChecker()
+        jwtChecker?.startChecking(context, object : BackgroundJWTChecker.Callback {
+            override fun onTokenVerified() {
+                // Continu app
+            }
+
+            override fun onTokenInvalid() {
+                val intent = Intent(context, LoginActivity::class.java)
+                intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+                context.startActivity(intent)
+                if (context is Activity) {
+                    context.finish()
+                }
+            }
+
+            override fun onNetworkError() {
+                // Harusnya ini coba cek isi JWT pake library
+            }
+        })
+    }
+
+
+}
diff --git a/app/src/main/java/com/atm/bondowowo/LoginActivity.kt b/app/src/main/java/com/atm/bondowowo/LoginActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bdb25211c3ed5051a44c170f116ded610661d2ac
--- /dev/null
+++ b/app/src/main/java/com/atm/bondowowo/LoginActivity.kt
@@ -0,0 +1,107 @@
+package com.atm.bondowowo
+
+import android.content.Intent
+import android.os.Bundle
+import android.widget.Button
+import android.widget.EditText
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import com.atm.bondowowo.data.model.LoginRequest
+import com.atm.bondowowo.utils.NetworkUtils.apiService
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class LoginActivity : AppCompatActivity() {
+
+    private lateinit var etUsername: EditText
+    private lateinit var etPassword: EditText
+    private lateinit var btnLogin: Button
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_login)
+
+        CoroutineScope(Dispatchers.Main).launch {
+            if (isUserAuthenticated()) {
+                navigateToMainActivity()
+            } else {
+                etUsername = findViewById(R.id.etUsername)
+                etPassword = findViewById(R.id.etPassword)
+                btnLogin = findViewById(R.id.btnLogin)
+
+                btnLogin.setOnClickListener {
+                    performLogin()
+                }
+            }
+        }
+    }
+
+    private fun performLogin() {
+        val email = etUsername.text.toString()
+        val password = etPassword.text.toString()
+
+        val loginRequest = LoginRequest(email, password)
+
+        CoroutineScope(Dispatchers.IO).launch {
+            try {
+                val response = apiService.login(loginRequest)
+                if (response.isSuccessful) {
+                    val loginResponse = response.body()
+                    val token = loginResponse?.token
+
+                    saveTokenSecurely(token)
+
+                    startActivity(Intent(this@LoginActivity, MainActivity::class.java))
+                    finish()
+                } else {
+                    withContext(Dispatchers.Main) {
+                        Toast.makeText(this@LoginActivity, "Invalid Credentials", Toast.LENGTH_SHORT)
+                            .show()
+                    }
+                }
+            } catch (e: Exception) {
+                withContext(Dispatchers.Main) {
+                    Toast.makeText(this@LoginActivity, "An error occurred", Toast.LENGTH_SHORT)
+                        .show()
+                }
+            }
+        }
+    }
+
+    private fun saveTokenSecurely(token: String?) {
+        val sharedPreferences = getSharedPreferences("AUTH_PREFS", MODE_PRIVATE)
+        val editor = sharedPreferences.edit()
+        editor.putString("JWT_TOKEN", token)
+        editor.apply()
+    }
+
+    private suspend fun isUserAuthenticated(): Boolean {
+        val sharedPreferences = getSharedPreferences("AUTH_PREFS", MODE_PRIVATE)
+        val token = sharedPreferences.getString("JWT_TOKEN", null)
+        val isValid = token?.let { isTokenValid(it) }
+
+        return token != null && isValid == true
+    }
+
+    private fun navigateToMainActivity() {
+        startActivity(Intent(this, MainActivity::class.java))
+        finish()
+    }
+
+    private suspend fun isTokenValid(token: String): Boolean {
+        return try {
+            val response = apiService.verifyToken("Bearer $token")
+            if (response.isSuccessful) {
+                val responseBody = response.body()
+                val isString = responseBody is String
+                return !isString
+            } else {
+                false
+            }
+        } catch (e: Exception) {
+            false
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/atm/bondowowo/MainActivity.kt b/app/src/main/java/com/atm/bondowowo/MainActivity.kt
index 2a4bb9959cf0656d911052dfba5c02217a31875d..8c31aacfddb0787b6425bfe60d37ffe0dd402dd1 100644
--- a/app/src/main/java/com/atm/bondowowo/MainActivity.kt
+++ b/app/src/main/java/com/atm/bondowowo/MainActivity.kt
@@ -1,13 +1,17 @@
 package com.atm.bondowowo
 
+import android.content.Intent
 import android.os.Bundle
+import android.widget.Toast
 import androidx.appcompat.app.AppCompatActivity
 import androidx.navigation.findNavController
 import androidx.navigation.fragment.NavHostFragment
-import androidx.navigation.ui.AppBarConfiguration
-import androidx.navigation.ui.setupActionBarWithNavController
 import androidx.navigation.ui.setupWithNavController
 import com.atm.bondowowo.databinding.ActivityMainBinding
+import com.atm.bondowowo.utils.NetworkUtils
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
 
 class MainActivity : AppCompatActivity() {
 
@@ -18,20 +22,57 @@ class MainActivity : AppCompatActivity() {
         binding = ActivityMainBinding.inflate(layoutInflater)
         setContentView(binding.root)
 
-        // Navigation Component
-        val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
-        val navController = navHostFragment.navController
-        val appBarConfiguration = AppBarConfiguration(
-            setOf(
-                R.id.transactionFragment, R.id.scanFragment, R.id.graphFragment, R.id.settingsFragment
-            )
-        )
-        setupActionBarWithNavController(navController, appBarConfiguration)
-        binding.bottomNavLayout.bottomNavigation.setupWithNavController(navController)
+        CoroutineScope(Dispatchers.Main).launch {
+            if (!isUserAuthenticated()) {
+                navigateToLoginActivity()
+            } else {
+                val navHostFragment =
+                    supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
+                val navController = navHostFragment.navController
+                binding.bottomNavLayout.bottomNavigation.setupWithNavController(navController)
+            }
+        }
     }
 
     override fun onSupportNavigateUp(): Boolean {
         val navController = findNavController(R.id.nav_host_fragment)
         return navController.navigateUp() || super.onSupportNavigateUp()
     }
+
+    private suspend fun isUserAuthenticated(): Boolean {
+        val sharedPreferences = getSharedPreferences("AUTH_PREFS", MODE_PRIVATE)
+        val token = sharedPreferences.getString("JWT_TOKEN", null)
+
+        return token != null && isTokenValid(token)
+    }
+
+    private fun navigateToLoginActivity() {
+        startActivity(Intent(this, LoginActivity::class.java))
+        finish()
+    }
+
+    private suspend fun isTokenValid(token: String): Boolean {
+        return try {
+            val response = NetworkUtils.apiService.verifyToken("Bearer $token")
+            if (response.isSuccessful) {
+                val responseBody = response.body()
+                val isString = responseBody is String
+                return !isString
+            } else {
+                Toast.makeText(
+                    this@MainActivity,
+                    "Session Expired, Please Re login",
+                    Toast.LENGTH_SHORT
+                ).show()
+                false
+            }
+        } catch (e: Exception) {
+            Toast.makeText(
+                this@MainActivity,
+                "An error occurred, Please Re login",
+                Toast.LENGTH_SHORT
+            ).show()
+            false
+        }
+    }
 }
diff --git a/app/src/main/java/com/atm/bondowowo/data/model/LoginRequest.kt b/app/src/main/java/com/atm/bondowowo/data/model/LoginRequest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f3aedb66a3a98445b76452ac8e7a5eb27131b3b0
--- /dev/null
+++ b/app/src/main/java/com/atm/bondowowo/data/model/LoginRequest.kt
@@ -0,0 +1,6 @@
+package com.atm.bondowowo.data.model
+
+data class LoginRequest(
+    val email: String,
+    val password: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/atm/bondowowo/data/model/LoginResponse.kt b/app/src/main/java/com/atm/bondowowo/data/model/LoginResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4d9a220d14f37a0e1798bb723925f0b71e446e82
--- /dev/null
+++ b/app/src/main/java/com/atm/bondowowo/data/model/LoginResponse.kt
@@ -0,0 +1,5 @@
+package com.atm.bondowowo.data.model
+
+data class LoginResponse(
+    val token: String // JWT
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/atm/bondowowo/data/model/VerifyResponse.kt b/app/src/main/java/com/atm/bondowowo/data/model/VerifyResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7d5e9641f408f7021aee7010628f1a3df00c9a76
--- /dev/null
+++ b/app/src/main/java/com/atm/bondowowo/data/model/VerifyResponse.kt
@@ -0,0 +1,7 @@
+package com.atm.bondowowo.data.model
+
+data class VerifyResponse(
+    val nim: String,
+    val iat: Int,
+    val exp: Int
+)
diff --git a/app/src/main/java/com/atm/bondowowo/data/remote/ApiService.kt b/app/src/main/java/com/atm/bondowowo/data/remote/ApiService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a1a54404537b435ace213c5fa017bd0f236b989e
--- /dev/null
+++ b/app/src/main/java/com/atm/bondowowo/data/remote/ApiService.kt
@@ -0,0 +1,17 @@
+package com.atm.bondowowo.data.remote
+
+import com.atm.bondowowo.data.model.LoginRequest
+import com.atm.bondowowo.data.model.LoginResponse
+import com.atm.bondowowo.data.model.VerifyResponse
+import retrofit2.Response
+import retrofit2.http.Body
+import retrofit2.http.Header
+import retrofit2.http.POST
+
+interface ApiService {
+    @POST("api/auth/login")
+    suspend fun login(@Body loginRequest: LoginRequest): Response<LoginResponse>
+
+    @POST("api/auth/token")
+    suspend fun verifyToken(@Header("Authorization") jwtToken: String): Response<Any>
+}
diff --git a/app/src/main/java/com/atm/bondowowo/utils/BackgroundJWTCheckerUtil.kt b/app/src/main/java/com/atm/bondowowo/utils/BackgroundJWTCheckerUtil.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5af4f8c09a09732e962262245b768c1eb5e47ccb
--- /dev/null
+++ b/app/src/main/java/com/atm/bondowowo/utils/BackgroundJWTCheckerUtil.kt
@@ -0,0 +1,50 @@
+package com.atm.bondowowo.utils
+
+import androidx.appcompat.app.AppCompatActivity
+import android.content.Context
+import kotlinx.coroutines.*
+
+class BackgroundJWTChecker {
+
+    private var job: Job? = null
+
+    interface Callback {
+        fun onTokenVerified()
+        fun onTokenInvalid()
+        fun onNetworkError()
+    }
+
+    @OptIn(DelicateCoroutinesApi::class)
+    fun startChecking(context: Context, callback: Callback) {
+        job = GlobalScope.launch(Dispatchers.IO) {
+            while (isActive) {
+                val sharedPreferences = context.getSharedPreferences(
+                    "AUTH_PREFS",
+                    AppCompatActivity.MODE_PRIVATE
+                )
+                val token = sharedPreferences.getString("JWT_TOKEN", null)
+                if (token != null) {
+                    verifyToken(token, callback)
+                } else {
+                    callback.onTokenInvalid()
+                }
+                delay(3 * 60 * 1000)
+            }
+        }
+    }
+
+
+    private suspend fun verifyToken(token: String, callback: Callback) {
+        try {
+            val response = NetworkUtils.apiService.verifyToken("Bearer $token")
+            if (response.isSuccessful && response.body() !is String) {
+                callback.onTokenVerified()
+            } else {
+                callback.onTokenInvalid()
+            }
+        } catch (e: Exception) {
+            callback.onNetworkError()
+        }
+    }
+
+}
diff --git a/app/src/main/java/com/atm/bondowowo/utils/NetworkUtils.kt b/app/src/main/java/com/atm/bondowowo/utils/NetworkUtils.kt
new file mode 100644
index 0000000000000000000000000000000000000000..41395daa2d346845ad92f488fb7330dd953b3a4e
--- /dev/null
+++ b/app/src/main/java/com/atm/bondowowo/utils/NetworkUtils.kt
@@ -0,0 +1,16 @@
+package com.atm.bondowowo.utils
+
+// NetworkUtils.kt
+
+import com.atm.bondowowo.data.remote.ApiService
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+
+object NetworkUtils {
+    private val retrofit = Retrofit.Builder()
+        .baseUrl("https://pbd-backend-2024.vercel.app/")
+        .addConverterFactory(GsonConverterFactory.create())
+        .build()
+
+    val apiService: ApiService = retrofit.create(ApiService::class.java)
+}
diff --git a/app/src/main/res/color/bottom_nav_icon_color.xml b/app/src/main/res/color/bottom_nav_icon_color.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ce755c13eefc72615179bf365e86ff0ce8ede0e4
--- /dev/null
+++ b/app/src/main/res/color/bottom_nav_icon_color.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/black" android:state_checked="true" />
+    <item android:color="@color/grey" android:state_checked="false" />
+</selector>
\ No newline at end of file
diff --git a/app/src/main/res/color/bottom_nav_text_color.xml b/app/src/main/res/color/bottom_nav_text_color.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ce755c13eefc72615179bf365e86ff0ce8ede0e4
--- /dev/null
+++ b/app/src/main/res/color/bottom_nav_text_color.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/black" android:state_checked="true" />
+    <item android:color="@color/grey" android:state_checked="false" />
+</selector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_logo.png b/app/src/main/res/drawable/ic_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..ec727bb458122581b1579079bc02db305b2a7492
Binary files /dev/null and b/app/src/main/res/drawable/ic_logo.png differ
diff --git a/app/src/main/res/drawable/ic_scan.png b/app/src/main/res/drawable/ic_scan.png
index 5bc7a45d202c7b5f24ea36464b312bfd8d1eb3d1..cc17501d3eeb34b3eaf3cbd12d48561aa0145290 100644
Binary files a/app/src/main/res/drawable/ic_scan.png and b/app/src/main/res/drawable/ic_scan.png differ
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f4c3510fa82e5c378fba2e78c7d49147ef853d83
--- /dev/null
+++ b/app/src/main/res/layout/activity_login.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="23dp">
+
+    <ImageView
+        android:id="@+id/ivLogo"
+        android:layout_width="350dp"
+        android:layout_height="150dp"
+        android:layout_marginTop="80dp"
+        android:src="@drawable/ic_logo"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.466"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        android:contentDescription="Logo" />
+
+    <EditText
+        android:id="@+id/etUsername"
+        android:layout_width="0dp"
+        android:layout_height="60dp"
+        android:layout_marginTop="84dp"
+        android:hint="Enter your email"
+        android:inputType="text"
+        android:background="@color/textfield"
+        android:paddingStart="20dp"
+        android:paddingEnd="20dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/ivLogo"
+        android:autofillHints="emailAddress" />
+
+    <EditText
+        android:id="@+id/etPassword"
+        android:layout_width="0dp"
+        android:layout_height="75dp"
+        android:layout_marginTop="16dp"
+        android:hint="Enter your password"
+        android:inputType="textPassword"
+        android:background="@color/textfield"
+        android:paddingStart="20dp"
+        android:paddingEnd="20dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/etUsername"
+        android:autofillHints="password" />
+
+    <Button
+        android:id="@+id/btnLogin"
+        android:layout_width="0dp"
+        android:layout_height="70dp"
+        android:layout_marginTop="104dp"
+        android:backgroundTint="@color/primary1"
+
+        android:text="Login"
+        android:textAllCaps="false"
+        android:textColor="#FFFFFF"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/etPassword" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/bottom_navigation_layout.xml b/app/src/main/res/layout/bottom_navigation_layout.xml
index c31cae370d6934b0cdb7814cd8aecbfe6dccd145..ea6f4c7ba26a0a9950874998902286b23ffb6de9 100644
--- a/app/src/main/res/layout/bottom_navigation_layout.xml
+++ b/app/src/main/res/layout/bottom_navigation_layout.xml
@@ -3,5 +3,8 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/bottomNavigation"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    app:menu="@menu/bottom_navigation_menu" />
\ No newline at end of file
+    android:layout_height="70dp"
+    android:background="@color/white"
+    app:itemIconTint="@color/bottom_nav_icon_color"
+    app:itemTextColor="@color/bottom_nav_text_color"
+    app:menu="@menu/bottom_navigation_menu" />
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index f8c6127d327620c93d2b2d00342a68e97b98a48d..14750cdafd804111205aed7dd59c2fb2faac95d3 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -7,4 +7,10 @@
     <color name="teal_700">#FF018786</color>
     <color name="black">#FF000000</color>
     <color name="white">#FFFFFFFF</color>
+    <color name="grey">#808080</color>
+    <color name="primary1">#012B39</color>
+    <color name="primary2">#1F7A8C</color>
+    <color name="primary3">#C0DBF8</color>
+    <color name="textfield">#F7F8F9</color>
+
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9c62daf94e2a79bf1708a5a8c3a59991de5e5050
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
+    </style>
+
+    <style name="AppTheme.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
+        <item name="colorPrimary">@color/white</item>
+        <item name="colorPrimaryDark">@color/primary1</item>
+        <item name="colorAccent">@color/primary2</item>
+    </style>
+</resources>
\ No newline at end of file