-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Create a token manager library for MapBox #23511
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
Merged
Merged
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
c9b8494
Add Onyx key and initial method
tgolen b0fed71
Handle expired tokens
tgolen 5449a64
Add token refresh
tgolen c8bb64b
Add app becomes active handler
tgolen 416b970
Fix expiration calculation, improve comments, document token shape
tgolen 53fd9f9
Move timeout logic into refresh method
tgolen c53d822
Add API calls
tgolen 2ffe94a
Merge branch 'main' into tgolen-mapbox-tokenmanager
tgolen 1083804
Move file to actions and rename
tgolen f1dd506
Add imports and debug logs
tgolen 09b09e5
Protect against multiple connections
tgolen cdcd828
Move refresh interval to a variable
tgolen 957f01c
Move API method inside of timeout
tgolen 4df7fed
Add early return and change variable to const
tgolen 073fbe6
Protect against undefined token
tgolen 49a8a74
Rename file and protect against logged out user
tgolen d8479d0
Update comments and add another logout check
tgolen 09e4f5c
Rename method
tgolen e7b930b
Simplify expiration check
tgolen 37c4017
Simplify method even further
tgolen f807f7b
Fix typo, all caps constant, remove function body
tgolen 646da93
Merge branch 'main' into tgolen-mapbox-tokenmanager
tgolen 9d89624
Add missing import
tgolen 3193c56
Merge branch 'main' into tgolen-mapbox-tokenmanager
thienlnam e07ede3
Comment update
thienlnam 98336ec
Update src/libs/actions/MapboxToken.js
tgolen de6f49a
Update src/libs/actions/MapboxToken.js
tgolen db9bc9f
Merge branch 'main' into tgolen-mapbox-tokenmanager
tgolen 29c6f35
Remove unnecessary whitespace
tgolen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
This file contains hidden or 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import _ from 'underscore'; | ||
import moment from 'moment'; | ||
import Onyx from 'react-native-onyx'; | ||
import {AppState} from 'react-native'; | ||
import lodashGet from 'lodash/get'; | ||
import ONYXKEYS from '../../ONYXKEYS'; | ||
import * as API from '../API'; | ||
import CONST from '../../CONST'; | ||
|
||
let authToken; | ||
Onyx.connect({ | ||
key: ONYXKEYS.SESSION, | ||
callback: (val) => { | ||
authToken = lodashGet(val, 'authToken', null); | ||
}, | ||
}); | ||
|
||
let connectionID; | ||
let currentToken; | ||
let refreshTimeoutID; | ||
const REFRESH_INTERVAL = 1000 * 60 * 25; | ||
|
||
const setExpirationTimer = () => { | ||
console.debug('[MapboxToken] refreshing token on an interval', REFRESH_INTERVAL, 'ms'); | ||
|
||
// Cancel any previous timeouts so that there is only one request to get a token at a time. | ||
clearTimeout(refreshTimeoutID); | ||
|
||
// Refresh the token every 25 minutes | ||
refreshTimeoutID = setTimeout(() => { | ||
// If the user has logged out while the timer was running, skip doing anything when this callback runs | ||
if (!authToken) { | ||
console.debug('[MapboxToken] Skipping the fetch of a new token because user signed out'); | ||
return; | ||
} | ||
console.debug(`[MapboxToken] Fetching a new token after waiting ${REFRESH_INTERVAL / 1000 / 60} minutes`); | ||
API.read('GetMapboxAccessToken'); | ||
}, REFRESH_INTERVAL); | ||
}; | ||
|
||
const hasTokenExpired = () => moment().isAfter(currentToken.expiration); | ||
|
||
const clearToken = () => { | ||
console.debug('[MapboxToken] Deleting the token stored in Onyx'); | ||
|
||
// Use Onyx.set() to delete the key from Onyx, which will trigger a new token to be retrieved from the API. | ||
Onyx.set(ONYXKEYS.MAPBOX_ACCESS_TOKEN, null); | ||
}; | ||
|
||
const init = () => { | ||
if (connectionID) { | ||
console.debug('[MapboxToken] init() is already listening to Onyx so returning early'); | ||
return; | ||
} | ||
|
||
// When the token changes in Onyx, the expiration needs to be checked so a new token can be retrieved. | ||
connectionID = Onyx.connect({ | ||
key: ONYXKEYS.MAPBOX_ACCESS_TOKEN, | ||
/** | ||
* @param {Object} token | ||
* @param {String} token.token | ||
* @param {String} token.expiration | ||
* @param {String[]} [token.errors] | ||
*/ | ||
callback: (token) => { | ||
// If the user has logged out, don't do anything and ignore changes to the access token | ||
if (!authToken) { | ||
console.debug('[MapboxToken] Ignoring changes to token because user signed out'); | ||
return; | ||
} | ||
|
||
// If the token is falsy or an empty object, the token needs to be retrieved from the API. | ||
// The API sets a token in Onyx with a 30 minute expiration. | ||
if (!token || _.isEmpty(token)) { | ||
console.debug('[MapboxToken] Token does not exist so fetching one'); | ||
API.read('GetMapboxAccessToken'); | ||
return; | ||
} | ||
|
||
// Store the token in a place where the AppState callback can also access it. | ||
currentToken = token; | ||
|
||
if (hasTokenExpired()) { | ||
console.debug('[MapboxToken] Token has expired after reading from Onyx'); | ||
clearToken(); | ||
return; | ||
} | ||
|
||
console.debug('[MapboxToken] Token is valid, setting up refresh'); | ||
setExpirationTimer(); | ||
}, | ||
}); | ||
|
||
AppState.addEventListener('change', (nextAppState) => { | ||
// Skip getting a new token if: | ||
// - The app state is not changing to active | ||
// - There is no current token (which means it's not been fetch yet for the first time) | ||
// - The token hasn't expired yet (this would just be a waste of an API call) | ||
// - There is no authToken (which means the user has logged out) | ||
if (nextAppState !== CONST.APP_STATE.ACTIVE || !currentToken || !hasTokenExpired() || !authToken) { | ||
return; | ||
} | ||
console.debug('[MapboxToken] Token is expired after app became active'); | ||
clearToken(); | ||
}); | ||
}; | ||
|
||
export default init; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.