Skip to content

Commit 99d0c93

Browse files
added auth interceptor and token manager
1 parent 7f59e9b commit 99d0c93

8 files changed

Lines changed: 109 additions & 46 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.cornellappdev.uplift.data.repositories
2+
3+
import okhttp3.Interceptor
4+
import okhttp3.Response
5+
import javax.inject.Inject
6+
import javax.inject.Singleton
7+
8+
@Singleton
9+
class AuthInterceptor @Inject constructor(
10+
private val tokenManager: TokenManager
11+
) : Interceptor {
12+
override fun intercept(chain: Interceptor.Chain): Response {
13+
val token = tokenManager.getAccessToken()
14+
val request = chain.request().newBuilder().apply {
15+
if (token != null) {
16+
addHeader("Authorization", "Bearer $token")
17+
}
18+
}.build()
19+
return chain.proceed(request)
20+
}
21+
}
Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,43 @@
11
package com.cornellappdev.uplift.data.repositories
22

33
import android.content.Context
4+
import android.content.SharedPreferences
5+
import android.util.Log
46
import androidx.security.crypto.EncryptedSharedPreferences
57
import androidx.security.crypto.MasterKey
68
import androidx.core.content.edit
9+
import dagger.hilt.android.qualifiers.ApplicationContext
10+
import javax.inject.Inject
11+
import javax.inject.Singleton
712

