[안드로이드] Aws Amplify 라이브러리 v1 -> v2 마이그래이션
Amazon Web Services에서 메일이 날라왔다. 2022년 12월에 AWS는 안드로이드용 Amplify 라이브러리 v2의 정식 출시되었고, v1은 더이상 업데이트 되지 않는다는 내용이었다.
이 알림이 표시된 이유는 고객님 계정에서 현재 유지 관리 중인 Android용 Amplify Library v1을 사용하고 있을 수 있기 때문입니다. | |
2022년 12월에 AWS는 안드로이드용 Amplify 라이브러리 v2의 정식 출시를 발표한 바 있습니다 [1]. 이 버전의 라이브러리는 Android 개발자의 인증 및 저장소 기능 사용 경험을 개선하기 위해 다시 작성되었습니다. 금번 출시 이후 최신 버전의 라이브러리에 푸시 알림과 같은 흥미로운 기능도 추가되었습니다. | |
최신 기술 및 기능을 활용할 수 있도록 최대한 빠른 시일 내에 Android의 Amplify Library v2로 업그레이드하는 것을 권장 드립니다. 업그레이드 가이드 [2] 를 참조하여 v1에서 v2로의 API 변경 사항을 확인할 수 있습니다. 고객 지원을 위해 12개월 동안 v1을 유지 관리(Maintenance)로 즉시 전환하여 최신 버전의 라이브러리로 업그레이드할 수 있는 충분한 시간을 제공합니다. | |
<유지 관리란 무엇을 의미하나요?> | |
유지 관리 기간 동안 amplify-android v1은 백엔드 서비스 및 보안 업데이트와의 호환성을 보장하는 업데이트를 계속 받게 됩니다. Amplify-android v1에는 새로운 기능이 도입되지 않습니다. | |
<유지 보수는 얼마나 오래 지속되나요?> | |
v1 amplify-android는 12개월 동안, 즉 2024년 5월 31일까지 유지 보수가 진행되며, 그 이후에는 새로운 업데이트가 이루어지지 않습니다. 이로 인해 API가 서비스와 동기화되지 않아 프로덕션 앱이 중단될 수 있습니다. |
이러한 이유로 2024년 5월 31일전까지 Amplify 라이브러리를 최신으로 이전하고자 결심했다. 확인해보니 내가 현재 진행중인 프로젝트에서는 다음과 같은 내용의 변동 사항들이 있었다.
1. AuthUser 정보를 가져오는 코드가 동기 코드에서 비동기 코드로 변경됨
2. 유저의 토큰을 userPoolToken에서 사용
3. AWSKeyValueStore, AwsMobileClient 등의 클래스가 사라짐
4. 비밀번호를 리셋하는 메소드에 username 파라미터가 추가됨
이전 v1에서는 AuthUser와 Token을 모두 동기 코드로 가져와 사용하고 있어 해당 부분을 비동기 코드로 변경해야했다. 하지만 프로젝트에서 많은 부분을 차지하고 있어 동기 코드를 비동기 코드로 바꾸기에는 어렵다고 판단하여 SessionManager(싱글톤 클래스)에 비동기 코드를 동기 코드처럼 사용할 수 있게 만들어서 사용했다.
object SessionManager { | |
val authUser: AuthUser? | |
get() { | |
val completableFuture = CompletableFuture<AuthUser>() | |
Amplify.Auth.getCurrentUser( | |
{ authUser -> completableFuture.complete(authUser) }, | |
{ exception -> completableFuture.completeExceptionally(exception) } | |
) | |
return try { | |
completableFuture.get() | |
} catch (e: Exception) { | |
// 오류 처리 | |
null | |
} | |
} | |
val userPoolToken: AuthSessionResult<AWSCognitoUserPoolTokens>? | |
get() { | |
val completableFuture = CompletableFuture<AuthSessionResult<AWSCognitoUserPoolTokens>>() | |
Amplify.Auth.fetchAuthSession({ session -> | |
completableFuture.complete((session as AWSCognitoAuthSession).userPoolTokensResult) | |
}, { exception -> | |
completableFuture.completeExceptionally(exception) | |
}) | |
return try { | |
completableFuture.get() | |
} catch (e: Exception) { | |
// 오류 처리 | |
null | |
} | |
} | |
val expirationAt: Long? | |
get() { | |
val completableFuture = CompletableFuture<Long?>() | |
Amplify.Auth.fetchAuthSession({ authSession -> | |
val tempCredential = (authSession as AWSCognitoAuthSession).awsCredentialsResult.value as? AWSTemporaryCredentials | |
completableFuture.complete(tempCredential?.expiresAt?.toEpochMilli()) | |
}, { exception -> | |
completableFuture.completeExceptionally(exception) | |
}) | |
return try { | |
completableFuture.get() | |
} catch (e: Exception) { | |
// 오류 처리 | |
null | |
} | |
} | |
} |
위의 클래스를 이용해서
1. "AuthUser 정보를 가져오는 코드가 동기 코드에서 비동기 코드로 변경됨"의 경우 다음과 같이 수정했다
변경 전 | |
val authUser = Amplify.Auth.currentUser | |
// authUser 사용 | |
변경 후 | |
SessionManager.authUser?.let { authUser -> | |
// 로그인된 상태 | |
// authUser 사용 | |
} ?: run { | |
// 비로그인 상태 | |
} |
2. "유저의 토큰을 userPoolToken에서 사용" 의 경우 다음과 같이 수정했다
val tokens = AWSMobileClient.getInstance().tokens | |
val idToken = tokens.idToken.tokenString | |
val refreshToken = tokens.refreshToken.tokenString | |
val accessToken = tokens.accessToken.tokenString | |
변경 후 | |
val tokens = SessionManager.userPoolToken?.value | |
val idToken = tokens.idToken | |
val refreshToken = tokens.refreshToken | |
val accessToken = tokens.accessToken |
3. "AWSKeyValueStore, AwsMobileClient 등의 클래스가 사라짐" 의 경우 다음과 같이 수정했다
1) AWSKeyValueStore
현재 진행중인 프로젝트에서 AWSKeyValueStore 클래스의 쓰임은 로그아웃 후에 캐시된 데이터를 지우는 용도로 사용하고 있다.
AWSKeyValueStore(this, "CognitoIdentityProviderCache", true).clear() |
구글링 결과 Amplify 라이브러리 버전이 올라가면서 해당 이슈가 사라졌다고 확인하여 코드를 제거하기로 하였다. 하지만 정확한 정보가 아니므로 이 글을 읽고 있는 독자 중에 AWSKeyValueStore 클래스를 대체하여 캐시를 지우는 방법을 아시는 분이 계시다면 댓글에 정보를 공유해주시면 좋을 것 같습니다.
+ 2024년 1월 25일 추가
로그인 세션이 만료되고 캐시된 데이터(CognitoIdentityProviderCache)를 지우지 않으면, 유저가 로그인을 시도해도 로그인이 되지 않는다. 따라서 정상 로직은 Hub에서 SESSION_EXPIRED를 수신하면 유저를 로그아웃시키고, CognitoIdentityProviderCache의 데이터를 지워줘야한다. 하지만 나는 Amplify 라이브러리 버전이 올라가면서 해당 이슈가 해결되었다고 생각하고, 캐시 데이터를 지우는 코드를 제외하였기 때문에 로그인이 되지 않는다는 유저의 cs가 인입되었다.
해결하는 방법은 다음과 같이 CognitoIdentityProviderCache 파일에 데이터가 있으면, 강제로 로그아웃 시키고 CognitoIdentityProviderCache 파일의 데이터를 모두 제거하였다. 이렇게 하면 유저는 로그인을 할 수 있게 된다.
private val preferences: SharedPreferences by lazy { | |
getSharedPreferences("CognitoIdentityProviderCache", MODE_PRIVATE) | |
} | |
private fun clearCognitoIdentityProviderCache() { | |
if (preferences.all.isNotEmpty()) { | |
Amplify.Auth.signOut { signOutResult -> | |
when (signOutResult) { | |
is AWSCognitoAuthSignOutResult.CompleteSignOut, is AWSCognitoAuthSignOutResult.PartialSignOut -> { | |
val editor = preferences.edit() | |
editor.clear() | |
editor.apply() | |
} | |
is AWSCognitoAuthSignOutResult.FailedSignOut -> { | |
Timber.e(signOutResult.exception) | |
} | |
} | |
} | |
} | |
} | |
} |
2) AwsMobileClient
AwsMobileClient 클래스의 경우는 token을 가져오는 용도로 사용되었으며, 2번의 "유저의 토큰을 userPoolToken에서 사용" 문제를 해결함으로써 자연스럽게 해결되었다.
4. "비밀번호를 리셋하는 메소드에 username 파라미터가 추가됨"의 경우 다음과 같이 수정했다.
변경 전 | |
Amplify.Auth.confirmResetPassword(password, verificationCode, { | |
// 비밀번호 리셋 성공 | |
}, { exception -> | |
// 비밀번호 리셋 실패 | |
}) | |
변경 후 | |
Amplify.Auth.confirmResetPassword(username, password, verificationCode, { | |
// 비밀번호 리셋 성공 | |
}, { exception -> | |
// 비밀번호 리셋 실패 | |
}) |
Amplify.Auth.confirmResetPassword 함수에 username 파라미터가 하나 추가 되었는데, 이 함수는 소셜로 회원가입된 것이 아닌 이름, 이메일, 비밀번호로 회원가입된 유저들을 위한 함수이다. 따라서, 회원가입에서 입력받은 이름을 username 파라미터에 넣어서 함수 호출을 하면 된다. 나의 프로젝트의 경우 회원가입에서 유저 이름을 입력받지 않아서 usename에 email을 넣어서 회원가입을 하도록 만들었다. 따라서, 비밀번호 초기화에도 username 파라미터에 email 값을 넣어서 함수를 호출했다.
더 많은 부분들이 변경되었지만 나의 경우 크게 문제되는 것은 위 설명한 4가지였다. Amplify v2 라이브러리를 마이그래이션 하면서 어려웠던 것이 관련 내용이 블로그, stackoverflow, github, 구글 등에서 찾아볼 수 없었다. 그래서 AWS의 re:Post에 질문을 올려 찾아가면서 마이그래이션을 마칠 수 있었다. 마이그래이션을 진행하실 독자분이 참고가 됐으면 좋겠다. 끝으로 cognito의 토큰 관련해서 이해도가 부족했던 제가 AWS에 질문했던 내용 및 답변을 끝으로 마무리 하겠습니다.
질문
안드로이드 Amplify v2(amplify-android version 2) Auth // SESSION_EXPIRED 이벤트 전달받는 방법 | |
안녕하세요. 저는 안드로이드 Amplify 라이브러리를 aws-sdk-android -> amplify-android version 2 로 마이그레이션을 진행하고 있습니다. | |
1.현재 Auth.Hub 를 통해서 AuthChannelEventName.SESSION_EXPIRED 이벤트를 수신했을 때 로그아웃 후 로그인을 유도하도록 로직이 짜여져 있습니다. refreshToken이 만료된 시점에 Amplify.Auth.fetchAuthSession을 호출하면 Auth.Hub에서 SESSION_EXPIRED 이벤트를 수신받나요? 아니라면 어떻게 refreshToken 만료되었을 때 어떤 동작을 통해서 Auth.Hub에서 AuthChannelEventName.SESSION_EXPIRED 이벤트를 수신받을 수 있나요? | |
2.session을 refresh 한다는게 어떤 의미인가요? session을 refresh한다는게 access 토큰이 만료되었다면 refersh 토큰을 통해 access 토큰을 발급하고, refresh토큰이 만료되었을 때, Auth.Hub에 AuthChannelEventName.SESSION_EXPIRED 이벤트를 받을 수 있는건가요? |
답변
안녕하세요 KKH 님, | |
amplify-android version 2 라이브러리에서 SESSION_EXPIRED 에 대한 질문을 주셨습니다. | |
Q1. SESSION_EXPIRED 이벤트 전달받는 방법. | |
Q1-1) refeshToken이 만료된 시점에 Amplify.Auth.fetchAuthSession을 호출하면 Auth.Hub에서 SESSION_EXPIRED 이벤트를 수신받나요? | |
A1-1) | |
네 맞습니다. | |
Amplify.Auth.fetchAuthSession 에서 내부적으로 호출되는 _fetchAuthSession 에서 authentication session 로직을 다루고, authStateMachine 상태 머신을 사용하여, 콜백의 성공과 에러를 호출합니다. | |
_fetchAuthSession 에서 SessionExpiredException 이 발생한 경우 Auth.Hub에 SESSION_EXPIRED 이벤트를 전송하게 됩니다. | |
자세한 사항은 다음 문서를 참고하세요. | |
Link : RealAWSCognitoAuthPlugin.kt (_fetchAuthSession) | |
. | |
Q1-2) 어떻게 refreshToken 만료되었을 때 어떤 동작을 통해서 Auth.Hub에서 AuthChannelEventName.SESSION_EXPIRED 이벤트를 수신받을 수 있나요? | |
A1-2) | |
위와 같은 이벤트 처리를 위해서, Amplify Hub 의 subscribe 함수를 사용하여 이벤트를 수신받을 수 있습니다. | |
자세한 사항은 다음 문서를 참고하세요. | |
Link : Amplify Hub - Listening for messages | |
Q2. Session Refresh | |
Q2-1) Session Refresh 가 어떤 의미인가요? | |
A2-1) | |
Amazon Cognito 에서 Session 을 Refresh 한다는 것은 refresh token 으로 만료된 ID token 과 access token 을 새로 발급 받는 것을 말합니다. | |
. | |
Q2-2) access 토큰이 만료되었다면 refersh 토큰을 통해 access 토큰을 발급하고, refresh토큰이 만료되었을 때, Auth.Hub에 AuthChannelEventName.SESSION_EXPIRED 이벤트를 받을 수 있는건가요? | |
A2-2) | |
네 맞습니다. | |
Amplify for Android 에서는 유효한 refresh token 이 존재할 때, 만료된 사용자의 ID token 과 access token 을 자동으로 refresh 합니다. refresh token 만료되면, 자동으로 refresh 하는 과정에서 SessionExpiredException 이 발생하며 Hub 에 AuthChannelEventName.SESSION_EXPIRED 이벤트가 전송됩니다. | |
Link : Amazon Cognito - Using the refresh token | |
하지만 Amplify for Android 에서 Auth.signIn API 가 아닌 federateToIdentityPool API 를 사용하는 경우라면, 수동으로 token refresh logic 를 처리하고 federateToIdentityPool API 에 새로운 token 을 제공해야 합니다. | |
Link : Identity Pool Federation : Token Refresh | |
답변이 도움이 되었기를 바라며, 추가로 궁금하신 내용이 있을 시 편하게 댓글 남겨주세요 :) |