@cfjedimaster
So this may help to clarify (having gone done this road myself).
The TL;DR of it is this:
currentUser()
isn’t making an external api call.
jwt()
, also, isn’t making an external api call unless the current token is expired or you have asked it to force refresh: jwt(true)
Forcing refresh of the token before function calls should do it, because it will error and call clearSession()
which calls removeSavedSession()
to delete the localStorage
values, and null
s the currentUser
.
As an additional FYI, decoding and using the access_token
values (see the very end of all this) in app can help you keep external changes in sync.
The long path to the above:
login()
and currentUser()
The user created upon logging in is saved in localStorage
.
You can follow the path from login()
to createUser()
to the User class’ _saveSession()
_saveSession() {
isBrowser() && localStorage.setItem(storageKey, JSON.stringify(this._details));
return this;
}
Now logged in, a call to currentUser()
- first calls the User class’
recoverSession()
, which in turn is checking the currentUser
defined outside the class, but assigned this
inside the User
class constructor()
Here, currentUser()
is getting and returning the user object created at login()
.
- If no
currentUser
is found, it then checks localStorage
:
const json = isBrowser() && localStorage.getItem(storageKey);
if (json) {
try {
const data = JSON.parse(json);
const { url, token, audience } = data;
- if the
localStorage
data is bad (missing url
or token
), then it uses the passed-in apiInstance
or creates a new api instance and passes that to a new User
to recreate it.
But no call has been made to fetch any data since login()
.jwt() and refresh
the .jwt()
method of the User class:
- first gets
this.token
by calling this.tokenDetails()
(it just returns this.token
). this.token
is defined in _processTokenResponse
() which is called in the User class contructor()
(and updated elsewhere). So, it’s just getting the current token.
- it checks to make sure the token exists, then destructures to check for expiration. It also checks here if you asked to force a refresh (by passing
true
as a parameter to .jwt()
)
- if a refresh wasn’t requested and token is not expired, it returns the current
access_token
. Which means, the one available in this.token
in the User
class instance.
Again, at this point no new data has been fetched. If the token is not expired, you are getting the current token, not a new one.
Forcing the refresh is simple, just pass true
like this: .jwt(true)
and then the method will call _refreshToken()
The _refreshToken() method:
- checks for any waiting refreshes, then:
return (refreshPromises[refresh_token] = this.api
.request('/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `grant_type=refresh_token&refresh_token=${refresh_token}`,
})
Once the new data is returned, it calls _processTokenResponse()
to save the token to this.token
, it calls _refreshSavedSession()
that calls _saveSession()
to store the value in localStorage
, and then returns the access_token
.
Note though, that _saveSession
actually saves the response from _details()
into localStorage
. That is, via an iteration of the properties on the instance’s this
. Meaning, the only part of the new token from jwt(true)
that gets stored to localStorage
is the access_token
assigned to this.token
(via _processTokenResponse()
).
The rest of the data saved (and then later returned by any call to currentUser()
will be the data that passed through the constructor upon logging in. Missing that point caused me many headaches.
Refreshed Access_Token and User Data
The access_token
returned by a call to .jwt(true)
is obviously not full user data, but it does contain the following, up to date from the sever, properties:
app_metadata
, email
, exp
, sub
, user_metadata
Since important things like roles[]
are in app_metadata
, the access_token
returned by .jwt(true)
can be decoded and used to keep values up to date/in sync with the app.
jwt-decode
can be used to decode the token like so:
import jwtDecode from 'jwt-decode'
const user = auth.currentUser()
const accessToken = await user.jwt(true)
const freshData = jwtDecode(accessToken)
const userRole = freshData.app_metadata.roles[0]
I hope that helps, I know wrapping my brain around what was
happening took a little while