8-
class TokenManager(context: Context) {
13+
@Singleton
14+
class TokenManager @Inject constructor(@ApplicationContext private val context: Context) {
915
private val fileName = "encrypted_tokens"
1016

11-
// 1. Create or retrieve the Master Key for encryption
12-
private val masterKey = MasterKey.Builder(context)
13-
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
14-
.build()
17+
// Initialize EncryptedSharedPreferences
18+
private val sharedPreferences: SharedPreferences by lazy {
19+
try {
20+
createEncryptedPrefs()
21+
} catch (e: Exception) {
22+
Log.e("TokenManager", "Failed to initialize EncryptedSharedPreferences", e)
23+
context.deleteSharedPreferences(fileName)
24+
context.getSharedPreferences(fileName, Context.MODE_PRIVATE)
25+
}
26+
}
1527

16-
// 2. Initialize EncryptedSharedPreferences
17-
private val sharedPreferences = EncryptedSharedPreferences.create(
18-
context,
19-
fileName,
20-
masterKey,
21-
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
22-
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
23-
)
28+
private fun createEncryptedPrefs(): SharedPreferences {
29+
val masterKey = MasterKey.Builder(context)
30+
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
31+
.build()
32+
33+
return EncryptedSharedPreferences.create(
34+
context,
35+
fileName,
36+
masterKey,
37+
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
38+
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
39+
)
40+
}
2441

2542
fun saveTokens(accessToken: String, refreshToken: String) {
2643
sharedPreferences.edit().apply {
@@ -37,4 +54,4 @@ class TokenManager(context: Context) {
3754
fun clearTokens() {
3855
sharedPreferences.edit { clear() }
3956
}
40-
}
57+
}

app/src/main/java/com/cornellappdev/uplift/data/repositories/UserInfoRepository.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import androidx.datastore.preferences.core.edit
88
import androidx.datastore.preferences.core.Preferences
99
import com.apollographql.apollo.ApolloClient
1010
import com.cornellappdev.uplift.CreateUserMutation
11+
import com.cornellappdev.uplift.DeleteUserMutation
1112
import com.cornellappdev.uplift.GetUserByNetIdQuery
1213
import com.cornellappdev.uplift.LoginUserMutation
1314
import com.cornellappdev.uplift.SetWorkoutGoalsMutation
@@ -57,7 +58,12 @@ class UserInfoRepository @Inject constructor(
5758
tokenManager.saveTokens(accessToken, refreshToken)
5859
if (!skip) {
5960
val numericId = id.toIntOrNull()
60-
uploadGoal(numericId, goal)
61+
if (!uploadGoal(numericId, goal)) {
62+
return false
63+
}
64+
}
65+
else {
66+
Log.d("UserInfoRepository", "Skipping goal upload")
6167
}
6268
storeUserFields(id, name, netId, email, skip, accessToken, refreshToken, goal)
6369
Log.d("UserInfoRepositoryImpl", "User created successfully")
@@ -79,13 +85,12 @@ class UserInfoRepository @Inject constructor(
7985
workoutGoal = goal
8086
)
8187
)
82-
// Change so that it uses auth interceptor
83-
.addHttpHeader("Authorization", "Bearer ${tokenManager.getAccessToken()}")
8488
.execute()
8589
if (goalResponse.hasErrors()) {
8690
Log.e("UserInfoRepository", "Failed to set goal: ${goalResponse.errors}")
8791
return false
8892
}
93+
Log.d("UserInfoRepository", "Goal set successfully: $goal")
8994
return true
9095
}
9196

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package com.cornellappdev.uplift.di
22

33
import com.apollographql.apollo.ApolloClient
4+
import com.apollographql.apollo.network.okHttpClient
45
import com.cornellappdev.uplift.BuildConfig
6+
import com.cornellappdev.uplift.data.repositories.AuthInterceptor
57
import dagger.Module
68
import dagger.Provides
79
import dagger.hilt.InstallIn
810
import dagger.hilt.components.SingletonComponent
11+
import okhttp3.OkHttpClient
912
import javax.inject.Singleton
1013

1114

@@ -15,10 +18,19 @@ object AppModule {
1518

1619
@Provides
1720
@Singleton
18-
fun provideApolloClient(): ApolloClient {
21+
fun provideOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient {
22+
return OkHttpClient.Builder()
23+
.addInterceptor(authInterceptor)
24+
.build()
25+
}
26+
27+
@Provides
28+
@Singleton
29+
fun provideApolloClient(okHttpClient: OkHttpClient): ApolloClient {
1930
return ApolloClient.Builder()
2031
.serverUrl(BuildConfig.BACKEND_URL)
32+
.okHttpClient(okHttpClient)
2133
.build()
2234
}
2335

24-
}
36+
}

app/src/main/java/com/cornellappdev/uplift/ui/MainNavigationWrapper.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import com.cornellappdev.uplift.ui.screens.profile.ProfileScreen
4646
import com.cornellappdev.uplift.ui.screens.profile.SettingsScreen
4747
import com.cornellappdev.uplift.ui.screens.reminders.CapacityReminderScreen
4848
import com.cornellappdev.uplift.ui.screens.reminders.MainReminderScreen
49-
import com.cornellappdev.uplift.ui.screens.reminders.WorkoutReminderOnboardingScreen
49+
import com.cornellappdev.uplift.ui.screens.onboarding.WorkoutReminderOnboardingScreen
5050
import com.cornellappdev.uplift.ui.screens.report.ReportIssueScreen
5151
import com.cornellappdev.uplift.ui.screens.report.ReportSubmittedScreen
5252
import com.cornellappdev.uplift.ui.viewmodels.classes.ClassDetailViewModel
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.cornellappdev.uplift.ui.screens.onboarding
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.getValue
5+
import androidx.hilt.navigation.compose.hiltViewModel
6+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
7+
import com.cornellappdev.uplift.ui.screens.reminders.WorkoutReminderScreen
8+
import com.cornellappdev.uplift.ui.viewmodels.onboarding.ProfileCreationViewModel
9+
10+
@Composable
11+
fun WorkoutReminderOnboardingScreen(
12+
viewModel: ProfileCreationViewModel = hiltViewModel(),
13+
) {
14+
val uiState by viewModel.uiStateFlow.collectAsStateWithLifecycle()
15+
val goalValue = uiState.goal
16+
17+
WorkoutReminderScreen(
18+
goalValue = goalValue,
19+
isOnboarding = true,
20+
onGoalValueChange = { viewModel.updateGoals(it) },
21+
onBackClick = { viewModel.onBackClick() },
22+
onNext = { viewModel.onNext() },
23+
onSkip = { viewModel.onSkip() },
24+
)
25+
}

app/src/main/java/com/cornellappdev/uplift/ui/screens/reminders/WorkoutReminderScreen.kt

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,6 @@ data class Reminder(
4747
val enabled: Boolean
4848
)
4949

50-
@Composable
51-
fun WorkoutReminderOnboardingScreen(
52-
viewModel: ProfileCreationViewModel = hiltViewModel(),
53-
) {
54-
val uiState by viewModel.uiStateFlow.collectAsStateWithLifecycle()
55-
val goalValue = uiState.goal
56-
57-
WorkoutReminderScreen(
58-
goalValue = goalValue,
59-
isOnboarding = true,
60-
onGoalValueChange = { viewModel.updateGoals(it.toInt()) },
61-
onBackClick = { viewModel.onBackClick() },
62-
onNext = { viewModel.onNext() },
63-
onSkip = { viewModel.onSkip() },
64-
)
65-
}
66-
6750
@Composable
6851
fun WorkoutReminderSettingsScreen(
6952
viewModel: SettingsViewModel = hiltViewModel(),

app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/onboarding/ProfileCreationViewModel.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import com.cornellappdev.uplift.data.repositories.UserInfoRepository
77
import com.cornellappdev.uplift.ui.UpliftRootRoute
88
import com.cornellappdev.uplift.ui.nav.RootNavigationRepository
99
import com.cornellappdev.uplift.ui.viewmodels.UpliftViewModel
10+
import com.google.firebase.auth.FirebaseUser
1011
import dagger.hilt.android.lifecycle.HiltViewModel
1112
import kotlinx.coroutines.launch
1213
import javax.inject.Inject
1314

1415
data class ProfileCreationUiState(
16+
val user: FirebaseUser? = null,
1517
val name: String = "",
1618
val imageUri: Uri? = null,
1719
val isGoalSkipped: Boolean = false,
@@ -29,13 +31,14 @@ class ProfileCreationViewModel @Inject constructor(
2931
val user = userInfoRepository.getFirebaseUser()
3032
val name = user?.displayName ?: ""
3133
applyMutation {
32-
copy(name = name)
34+
copy(user = user, name = name)
3335
}
3436
}
3537
}
3638

3739
private fun createUser() = viewModelScope.launch {
38-
val user = userInfoRepository.getFirebaseUser()
40+
val state = getStateValue()
41+
val user = state.user
3942
val name = user?.displayName ?: ""
4043
val email = user?.email
4144
if (email.isNullOrBlank()) {
@@ -45,11 +48,8 @@ class ProfileCreationViewModel @Inject constructor(
4548
}
4649

4750
val netId = email.substringBefore("@")
48-
val isSkipped = getStateValue().isGoalSkipped
49-
var goal = 0
50-
if (!isSkipped) {
51-
goal = getStateValue().goal.toInt()
52-
}
51+
val isSkipped = state.isGoalSkipped
52+
val goal = if (isSkipped) 0 else state.goal.toInt()
5353
if (userInfoRepository.createUser(email, name, netId, isSkipped, goal)) {
5454
navigateToHome()
5555
} else {
@@ -70,9 +70,9 @@ class ProfileCreationViewModel @Inject constructor(
7070
rootNavigationRepository.navigateUp()
7171
}
7272

73-
fun updateGoals(newGoal: Int) {
73+
fun updateGoals(newGoal: Float) {
7474
applyMutation {
75-
copy(goal = newGoal.toFloat())
75+
copy(goal = newGoal)
7676
}
7777
}
7878

0 commit comments

Comments
 (0)