-
Notifications
You must be signed in to change notification settings - Fork 116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is there any way to force refresh the token? #2483
Comments
Hi @ffeliciodeveloper you can force refresh the token. Sample code can be found here |
Many thanks for the reply @gpanshu! I made use of the option statement mentioned in my login process. Since I'm not familiar with the library, I thought there was some method that could be called when the access token expires (usually set to renew every 1 hour). Since the login flow depends on a webpage, every time Amplify reports that the user data is invalid (using the fetchUserAttributes method), I force logout through Amplify and inform the user that the login action must be done again. I ended up finding this solution (I don't know if it would be the most correct), but it ended up working for me. In the Amplify for Android doc I didn't find anything specific or an implementation that I could include to update accessToken and idToken without having to do the steps I described. Again, thank you so much for your support. |
What was the solution that you found @ffeliciodeveloper ? Also once your session is expired you have to manually log out and log back in again as the app will still be in the signed in state with invalid credentials. You can however make sure your refresh token has a long expiry and that you refresh your access token well before its expiry which will ensure your session remains active. |
Good morning @gpanshu. Thank you very much for answering me.
About the question above, I always force the signout (only in Amplify), and after the successful logout, I call the method that does the signin through signInWithWebUi. How is it working here in the app? Our app is offline first. You must be wondering "why all this?" The app login process depends on a platform hosted on the customer's server that will use the app. That's why I ended up doing this process of always signingOut only to Amplify and then triggering the signInWithWebUI method to force the user to perform the login process on the platform they use internally.
About what I reported above, would you have an example so I can understand better? In fact, I wanted to know if there was a more correct way to do it without having to carry out this whole flow that I mentioned earlier, that is, always calling signOut and calling signInWithWebUI again. I tried calling the signInWithWebUI method directly when I ran into some exception when calling the fetchUserAttributes method, but it always throws the exception of the signInWithWebUI method stating that the session is still active, and that's why I included this solution of always logging out of Amplify and then doing the login again with signInWithWebUI method. |
So just so you know the refreshing of the credentials is handled by fetchAuthSession which is what you need instead of fetchUserAttributes. As fetchAuthSession will refresh your credentials or return a notAuthorized (if guest access is not allowed) and then you can chose to sign out the user instead of always signing out the user. Hope this helps. |
Good afternoon @gpanshu. So, based on your answer, I could use the fetchAuthSession method to make the entire accessToken and idToken expiration flow, would that be what I understood? Would it be a problem to keep calling this method in the moments I mentioned? (return from the internet or the app stays in the foreground). It would help me a lot. From the tests I did here earlier with the method you indicated (using the AuthFetchSessionOptions.builder().forceRefresh(true).build() option together), even if the accessToken and idToken are expired, it always falls in the block referring to the success in the redemption process, so I ended up using the fetchUserAttributes method. I think my biggest problem is knowing when accessToken and idToken expire within the app. To make it a little more difficult, the app works in different time zones, so you can't guarantee control of the expiration time as mentioned. Currently the app has 2 login flows, the first using an SSO feature and the other using Amplify. |
No what I am saying is that call fetchAuthSession before it expires to get new tokens. If your tokens are expired you will either get the Session Expired hub event OR a notAuthorizedException both of which you can handle to sign out and and sign in the user. You can capture the token expiration time by converting the JWT String to JWT and capturing the expiration time from there if you would like to manage its lifecycle but a refresh on each time the app is started and/or every x minutes should be sufficient. And if it returns expired or not authorized then you force the sign in. If you would like to get on a call and discuss you can book sometime on discord or https://prelude.amazon.com/s/fmeOGZKpI/TRNwwi |
Good afternoon @gpanshu! On your suggestion mentioned above, I performed the operation you indicated. val option : AuthFetchSessionOptions = AuthFetchSessionOptions.builder().forceRefresh(true).build()
Amplify.Auth.fetchAuthSession(
option,
{
val awsCognitoAuthSession = it as AWSCognitoAuthSession
Log.i("fetchAuthSession", awsCognitoAuthSession.toString())
val authSessionResultAWSCognitoUserPoolTokens : AuthSessionResult<AWSCognitoUserPoolTokens> = awsCognitoAuthSession.userPoolTokensResult
val awsCognitoUserPoolTokens : AWSCognitoUserPoolTokens = authSessionResultAWSCognitoUserPoolTokens.value as AWSCognitoUserPoolTokens
val idToken = awsCognitoUserPoolTokens.idToken!!
val accessToken = awsCognitoUserPoolTokens.accessToken!!
Log.i("fetchAuthSession_ID_TOKEN", idToken)
Log.i("fetchAuthSession_ACCESS_TOKEN", accessToken)
var jwt = JWT(idToken)
Log.i("fetchAuthSession_JWT_ID_TOKEN_ISSUE_AT", jwt.issuedAt.toString())
Log.i("fetchAuthSession_JWT_ID_TOKEN_ISSUE_TIME", jwt.issuedAt?.time.toString())
Log.i("fetchAuthSession_JWT_ID_TOKEN_EXPIRED_AT", jwt.expiresAt.toString())
Log.i("fetchAuthSession_JWT_ID_TOKEN_EXPIRED_TIME", jwt.expiresAt?.time.toString())
jwt = JWT(accessToken)
Log.i("fetchAuthSession_JWT_ID_TOKEN_ISSUE_AT", jwt.issuedAt.toString())
Log.i("fetchAuthSession_JWT_ID_TOKEN_ISSUE_TIME", jwt.issuedAt?.time.toString())
Log.i("fetchAuthSession_JWT_ID_TOKEN_EXPIRED_AT", jwt.expiresAt.toString())
Log.i("fetchAuthSession_JWT_ID_TOKEN_EXPIRED_TIME", jwt.expiresAt?.time.toString())
var expiresIn : Long = 0
if(jwt.expiresAt != null && jwt.issuedAt != null) {
expiresIn = jwt.expiresAt?.time!!.minus(jwt.issuedAt?.time!!)
}
Log.i("fetchAuthSession_JWT_ID_TOKEN_EXPIRED_TIME", expiresIn.toString())
jwt = JWT(accessToken)
Log.i("fetchAuthSession_JWT_ACCESS_TOKEN_ISSUE_AT", jwt.issuedAt.toString())
Log.i("fetchAuthSession_JWT_ACCESS_TOKEN_EXPIRED_AT", jwt.expiresAt.toString())
},
{ authException ->
Log.e("fetchAuthSession", "Failed to fetch auth session", authException)
}
) Even doing as described above, my token data (accessToken/idToken), were not updated, the old values are still maintained. I was able to access the Amplify discord (uri https://discord.com/channels/705853757799399426/1095000026247348265/1095732566092415038) and found a problem like mine and the same operation I currently perform was suggested, that is, force the signOut and then perform the signIn again. I also tested by calling the fetchAuthSessions method several times, but even so, the accessToken and idToken information is not renewed. |
HI @ffeliciodeveloper I am going to test this and get back to you as forcing a refresh if your tokens are valid should return you new tokens. |
Hello @gpanshu! Thanks for your help! |
Hi @ffeliciodeveloper as per your suggestion I setup my access token to expire every 60 minutes and refresh token to expire every 90 days. Then I changed my clock forward to see if I can do fetchUserAttributes or fetchAuthSession (both worked). After that I put my app in background for the day and opened it up again and did a fetchAuthSession(forced) and that forced the access tokens to refresh. I noticed that the access tokens if expired refreshed as long as the refresh token was valid with new expiry times. Here are some troubleshooting steps that could help up with your issue:
|
Good afternoon @gpanshu Please forgive me for the delay in responding. So this is the problem I'm really facing. package test
import android.util.Log
import androidx.lifecycle.lifecycleScope
import aws.sdk.kotlin.services.cognitoidentityprovider.model.TimeUnitsType
import com.amplifyframework.auth.AWSCognitoUserPoolTokens
import com.amplifyframework.auth.AuthChannelEventName
import com.amplifyframework.auth.AuthUserAttribute
import com.amplifyframework.auth.cognito.AWSCognitoAuthSession
import com.amplifyframework.auth.cognito.result.AWSCognitoAuthSignOutResult
import com.amplifyframework.auth.exceptions.NotAuthorizedException
import com.amplifyframework.auth.exceptions.SessionExpiredException
import com.amplifyframework.auth.exceptions.SignedOutException
import com.amplifyframework.auth.options.AuthFetchSessionOptions
import com.amplifyframework.auth.options.AuthSignOutOptions
import com.amplifyframework.auth.result.AuthSessionResult
import com.amplifyframework.core.Amplify
import com.amplifyframework.core.InitializationStatus
import com.amplifyframework.hub.HubChannel
import com.auth0.android.jwt.JWT
import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.koin.androidx.viewmodel.ext.android.viewModel
import tech.ambar.eva.android_services.services.interfaces.ServiceCallbacks
import tech.ambar.eva.domain.login.model.UserAttributesAD
open class LoggedBaseActivity: BaseActivity(), DataUpdateListener, ServiceCallbacks {
companion object {
val TAG : String = "LoggedActivity"
}
// method called when the app returns to the foreground and when the internet returns.
override fun executeProcessLinkedAmplify(
callback : () -> Unit,
executedByRestartFront : Boolean
) {
/*
Even calling the fetchAuthSession method every time the app returns to the foreground or the internet also returns,
the updated token data (accessToken/idToken) is not updated.
Even if the token values expire, it always returns me the previous value, it is never renewed,
that's why I use the fetchUserAttributes method. Only with it can I know if the token has expired.
*/
fetchAuthSession()
Amplify.Auth.fetchUserAttributes(
{
// calls callback function if accessToken/idToken is not expired
callback()
}
) {
when(it) {
is NotAuthorizedException -> {
// Force signOut on Amplify and inform user about token expiration.
// This is where I have the expired accessToken response
forceLogoutAfterNotAuthorizedException(callback)
}
is SessionExpiredException -> {
showDialogForSessionExpired(callback, message = "Session expired.\nYour account needs to be re-authenticated by AD")
}
is SignedOutException -> {
//
showDialogForSessionExpired(
callback = { signInWithWebUI(callback) },
message = "Session ended. Your account requires AD authentication"
)
}
else -> {
Log.d("TAG", "$it")
}
}
}
}
private fun fetchAuthSession() {
val option : AuthFetchSessionOptions = AuthFetchSessionOptions.builder().forceRefresh(true).build()
Amplify.Auth.fetchAuthSession(
option,
{
val awsCognitoAuthSession = it as AWSCognitoAuthSession
Log.i(TAG, awsCognitoAuthSession.toString())
val authSessionResultAWSCognitoUserPoolTokens : AuthSessionResult<AWSCognitoUserPoolTokens> = awsCognitoAuthSession.userPoolTokensResult
val awsCognitoUserPoolTokens : AWSCognitoUserPoolTokens = authSessionResultAWSCognitoUserPoolTokens.value as AWSCognitoUserPoolTokens
val idToken = awsCognitoUserPoolTokens.idToken!!
val accessToken = awsCognitoUserPoolTokens.accessToken!!
Log.i(TAG, idToken)
Log.i(TAG, accessToken)
var jwt = JWT(idToken)
Log.i(TAG, jwt.issuedAt.toString())
Log.i(TAG, jwt.issuedAt?.time.toString())
Log.i(TAG, jwt.expiresAt.toString())
Log.i(TAG, jwt.expiresAt?.time.toString())
jwt = JWT(accessToken)
Log.i(TAG, jwt.issuedAt.toString())
Log.i(TAG, jwt.issuedAt?.time.toString())
Log.i(TAG, jwt.expiresAt.toString())
Log.i(TAG, jwt.expiresAt?.time.toString())
// handling to get token expiry time
var expiredTime : Long = 0
if(jwt.expiresAt != null && jwt.issuedAt != null) {
expiredTime = jwt.expiresAt?.time!!.minus(jwt.issuedAt?.time!!)
}
Log.i(TAG, expiredTime.toString())
jwt = JWT(accessToken)
Log.i(TAG, jwt.issuedAt.toString())
Log.i(TAG, jwt.expiresAt.toString())
},
{ authException ->
Log.e(TAG, "Failed to fetch auth session", authException)
}
)
}
private fun showDialogForSessionExpired(
callback : () -> Unit,
message : String
) {
runOnUiThread {
if (!this@LoggedBaseActivity.isFinishing) {
ranTheCallOnRestartFront = true
openDialog(
titleId = R.string.app_title,
message = message,
includeNeutralButton = true,
cancelFun = {},
confirmFun = {
signInWithWebUI(callback)
}
)
}
}
}
// method used to call the customer's AD
private fun signInWithWebUI(callback : () -> Unit) {
Amplify.Auth.signInWithWebUI(
this@LoggedBaseActivity,
{ authSigningInResult ->
if(authSigningInResult.isSignedIn) {
Amplify.Auth.fetchAuthSession(
AuthFetchSessionOptions.builder().forceRefresh(true).build(),
{
fetchUserAttributes(it as AWSCognitoAuthSession, callback)
},
{}
)
}
},
{
logoutAmplify(callback)
}
)
}
private fun logoutAmplify(callback : () -> Unit) {
Amplify.Auth.signOut(options) { signOutResult ->
when(signOutResult) {
is AWSCognitoAuthSignOutResult.CompleteSignOut -> {
signInWithWebUI(callback)
}
is AWSCognitoAuthSignOutResult.FailedSignOut -> {}
}
}
}
private fun forceLogoutAfterNotAuthorizedException(callback : () -> Unit) {
Amplify.Auth.signOut(options) { signOutResult ->
when(signOutResult) {
is AWSCognitoAuthSignOutResult.PartialSignOut -> {
showDialogForSessionExpired(callback, message = "Your account needs AD authentication")
}
is AWSCognitoAuthSignOutResult.CompleteSignOut -> {}
is AWSCognitoAuthSignOutResult.FailedSignOut -> {}
}
}
}
private fun fetchUserAttributes(awsCognitoAuthSession: AWSCognitoAuthSession, callback : () -> Unit) {
val authSessionResultAWSCognitoUserPoolTokens : AuthSessionResult<AWSCognitoUserPoolTokens> = awsCognitoAuthSession.userPoolTokensResult
val authSessionResult : AuthSessionResult<String> = awsCognitoAuthSession.userSubResult
if(authSessionResultAWSCognitoUserPoolTokens.value == null) {
return
}
val awsCognitoUserPoolTokens : AWSCognitoUserPoolTokens = authSessionResultAWSCognitoUserPoolTokens.value as AWSCognitoUserPoolTokens
Amplify.Auth.fetchUserAttributes(
{ listAuthUserAttribute ->
val authUserAttributeList : List<AuthUserAttribute> = listAuthUserAttribute
val authUserData = authUserAttributeList[1].value as String
val userAttributesADMapper : Array<UserAttributesAD> = Json.decodeFromString(string = authUserData)
val userAttributesAD : UserAttributesAD = userAttributesADMapper.first()
var authUserAttributeEmail : String? = null
// handling to get token expiry time
var expiresIn : Long = 0
if(awsCognitoUserPoolTokens.idToken != null) {
val jwt = JWT(awsCognitoUserPoolTokens.idToken!!)
if(jwt.expiresAt != null && jwt.issuedAt != null) {
expiresIn = jwt.expiresAt?.time!!.minus(jwt.issuedAt?.time!!)
}
}
viewModel.userAttributesAD = userAttributesAD.copy(
sessionResult = authSessionResult.value,
idToken = awsCognitoUserPoolTokens.idToken,
accessToken = awsCognitoUserPoolTokens.accessToken,
refreshToken = awsCognitoUserPoolTokens.refreshToken,
email = authUserAttributeEmail,
expiresIn = expiresIn
)
viewModel.loginPerformedByAD()
},
{
}
)
}
} Do you have any gist or code example that I could use to see where I'm going wrong in the accessToken/idToken renewal flow, please? Thank you in advance for your help and I'm sorry for so many messages about this, but I haven't found anything referring to what I need in the documentation or in searches on the internet. |
@ffeliciodeveloper thank you so much for your patience while we deal with this to help unblock you.
|
Good afternoon @gpanshu! Again, sorry for the delay in responding. Thanks for the support. I apologize for that, it's just that my understanding of English is still very weak. Thanks for the help you are giving. |
You do not need to do come in for the meeting and we can have a virtual screenshare meeting. I would like to unblock you asap as I see you have been struggling for a while. Just use the link I provided you to book a meeting in your calendar. |
Good afternoon @gpanshu! I performed schedule creation to check token refresh feature. I created it with my own account, but someone else will accompany me on this solution that I'm having problems solving. Again, thanks a lot for your help. |
Hello @gpanshu! Good morning! What will our conversation be like today? Should I wait for your call? |
As per our conversation please report back once you have fixed your configuration file as that might be the root of the problem. |
As discussed over the call, this problem is now fixed. The problem was due to configuration being incorrect and also sign out not invalidating 3P session. |
|
@gpanshu |
Hi @sekitaka what problem are you facing? Can you elaborate? It might be better to create a new issue referencing this issue and attaching as much information as possible. |
Good morning/Good afternoon/Good evening everyone
First of all, thank you very much for your help in advance.
Here's the thing, I'm using the library and I'm facing some problems using it.
The application that I'm applying Amplify, uses the feature through the signInWithWebUI method (the lib calls a webView automatically) to perform the login through a custom interface created by the Web Frontend team.
After successfully logging in, I need to verify that the Amplify token has not expired in certain data transmission processes.
When I use the fetchUserAttributes method, it is falling into the expired Token flow and through Log.i the information below is shown:
When this flow occurs, I open an alertDialog informing that the session has expired and that there is a need to carry out a login process.
When the user confirms the execution, I call the signInWithWebUI method again to open the webView and perform the login processes again.
After this call, it is always falling into the error block stating that the session is active and to proceed, it is necessary to logoff and login again.
I would like to know if there would be a simple way to force the renewal of the token or would there be a way to change some implementation?
Below I leave the snippet of code for you to understand better:
If the Amplify.Auth.fetchAuthSession method is used, the user data linked to Cognito is returned correctly, so I would like to know if there is a way to renew the token, I saw that in Javascript there is a way to carry out this process, but on Android I ended up not finding anything yet.
In advance, I'm sorry for the problem (I'm already desperate to solve it kkkkkkk) and thank you very much for all your help.
The text was updated successfully, but these errors were encountered: