AzureAD / microsoft-authentication-library-for-java

Microsoft Authentication Library (MSAL) for Java http://aka.ms/aadv2
MIT License
289 stars 145 forks source link

Retrieve refresh token for persistence #706

Closed lx223 closed 1 year ago

lx223 commented 1 year ago

It seems like there is currently no way via the library to retrieve the refresh token for persistence. Is there a reason for that and could it be added? The use case for me is to do periodic refresh using the refresh token to make sure the token isn't revoked by the identity provider with microsoft SSO. I am aware of the silent flow but that won't survive through server restarts.

Avery-Dunn commented 1 year ago

Hello @lx223 : MSAL was designed to try to minimize how much you need to worry about refresh tokens, so we don't expose them.

However, if you need to persist the token cache we do expose the TokenCache object that each client application has, and you can serialize it whenever you need to persist it:

ConfidentialClientApplication clientApp = ... ; // Or PublicClientApplication
// ...other code
String cacheSnapshot = clientApp.tokenCache.serialize(); // JSON-formatted
bgavrilMS commented 1 year ago

What type of application do you have @lx223 ? And what MSAL API do you call ? Is it a web service calling another web service with AcquireTokenForClient? Or is a user involved?

lx223 commented 1 year ago

@Avery-Dunn thanks for the info! Will give it a try. But if my use case makes sense it would be good to expose it rather than having to grab from json.

@bgavrilMS we have a web/vr app with MS SSO integration. take the web flow for example, user authorise a login via SSO, the web app sends the necessary info to the server and the server exchanges/validates using the lib and issues an app token to the web app. We would like to periodically check if the refresh token has been revoked from the identity provider side to revoke the app token on our side. Do you have recommendations on how to achieve this?

bgavrilMS commented 1 year ago

@lx223 - if I understand correctly, you have a web app that calls a web api? Do you own both components or just the web app?

What MSAL APIs do you call on the web app?

HaydenDekker commented 1 year ago

I've just got to this as well,

I'm in the process of putting MSAL app together for the first time and have found this part particularly confusing.

I need to persist the refresh token. The Oauth spec says we get both an access and a refresh token but I could not find anyway of obtaining it. It was not clear by the API that I need to provide a custom TokenCacheAspect to implement my persistence. Plus I believe that is the full cache making the exchange inefficient.

I have a standard Oauth interface where I need to implement the refreshTokens method to renew the access token and provide another set. This keeps the app consistent across all Oauth compliant interfaces. So I'm keen to understand how to achieve it using MSAL.

I saw one method in another post, https://github.com/AzureAD/microsoft-authentication-library-for-java/issues/228 But... reflection.....

I note there it was for security consideration, perhaps a method on the IAuthentiionResult, getRefreshTokenInsuecure()

Avery-Dunn commented 1 year ago

Hello @HaydenDekker : As I said in my earlier comment here, every PublicClientApplication/ConfidentialClientApplication object you make has a tokenCache field that has a serialize method, and by default this will return a JSON-formatted string which will have all of the access/ID/refresh tokens retrieved by that ClientAppliction. You can persist this string however you want, and feed it back into a new ClientApplication object to populate a new cache with those tokens: https://learn.microsoft.com/en-us/entra/msal/java/advanced/msal-java-token-cache-serialization

You shouldn't need to handle refresh tokens individually: as long as you maintain the PublicClientApplication or ConfidentialClientApplication throughout the lifetime of your application then the acquireTokenSilently call will automatically use them to get new tokens, and if you need to maintain the cache through any sort of app shutdown then you can persist that string and load it back into the new PublicClientApplication/ConfidentialClientApplication when your app starts up.

HaydenDekker commented 1 year ago

Thankyou, yes I've set that up. And I used the getAccounts() to find the account that matches my domain account requiring refresh in order to call that and keep msal separate from the application. Works well. Appreciate your help.

Avery-Dunn commented 1 year ago

@HaydenDekker : Happy to help!

Closing due to inactivity from the original asker. If you're still having issues or have related questions, feel free to reopen this thread or start a new one.

andra-popa commented 1 year ago

Jumping in with another question. I noticed for the .NET implementation you allow persistent cache (source:

If perf is an issue, we should come up with a 2-layer cache: memory cache (L1) and persistent cache (L2). This is what we have in .NET for confidential client. For public client a disk access is very fast, I am not aware of perf considerations. So MSAL should always load from the cache and it's the cache's responsibility to be "fast". https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/5502)

Also, for the Java library, you allow persistent cache configuration for PublicClientApplication. However, for ConfidentialClientApplication there is no way to configure persistency, everything is in memory. Is that true? Are there any plans to have persistency for this one too in the near future @bgavrilMS ? It would spare users from implementing it themselves.

bgavrilMS commented 1 year ago

Hi @andra-popa - PublicClientApplication can make use of a persistence cache with the same semantics as MSAL.NET - see https://github.com/AzureAD/microsoft-authentication-extensions-for-java

For ConfidentialClientApplication - web site, web api, service to service - all MSALs expose cache extensibility options, to allow app developers to define their own semantics. The more mature apps use a distributed cache like Redis to store token caches.

We recommend to always have at least a memory cache. Here is an implementation using Google Guava's memory cache that also has eviction https://github.com/Azure-Samples/ms-identity-msal-java-samples/tree/main/1-server-side/msal-client-credential-secret-high-availability

A few more recommendations:

andra-popa commented 1 year ago

Thank you for the help! I think we can use redis

bgavrilMS commented 1 year ago

Sure. Please don't hesitate to open new issues on this topic, even for questions and to include more details on the scenario you have. Our current samples are here https://github.com/Azure-Samples/ms-identity-msal-java-samples, will schedule some work if we have gaps.

andra-popa commented 1 year ago

@bgavrilMS I've encountered an issue while implementing our own token cache persistency: to call acquireTokenSilently, we need to store the account object returned during the initial auth token request. Does that mean we have to implement our own serialiser to store the object somewhere? or is there a better solution?

We noticed the interface extends something called Serializable:

public interface IAccount extends Serializable { ...

but I don't see any serialising methods exposed

bgavrilMS commented 1 year ago

Hi @andra-popa - do you mind opening a new issue, as this one is closed and it's about something else. And pls describe in more detail the type of app you have etc.