diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 98f601ba4427b3266edc434b3b689167f3290ed9..24cf5ec87aa93da3b6eb68f368f4b276a57dd745 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -45,7 +45,14 @@ dependencies {
     implementation(libs.androidx.lifecycle.viewmodel.ktx)
     implementation(libs.androidx.navigation.fragment.ktx)
     implementation(libs.androidx.navigation.ui.ktx)
+    implementation(libs.androidx.annotation)
     testImplementation(libs.junit)
     androidTestImplementation(libs.androidx.junit)
     androidTestImplementation(libs.androidx.espresso.core)
+
+    // Retrofit
+    implementation(libs.retrofit2.retrofit)
+    implementation(libs.retrofit2.converter.gson)
+    implementation(libs.retrofit2.converter.scalars)
+    implementation(libs.okhttp3)
 }
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4a51b78ff66e9fcbe857133f367055c0b22182a1..a76744ed0fee6ca61d9a398d985824b3afa9ed2c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,6 +2,8 @@
 <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:allowBackup="true"
         android:dataExtractionRules="@xml/data_extraction_rules"
@@ -12,6 +14,10 @@
         android:supportsRtl="true"
         android:theme="@style/Theme.Bondoman"
         tools:targetApi="31">
+        <activity
+            android:name=".ui.login.LoginActivity"
+            android:exported="false"
+            android:label="@string/title_activity_login" />
         <activity
             android:name=".MainActivity"
             android:exported="true"
diff --git a/app/src/main/java/com/onionsquad/bondoman/MainActivity.kt b/app/src/main/java/com/onionsquad/bondoman/MainActivity.kt
index d92d0a8434e598970b72ff813ea90f82e68b30d0..a6ad2b9fb755fc340a1eb96c21c88a0dc4708055 100644
--- a/app/src/main/java/com/onionsquad/bondoman/MainActivity.kt
+++ b/app/src/main/java/com/onionsquad/bondoman/MainActivity.kt
@@ -1,13 +1,16 @@
 package com.onionsquad.bondoman
 
+import android.content.Intent
 import android.os.Bundle
-import com.google.android.material.bottomnavigation.BottomNavigationView
 import androidx.appcompat.app.AppCompatActivity
 import androidx.navigation.findNavController
 import androidx.navigation.ui.AppBarConfiguration
 import androidx.navigation.ui.setupActionBarWithNavController
 import androidx.navigation.ui.setupWithNavController
+import com.google.android.material.bottomnavigation.BottomNavigationView
+import com.onionsquad.bondoman.auth.SessionManager
 import com.onionsquad.bondoman.databinding.ActivityMainBinding
+import com.onionsquad.bondoman.ui.login.LoginActivity
 
 class MainActivity : AppCompatActivity() {
 
@@ -16,6 +19,11 @@ class MainActivity : AppCompatActivity() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
+        val sessionManager = SessionManager(this)
+        if (sessionManager.fetchAuthToken() == null) {
+            sendToLoginActivity()
+        }
+
         binding = ActivityMainBinding.inflate(layoutInflater)
         setContentView(binding.root)
 
@@ -29,4 +37,11 @@ class MainActivity : AppCompatActivity() {
         setupActionBarWithNavController(navController, appBarConfiguration)
         navView.setupWithNavController(navController)
     }
+
+    private fun sendToLoginActivity() {
+        val intent = Intent(this@MainActivity, LoginActivity::class.java)
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+        startActivity(intent)
+        finish()
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/auth/SessionManager.kt b/app/src/main/java/com/onionsquad/bondoman/auth/SessionManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..91fc448a741643829bf2833dc42eae409ba2ff56
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/auth/SessionManager.kt
@@ -0,0 +1,31 @@
+package com.onionsquad.bondoman.auth
+
+import android.content.Context
+import android.util.Log
+import com.google.gson.Gson
+import com.onionsquad.bondoman.R
+
+class SessionManager(context: Context) {
+    private val sharedPreferences = context.getSharedPreferences(
+        context.getString(R.string.preference_file_key),
+        Context.MODE_PRIVATE
+    )
+
+    companion object {
+        const val USER_TOKEN = "user_token"
+    }
+
+    fun fetchAuthToken(): Token? {
+        val tokenJson = sharedPreferences.getString(USER_TOKEN, null)
+        return if (tokenJson != null) Gson().fromJson(tokenJson, Token::class.java) else null
+    }
+
+    fun saveAuthToken(token: Token) {
+        sharedPreferences.edit().putString(USER_TOKEN, Gson().toJson(token)).apply()
+        Log.d("TOKEN", "${fetchAuthToken()}")
+    }
+
+    fun deleteAuthToken() {
+        sharedPreferences.edit().remove(USER_TOKEN).apply()
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/auth/Token.kt b/app/src/main/java/com/onionsquad/bondoman/auth/Token.kt
new file mode 100644
index 0000000000000000000000000000000000000000..435b5dba0a1767c56fbcaa233912f555fb34b0c8
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/auth/Token.kt
@@ -0,0 +1,3 @@
+package com.onionsquad.bondoman.auth
+
+data class Token(val tokenString: String, val nim: String, val exp: Long, val iat: Long)
diff --git a/app/src/main/java/com/onionsquad/bondoman/network/BondomanApiService.kt b/app/src/main/java/com/onionsquad/bondoman/network/BondomanApiService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5ecec9762bcbbb22be1f663763a71a9c8b18343b
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/network/BondomanApiService.kt
@@ -0,0 +1,36 @@
+package com.onionsquad.bondoman.network
+
+import okhttp3.OkHttpClient
+import retrofit2.Call
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import retrofit2.converter.scalars.ScalarsConverterFactory
+import retrofit2.http.Body
+import retrofit2.http.Header
+import retrofit2.http.Headers
+import retrofit2.http.POST
+
+
+private const val BASE_URL = "https://pbd-backend-2024.vercel.app/"
+private val client = OkHttpClient.Builder().build()
+private val retrofit = Retrofit.Builder()
+    .baseUrl(BASE_URL)
+    .addConverterFactory(ScalarsConverterFactory.create())
+    .addConverterFactory(GsonConverterFactory.create())
+    .client(client)
+    .build()
+
+interface BondomanApiService {
+    @Headers("Content-Type: application/json")
+    @POST("api/auth/login")
+    fun login(@Body loginData: LoginRequest): Call<String>
+
+    @POST("api/auth/token")
+    fun token(@Header("Authorization") token: String): Call<TokenResponse>
+}
+
+object BondomanApi {
+    val retrofitService: BondomanApiService by lazy {
+        retrofit.create(BondomanApiService::class.java)
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/network/LoginRequest.kt b/app/src/main/java/com/onionsquad/bondoman/network/LoginRequest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c2a6d36c6bc1bf96f6c3af2aee461c9c78946eec
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/network/LoginRequest.kt
@@ -0,0 +1,3 @@
+package com.onionsquad.bondoman.network
+
+data class LoginRequest(val email: String?, val password: String?)
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/network/LoginResponse.kt b/app/src/main/java/com/onionsquad/bondoman/network/LoginResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c264da3200c401f27b1c04755eb4aa154d44bbb1
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/network/LoginResponse.kt
@@ -0,0 +1,5 @@
+package com.onionsquad.bondoman.network
+
+import com.google.gson.annotations.SerializedName
+
+data class LoginResponse(val token: String?)
diff --git a/app/src/main/java/com/onionsquad/bondoman/network/TokenResponse.kt b/app/src/main/java/com/onionsquad/bondoman/network/TokenResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ed2cc004cb0bcc9941a021053d1a92fb622eab8b
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/network/TokenResponse.kt
@@ -0,0 +1,3 @@
+package com.onionsquad.bondoman.network
+
+data class TokenResponse(val nim: String?, val exp: Long?, val iat: Long?)
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginActivity.kt b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3eace85f0832e12fce0cf2201091ba1bdfeb9c3b
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginActivity.kt
@@ -0,0 +1,124 @@
+package com.onionsquad.bondoman.ui.login
+
+import android.app.Activity
+import android.content.Intent
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.widget.EditText
+import android.widget.Toast
+import com.onionsquad.bondoman.MainActivity
+import com.onionsquad.bondoman.databinding.ActivityLoginBinding
+
+import com.onionsquad.bondoman.R
+import com.onionsquad.bondoman.auth.SessionManager
+
+class LoginActivity : AppCompatActivity() {
+
+    private lateinit var loginViewModel: LoginViewModel
+    private lateinit var binding: ActivityLoginBinding
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        binding = ActivityLoginBinding.inflate(layoutInflater)
+        setContentView(binding.root)
+
+        val email = binding.username
+        val password = binding.password
+        val login = binding.login
+        val loading = binding.loading
+
+        val sessionManager = SessionManager(this)
+
+        loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]
+            .apply {
+                loginFormState.observe(this@LoginActivity, Observer {
+                    val loginState = it ?: return@Observer
+
+                    login.isEnabled = loginState.isDataValid
+
+                    if (loginState.emailError != null) {
+                        email.error = getString(loginState.emailError)
+                    }
+                })
+                loginResult.observe(this@LoginActivity, Observer {
+                    val loginResult = it ?: return@Observer
+
+                    loading.visibility = View.GONE
+                    if (loginResult.error != null) {
+                        showLoginFailed(loginResult.error)
+                    }
+                    if (loginResult.success != null) {
+                        sessionManager.saveAuthToken(loginResult.success)
+                        showLoginSuccess()
+                        sendToMainActivity()
+                    }
+                })
+            }
+
+        email.afterTextChanged {
+            loginViewModel.loginDataChanged(
+                email.text.toString(),
+                password.text.toString()
+            )
+        }
+
+        password.apply {
+            afterTextChanged {
+                loginViewModel.loginDataChanged(
+                    email.text.toString(),
+                    password.text.toString()
+                )
+            }
+
+            setOnEditorActionListener { _, actionId, _ ->
+                when (actionId) {
+                    EditorInfo.IME_ACTION_DONE ->
+                        loginViewModel.login(
+                            email.text.toString(),
+                            password.text.toString()
+                        )
+                }
+                false
+            }
+
+            login.setOnClickListener {
+                loading.visibility = View.VISIBLE
+                loginViewModel.login(email.text.toString(), password.text.toString())
+            }
+        }
+    }
+
+    private fun sendToMainActivity() {
+        val intent = Intent(this@LoginActivity, MainActivity::class.java)
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+        startActivity(intent)
+        finish()
+    }
+
+    private fun showLoginSuccess() {
+        Toast.makeText(applicationContext, R.string.login_success, Toast.LENGTH_LONG).show()
+    }
+
+    private fun showLoginFailed(error: String) {
+        Toast.makeText(applicationContext, error, Toast.LENGTH_SHORT).show()
+    }
+}
+
+fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
+    this.addTextChangedListener(object : TextWatcher {
+        override fun afterTextChanged(editable: Editable?) {
+            afterTextChanged.invoke(editable.toString())
+        }
+
+        override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
+
+        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
+    })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginFormState.kt b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginFormState.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dc8eb07260f3c148cec77ef1264538bf0588ed97
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginFormState.kt
@@ -0,0 +1,6 @@
+package com.onionsquad.bondoman.ui.login
+
+data class LoginFormState(
+    val emailError: Int? = null,
+    val isDataValid: Boolean = false
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginResult.kt b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginResult.kt
new file mode 100644
index 0000000000000000000000000000000000000000..25ce877ebd7c45ae4ca479ce9be4a4aad7c71ed5
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginResult.kt
@@ -0,0 +1,8 @@
+package com.onionsquad.bondoman.ui.login
+
+import com.onionsquad.bondoman.auth.Token
+
+data class LoginResult(
+    val success: Token? = null,
+    val error: String? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginViewModel.kt b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6cb095fbcddae41cb7ce9b7f78e243d2ea8766d3
--- /dev/null
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/login/LoginViewModel.kt
@@ -0,0 +1,87 @@
+package com.onionsquad.bondoman.ui.login
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import android.util.Patterns
+import com.google.gson.Gson
+
+import com.onionsquad.bondoman.R
+import com.onionsquad.bondoman.auth.SessionManager
+import com.onionsquad.bondoman.auth.Token
+import com.onionsquad.bondoman.network.BondomanApi
+import com.onionsquad.bondoman.network.LoginRequest
+import com.onionsquad.bondoman.network.LoginResponse
+import com.onionsquad.bondoman.network.TokenResponse
+import retrofit2.Call
+import retrofit2.Callback
+import retrofit2.Response
+
+class LoginViewModel : ViewModel() {
+
+    private val _loginForm = MutableLiveData<LoginFormState>()
+    val loginFormState: LiveData<LoginFormState> = _loginForm
+
+    private val _loginResult = MutableLiveData<LoginResult>()
+    val loginResult: LiveData<LoginResult> = _loginResult
+
+    fun login(email: String, password: String) {
+        BondomanApi.retrofitService.login(LoginRequest(email, password)).enqueue(
+            object : Callback<String> {
+                override fun onResponse(call: Call<String>, response: Response<String>) {
+                    if (response.isSuccessful) {
+                        val body = response.body()
+                        val tokenString = Gson().fromJson(body, LoginResponse::class.java).token!!
+                        Log.d("TOKEN", tokenString)
+                        fetchTokenInformation(tokenString)
+                    } else {
+                        val errBody = response.errorBody()!!.string()
+                        val error = when {
+                            errBody.contains("email") -> "Invalid email"
+                            errBody.contains("password") -> "Invalid password"
+                            else -> "Invalid email or password"
+                        }
+                        _loginResult.value = LoginResult(error = error)
+                    }
+                }
+
+                override fun onFailure(call: Call<String>, t: Throwable) {
+                    _loginResult.value = LoginResult(error = t.message)
+                }
+            }
+        )
+    }
+
+    private fun fetchTokenInformation(tokenString: String) {
+        BondomanApi.retrofitService.token("Bearer $tokenString").enqueue(
+            object : Callback<TokenResponse> {
+                override fun onResponse(
+                    call: Call<TokenResponse>,
+                    response: Response<TokenResponse>
+                ) {
+                    val token = response.body()!!
+                    _loginResult.value = LoginResult(
+                        success = Token(tokenString, token.nim!!, token.exp!!, token.iat!!)
+                    )
+                }
+
+                override fun onFailure(call: Call<TokenResponse>, t: Throwable) {
+                    _loginResult.value = LoginResult(error = t.message)
+                }
+            }
+        )
+    }
+
+    fun loginDataChanged(email: String, password: String) {
+        if (!isEmailValid(email)) {
+            _loginForm.value = LoginFormState(emailError = R.string.invalid_email)
+        } else {
+            _loginForm.value = LoginFormState(isDataValid = true)
+        }
+    }
+
+    private fun isEmailValid(username: String): Boolean {
+        return Patterns.EMAIL_ADDRESS.matcher(username).matches()
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsFragment.kt b/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsFragment.kt
index dc153a26a1d0a02e397c9400600979bbc3a8be75..ca0aa81d6383bd4a0fccad0625fca1b9176f558b 100644
--- a/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsFragment.kt
@@ -4,15 +4,17 @@ import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.Toast
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
+import androidx.navigation.fragment.findNavController
+import com.onionsquad.bondoman.R
+import com.onionsquad.bondoman.auth.SessionManager
 import com.onionsquad.bondoman.databinding.FragmentSettingsBinding
 
 class SettingsFragment : Fragment() {
     private var _binding: FragmentSettingsBinding? = null
 
-    // This property is only valid between onCreateView and
-    // onDestroyView.
     private val binding get() = _binding!!
 
     override fun onCreateView(
@@ -20,11 +22,19 @@ class SettingsFragment : Fragment() {
         container: ViewGroup?,
         savedInstanceState: Bundle?
     ): View {
-        val transactionViewModel =
-            ViewModelProvider(this).get(SettingsViewModel::class.java)
-
         _binding = FragmentSettingsBinding.inflate(inflater, container, false)
 
+        val sessionManager = SessionManager(requireContext())
+
+        binding.apply {
+            logoutButton.setOnClickListener {
+                sessionManager.deleteAuthToken()
+                Toast.makeText(requireContext(), "Log out success", Toast.LENGTH_SHORT).show()
+                findNavController().popBackStack(R.id.navigation_transaction, true)
+                requireActivity().recreate()
+            }
+        }
+
         return binding.root
     }
 
diff --git a/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsViewModel.kt b/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsViewModel.kt
deleted file mode 100644
index a877767c5378529d02be28a4688b0589b13d6064..0000000000000000000000000000000000000000
--- a/app/src/main/java/com/onionsquad/bondoman/ui/settings/SettingsViewModel.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.onionsquad.bondoman.ui.settings
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-
-class SettingsViewModel : ViewModel() {
-    private val _text = MutableLiveData<String>().apply {
-        value = "This is notifications Fragment"
-    }
-    val text: LiveData<String> = _text
-}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_baseline_logout_24.xml b/app/src/main/res/drawable/ic_baseline_logout_24.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c22a96f911c6a8c7615c8e149ee796eddc23d449
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_logout_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+      
+    <path android:fillColor="@android:color/white" android:pathData="M17,7l-1.41,1.41L18.17,11H8v2h10.17l-2.58,2.58L17,17l5,-5zM4,5h8V3H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8v-2H4V5z"/>
+    
+</vector>
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..934a2edc6b2b4eacc44b02b986b6628f51873712
--- /dev/null
+++ b/app/src/main/res/layout/activity_login.xml
@@ -0,0 +1,71 @@
+<?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"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    tools:context=".ui.login.LoginActivity">
+
+    <EditText
+        android:id="@+id/username"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="96dp"
+        android:autofillHints="@string/prompt_email"
+        android:hint="@string/prompt_email"
+        android:inputType="textEmailAddress"
+        android:selectAllOnFocus="true"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <EditText
+        android:id="@+id/password"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        android:autofillHints="@string/prompt_password"
+        android:hint="@string/prompt_password"
+        android:imeActionLabel="@string/action_sign_in"
+        android:imeOptions="actionDone"
+        android:inputType="textPassword"
+        android:selectAllOnFocus="true"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/username" />
+
+    <Button
+        android:id="@+id/login"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:layout_marginTop="16dp"
+        android:layout_marginBottom="64dp"
+        android:enabled="false"
+        android:text="@string/action_sign_in"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/password"
+        app:layout_constraintVertical_bias="0.2" />
+
+    <ProgressBar
+        android:id="@+id/loading"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="64dp"
+        android:layout_marginBottom="64dp"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="@+id/password"
+        app:layout_constraintStart_toStartOf="@+id/password"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="0.3" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
index 441476f744f7bedaef0590d7b4b632a82ee0354d..570a31e7a5eb1be342b0845e3542c86cfe97a81c 100644
--- a/app/src/main/res/layout/fragment_settings.xml
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -5,4 +5,14 @@
     android:layout_height="match_parent"
     tools:context=".ui.settings.SettingsFragment">
 
+    <Button
+        android:id="@+id/logout_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAlignment="textStart"
+        android:textColor="@color/design_default_color_error"
+        android:backgroundTint="@color/white"
+        android:drawableLeft="@drawable/ic_baseline_logout_24"
+        android:drawableTint="@color/design_default_color_error"
+        android:text="@string/action_sign_out" />
 </FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml
index fc575e810de8c1a52bac57a9d44e78422bee791d..f7d84c6783037f11b61b8f406f6570a4d374a7a9 100644
--- a/app/src/main/res/navigation/mobile_navigation.xml
+++ b/app/src/main/res/navigation/mobile_navigation.xml
@@ -7,21 +7,21 @@
 
     <fragment
         android:id="@+id/navigation_transaction"
-        android:name="com.onionsquad.bondoman.ui.graph.GraphFragment"
+        android:name="com.onionsquad.bondoman.ui.transaction.TransactionFragment"
         android:label="@string/title_transaction"
-        tools:layout="@layout/fragment_graph" />
+        tools:layout="@layout/fragment_transaction" />
 
     <fragment
         android:id="@+id/navigation_scan"
-        android:name="com.onionsquad.bondoman.ui.transaction.TransactionFragment"
+        android:name="com.onionsquad.bondoman.ui.scan.ScanFragment"
         android:label="@string/title_scan"
-        tools:layout="@layout/fragment_transaction" />
+        tools:layout="@layout/fragment_scan" />
 
     <fragment
         android:id="@+id/navigation_graph"
-        android:name="com.onionsquad.bondoman.ui.scan.ScanFragment"
+        android:name="com.onionsquad.bondoman.ui.graph.GraphFragment"
         android:label="@string/title_graph"
-        tools:layout="@layout/fragment_scan" />
+        tools:layout="@layout/fragment_graph" />
     <fragment
         android:id="@+id/navigation_settings"
         android:name="com.onionsquad.bondoman.ui.settings.SettingsFragment"
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ad704cda77a7589edab34feae698dbb5b25a3abb..dfc80c0ba927362dd53849ff4c5986dcce5d03f0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -4,4 +4,13 @@
     <string name="title_scan">Scan</string>
     <string name="title_graph">Graph</string>
     <string name="title_settings">Settings</string>
+    <string name="title_activity_login">Sign in</string>
+    <string name="prompt_email">Email</string>
+    <string name="prompt_password">Password</string>
+    <string name="action_sign_in">Sign in</string>
+    <string name="login_success">"Login success!"</string>
+    <string name="invalid_email">Not a valid email</string>
+    <string name="login_failed">"Login failed"</string>
+    <string name="preference_file_key">BondomanSharedPrefs</string>
+    <string name="action_sign_out">Sign out</string>
 </resources>
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index c3441753224764706fc741a2309298b5f20ff6ad..29c47e56b19e76de706812cbc86d529a727fa6b8 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -12,6 +12,9 @@ lifecycleLivedataKtx = "2.6.1"
 lifecycleViewmodelKtx = "2.6.1"
 navigationFragmentKtx = "2.6.0"
 navigationUiKtx = "2.6.0"
+retrofit = "2.11.0"
+okhttp3 = "4.12.0"
+annotation = "1.7.1"
 
 [libraries]
 androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -25,6 +28,11 @@ androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecy
 androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
 androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" }
 androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" }
+retrofit2-retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
+retrofit2-converter-scalars = { group = "com.squareup.retrofit2", name = "converter-scalars", version.ref = "retrofit" }
+retrofit2-converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }
+okhttp3 = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp3"  }
+androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" }
 
 [plugins]
 androidApplication = { id = "com.android.application", version.ref = "agp" }