Skip to content

Commit

Permalink
Merge pull request #103 from team-winey/feature/feat-kakao-login
Browse files Browse the repository at this point in the history
[feat] 온보딩 / 카카오 로그인 구현
  • Loading branch information
Sangwook123 authored Aug 9, 2023
2 parents 1b56937 + 853988a commit 6c9600c
Show file tree
Hide file tree
Showing 44 changed files with 1,420 additions and 155 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/pr_checker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,11 @@ jobs:
- name: Add Local Properties
env:
AUTH_BASE_URL: ${{ secrets.AUTH_BASE_URL }}
KAKAO_NATIVE_KEY: ${{ secrets.KAKAO_NATIVE_KEY }}
run: |
echo auth.base.url=\"$AUTH_BASE_URL\" >> ./local.properties
echo kakao.native.key=\"$KAKAO_NATIVE_KEY\" >> ./local.properties
echo kakaoNativeKey=$KAKAO_NATIVE_KEY >> ./local.properties
# - name: Access Firebase Service
# run: echo '${{ secrets.GOOGLE_SERVICES_JSON }}' > ./app/google-services.json
Expand Down
10 changes: 10 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ android {
"AUTH_BASE_URL",
gradleLocalProperties(rootDir).getProperty("auth.base.url")
)
buildConfigField(
"String",
"KAKAO_NATIVE_KEY",
gradleLocalProperties(rootDir).getProperty("kakao.native.key")
)

manifestPlaceholders["KAKAO_NATIVE_KEY"] = gradleLocalProperties(rootDir).getProperty("kakaoNativeKey")

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -84,6 +91,8 @@ dependencies {
implementation(workManager)
implementation(hiltWorkManager)
implementation(exif)
implementation(dataStore)
implementation(dataStoreCore)
}

TestDependencies.run {
Expand Down Expand Up @@ -112,6 +121,7 @@ dependencies {
implementation(balloon)
implementation(lottie)
implementation(circleImageView)
implementation(kakaoLogin)

debugImplementation(flipper)
debugImplementation(flipperNetwork)
Expand Down
19 changes: 19 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,25 @@
android:exported="false"
android:screenOrientation="portrait" />

<activity
android:name=".presentation.onboarding.LoginActivity"
android:exported="false"
android:screenOrientation="portrait" />

<activity
android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="oauth"
android:scheme="kakao${KAKAO_NATIVE_KEY}" />
</intent-filter>

</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.android.go.sopt.winey

import android.app.Application
import com.android.go.sopt.winey.BuildConfig.KAKAO_NATIVE_KEY
import com.kakao.sdk.common.KakaoSdk
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber

Expand All @@ -9,5 +11,6 @@ class WineyApplication : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree())
KakaoSdk.init(this, KAKAO_NATIVE_KEY)
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,89 @@
package com.android.go.sopt.winey.data.interceptor

import android.content.Context
import com.android.go.sopt.winey.BuildConfig.AUTH_BASE_URL
import com.android.go.sopt.winey.data.model.remote.response.ResponseReIssueTokenDto
import com.android.go.sopt.winey.data.model.remote.response.base.BaseResponse
import com.android.go.sopt.winey.domain.repository.DataStoreRepository
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import timber.log.Timber
import javax.inject.Inject

class AuthInterceptor @Inject constructor(
@ApplicationContext context: Context
@ApplicationContext context: Context,
private val json: Json,
private val dataStoreRepository: DataStoreRepository
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()

val headerRequest = originalRequest.newBuilder()
.addHeader(HEADER_TOKEN, USER_ID)
val headerRequest = originalRequest.newAuthBuilder()
.build()

return chain.proceed(headerRequest)
val response = chain.proceed(headerRequest)

when (response.code) {
CODE_TOKEN_EXPIRED -> {
try {
val refreshTokenRequest = originalRequest.newBuilder().post("".toRequestBody())
.url("$AUTH_BASE_URL/auth/token")
.addHeader(REFRESH_TOKEN, runBlocking(Dispatchers.IO) { getRefreshToken() })
.build()
val refreshTokenResponse = chain.proceed(refreshTokenRequest)
Timber.e("리프레시 토큰 : $refreshTokenResponse")

if (refreshTokenResponse.isSuccessful) {
val responseToken = json.decodeFromString(
refreshTokenResponse.body?.string().toString()
) as BaseResponse<ResponseReIssueTokenDto>

if (responseToken.data != null) {
saveAccessToken(
responseToken.data.accessToken,
responseToken.data.refreshToken
)
}
refreshTokenResponse.close()
val newRequest = originalRequest.newAuthBuilder().build()
return chain.proceed(newRequest)
}
saveAccessToken("", "")
} catch (t: Throwable) {
Timber.e(t)
saveAccessToken("", "")
}
}
}
return response
}

private fun Request.newAuthBuilder() =
this.newBuilder().addHeader(HEADER_TOKEN, runBlocking(Dispatchers.IO) { getAccessToken() })

private suspend fun getAccessToken(): String {
return dataStoreRepository.getAccessToken().first() ?: ""
}

private suspend fun getRefreshToken(): String {
return dataStoreRepository.getAccessToken().first() ?: ""
}

private fun saveAccessToken(accessToken: String, refreshToken: String) =
runBlocking {
dataStoreRepository.saveAccessToken(accessToken, refreshToken)
}

companion object {
private const val HEADER_TOKEN = "accessToken"
const val USER_ID = "1"
private const val CODE_TOKEN_EXPIRED = 401
private const val REFRESH_TOKEN = "refreshToken"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.android.go.sopt.winey.data.model.remote.request

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class RequestLoginDto(
@SerialName("socialType")
val socialType: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.android.go.sopt.winey.data.model.remote.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ResponseLoginDto(
@SerialName("userId")
val userId: Int,
@SerialName("accessToken")
val accessToken: String,
@SerialName("refreshToken")
val refreshToken: String,
@SerialName("isRegistered")
val isRegistered: Boolean
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.android.go.sopt.winey.data.model.remote.response

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ResponseReIssueTokenDto(
@SerialName("accessToken")
val accessToken: String,
@SerialName("refreshToken")
val refreshToken: String
)
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.android.go.sopt.winey.data.repository

import com.android.go.sopt.winey.data.model.remote.request.RequestCreateGoalDto
import com.android.go.sopt.winey.data.model.remote.request.RequestLoginDto
import com.android.go.sopt.winey.data.model.remote.request.RequestPostLikeDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseLoginDto
import com.android.go.sopt.winey.data.model.remote.response.ResponsePostWineyFeedDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseReIssueTokenDto
import com.android.go.sopt.winey.data.source.AuthDataSource
import com.android.go.sopt.winey.domain.entity.Goal
import com.android.go.sopt.winey.domain.entity.Like
Expand All @@ -17,9 +20,9 @@ import javax.inject.Inject
class AuthRepositoryImpl @Inject constructor(
private val authDataSource: AuthDataSource
) : AuthRepository {
override suspend fun getUser(): Result<User> =
override suspend fun getUser(): Result<User?> =
runCatching {
authDataSource.getUser().data!!.toUser()
authDataSource.getUser().data?.toUser()
}

override suspend fun getWineyFeedList(page: Int): Result<List<WineyFeed>> =
Expand All @@ -46,18 +49,34 @@ class AuthRepositoryImpl @Inject constructor(
authDataSource.deleteFeed(feedId)
}

override suspend fun postFeedLike(feedId: Int, requestPostLikeDto: RequestPostLikeDto): Result<Like> =
override suspend fun postFeedLike(
feedId: Int,
requestPostLikeDto: RequestPostLikeDto
): Result<Like> =
runCatching {
authDataSource.postFeedLike(feedId, requestPostLikeDto).toLike()
}

override suspend fun postCreateGoal(requestCreateGoalDto: RequestCreateGoalDto): Result<Goal> =
override suspend fun postCreateGoal(requestCreateGoalDto: RequestCreateGoalDto): Result<Goal?> =
runCatching {
authDataSource.postCreateGoal(requestCreateGoalDto).data!!.toGoal()
authDataSource.postCreateGoal(requestCreateGoalDto).data?.toGoal()
}

override suspend fun getRecommendList(page: Int): Result<List<Recommend>> =
override suspend fun getRecommendList(page: Int): Result<List<Recommend>?> =
runCatching {
authDataSource.getRecommendList(page).data!!.convertToRecommend()
authDataSource.getRecommendList(page).data?.convertToRecommend()
}

override suspend fun postLogin(
socialAccessToken: String,
requestLoginDto: RequestLoginDto
): Result<ResponseLoginDto?> =
runCatching {
authDataSource.postLogin(socialAccessToken, requestLoginDto).data
}

override suspend fun postReIssueToken(refreshToken: String): Result<ResponseReIssueTokenDto?> =
runCatching {
authDataSource.postReIssueToken(refreshToken).data
}
}
Loading

0 comments on commit 6c9600c

Please sign in to comment.