Closed black-snow closed 3 years ago
Hello @black-snow : Have a look at this doc for the basic/example way of persisting the token cache. What you describe is similar to what we recommend, except that you implement the ITokenCacheAccessAspect
interface and do your serialization/deserialization logic in there. The device code flow sample does this in a pretty simple way, but it should give you an idea of what's intended.
Also, depending on how you want to store the cache we have a small library of extensions which has some persistence features that you might find useful, mainly focused on persisting the cache securely.
@Avery-Dunn thanks for the quick reply!
How do I get the account to build the SilentParameters
? Do I have to store it away separately? Why does TokenCache
/ ITokenCache
not give access to its fields - why can't I just call TokenCache.getAccounts()
? :|
I also can't wrap TokenCache
since most stuff inside of it is package private. I could have my own Jackson-annotated POJO and deserialize the token cache into this one ... no wait, I can't, AccountCacheEntity
is also package private.
/edit: For now I tackled the issue by providing my own IAccount
and extracting homeAccountId, environment and username from the serialized token cache. But that's of course a shitty hack.
@black-snow For the accounts, it depends on how you're using the cache
SilentParameters
. SilentParameters
has a builder for scopes+account as well as just scopes, and if not provided an account then it will still work if each cache has one account.getAccounts()
method, find the account you need from the returned set, and then do the silent calls with that account. If you're loading up the client app and have a persistently stored cache, then you just need to set the cache when starting up the app like in this sample, after which you can call getAccounts()
to get them all from that cacheFor the token cache, most of this was all designed before my time here, but I believe the reason that most of the internals of the token cache aren't public is because they shouldn't ever need to be adjusted manually, refreshing is taken care of by the library's silent calls, and I think pretty much everything in the token cache is retrievable in some way:
AbstractClientApplicationBase.getAccounts()
, and an account should be in AuthenticationResult
objects returned in every token request (both silent requests and specific flows)AuthenticationResult
object returned in the initial token request, or in the AuthenticationResult
object returned when making a silent token request (this will have either the cached tokens, or the refreshed tokens if the originals expired)@Avery-Dunn thanks for the thorough reply! I missed the other constructor on SilentParameters. Now it's down to using my own ITokenCacheAccessAspect
.
I'll open some issues in the msal4j repo.
Thanks again and have some nice holidays!
@Avery-Dunn FYI
and if not provided an account then it will still work if each cache has one account
That doesn't seem to be true.
final SilentParameters parameters = SilentParameters.builder(
Collections.singleton("User.Read")
).build();
app.acquireTokenSilently(parameters);
This results in a warning, that the token cache could not be found (although it is there). I didn't investigate much, but looking at https://github.com/AzureAD/microsoft-authentication-library-for-java/blob/b447f9e1f1fd8026208f2410b095e696f13398b2/src/main/java/com/microsoft/aad/msal4j/AcquireTokenSilentSupplier.java#L33 there's a lot of shenanigans when an account is set vs. when no account is given.
I now pull the account from the app:
Set<IAccount> accounts = app.getAccounts().get(1L, TimeUnit.SECONDS);
account = accounts.isEmpty() ? null : accounts.iterator().next();
final SilentParameters parameters = SilentParameters.builder(
Collections.singleton("User.Read"),
account
).build();
app.acquireTokenSilently(parameters);
and it works fine with the stored refresh tokens.
I managed to implement the authentication flow (while still waiting for https://github.com/microsoftgraph/msgraph-sdk-java-auth/issues/14) and now I wonder what I should store long-term so that the users do not have to re-authenticate once their session is gone. The refresh token alone is not enough, I guess, we also need the account, the scopes are good to remember too just like the expiry.
The
TokenCache
is a candidate (why doesn'tITokenCache
implementSerializable
?) even though I'd prefer not to store some serialized 3rd party object in the DB that might change at any time. But the silent flow sample also uses theIAuthenticationResult
that's held in the session. I might extract the account from theTokenCache
butIConfidentialClientApplication.tokenCache()
only returns aITokenCache
and not theTokenCache
so I'd have to blindly cast it.tl;dr Before I stitch together a whacky solution - is there an official thing to store away? I'd like to actually use the refresh tokens long-term to not annoy the users with re-authentication. The most reliable and straight forward thing that comes to my mind without digging too deep is to code custom wrappers for
TokenCache
andIAuthenticationResult
to store them away.Cheers