Last active
May 31, 2021 10:13
-
-
Save damonmaria/d4daac2dac8014cffd9d5872355a4ad4 to your computer and use it in GitHub Desktop.
Keeping Cognito user pool and AWS tokens refreshed in browser, symptoms if you need this is the error: "Invalid login token. Token expired: 1446742058 >= 1446727732"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import AWS from 'aws-sdk/global' | |
import eventEmitter from 'event-emitter' | |
import differenceInMilliseconds from 'date-fns/difference_in_milliseconds' | |
import minDate from 'date-fns/min' | |
// Set this to match your setup | |
const env = { | |
awsRegion: XXXX, | |
identityPoolId: XXXX, | |
userPoolId: XXXX, | |
} | |
AWS.config = new AWS.Config({ | |
region: env.awsRegion, | |
}) | |
export default eventEmitter({ | |
_refreshTimer: null, | |
_cognito: null, | |
// Trigger everything from here. | |
// For example: call after cognitoUser.getSession(), or cognitoUser.authenticateUser() onSuccess callback | |
async updateCognitoSession(user, session) { | |
this._cancelRefresh() // Immediately cancel any refresh, so we won't have 2 calls to this method happening at the same time | |
this._cognito = { user, session } | |
// Apply cognito session to AWS credentials so we can use all AWS services | |
AWS.config.update({ | |
credentials: new AWS.CognitoIdentityCredentials({ | |
IdentityPoolId: env.identityPoolId, | |
Logins: { | |
[`cognito-idp.${env.awsRegion}.amazonaws.com/${env.userPoolId}`]: session.getIdToken().getJwtToken(), | |
}, | |
}) | |
}) | |
await AWS.config.credentials.getPromise() // This will refresh AWS credentials as we've just updated them | |
this._credentialsSet() | |
}, | |
signOut() { | |
this._cognito && this._cognito.user.signOut() | |
this._cacnelSession() | |
this.emit('signOut') | |
}, | |
async refreshSession() { | |
if (this._cognito) { | |
try { | |
const oiriginal = this._cognito | |
const newSession = await new Promise((resolve, reject) => { | |
this._cognito.user.refreshSession(oiriginal.session.getRefreshToken(), (err, result) => { | |
if (err) { | |
reject(err) | |
} else { | |
resolve(result) | |
} | |
}) | |
}) | |
if (this._cognito === oiriginal) { // Don't conitue if someone else has updated _cognito in the meantime | |
await this.updateCognitoSession(oiriginal.user, newSession) // Calls _credentialsSet which schedules next refresh | |
} | |
} catch (e) { | |
if (e.code === 'NotAuthorizedException') { | |
this._credentialsInvalid() | |
} else { | |
console.error(e) | |
this._scheduleRefresh() // Try again | |
} | |
} | |
} | |
}, | |
_credentialsSet() { | |
this.emit('set', AWS.config.credentials) // We have new credentials, whoever needs them should listen | |
this._scheduleRefresh() | |
}, | |
_credentialsInvalid() { | |
this._cacnelSession() | |
this.emit('invalid') // Listen to this and get the user to log in again | |
}, | |
_scheduleRefresh() { | |
if (this._refreshTimer == null && this._cognito) { | |
const msToExpiry = differenceInMilliseconds( | |
minDate( | |
this._cognito.session.getIdToken().getExpiration() * 1000, | |
this._cognito.session.getAccessToken().getExpiration() * 1000, | |
AWS.config.credentials.expireTime, | |
), | |
new Date(), | |
) | |
const adjustedMs = Math.max(msToExpiry / 2, 10000) // Ensure we don't call too often, and don't go negative if expiry already happened | |
console.log(`${new Date()}: Scheduling token refresh in ${adjustedMs / 1000}s`) | |
this._refreshTimer = setTimeout( | |
() => { | |
this._refreshTimer = null // So it is only set when there is a timer running | |
this.refreshSession() | |
}, | |
adjustedMs, | |
) | |
} | |
}, | |
_cacnelSession() { | |
this._cognito = null | |
this._cancelRefresh() | |
}, | |
_cancelRefresh() { | |
clearTimeout(this._refreshTimer) | |
this._refreshTimer = null | |
} | |
}) |
@david114 I read your comments here aws-amplify/amplify-js#405 , Thanks a ton for this research and solution.
Shall I update using the below or using your comment above? is there a difference ? I dont think so, but need a suggestion:
`// Apply cognito session to AWS credentials so we can use all AWS services
AWS.config.update({
credentials: new AWS.CognitoIdentityCredentials({
IdentityPoolId: env.identityPoolId,
Logins: {
[`cognito-idp.${env.awsRegion}.amazonaws.com/${env.userPoolId}`]: session.getIdToken().getJwtToken(),
},
})
})
await AWS.config.credentials.getPromise() // This will refresh AWS credentials as we've just updated them`
Hey @Selenophilia,
I'm afraid yes, it was configured in the frontend, if I remeber correctly.
However the credentials belonged to a user that was specially created just
for the app, with very restricted access.
Greetings,
David
…On Mon, May 31, 2021 at 5:49 AM Paulo Argenal ***@***.***> wrote:
***@***.**** commented on this gist.
------------------------------
Hi @david114 <https://github.com/david114>,
Where did you configuire you aws credentials? did you configured it on
your front end app?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<https://gist.github.com/d4daac2dac8014cffd9d5872355a4ad4#gistcomment-3763183>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AGTP3MRJYFZQWOJA6CK5DMTTQMBN7ANCNFSM452B3EAQ>
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thats a lot of code, i simplified a bit.
If you receive
Invalid login token. Token expired: 1446742058 >= 1446727732
error, just call this function: