Azure-Samples / ms-identity-python-webapp

A Python web application calling Microsoft graph that is secured using the Microsoft identity platform
MIT License
284 stars 135 forks source link

How can this sample be modified to be used in a serverless environment? #54

Open wolfit opened 3 years ago

wolfit commented 3 years ago

When running Flask (or other webserver) in an Azure function (or similar) I guess the web session based cache in the sample can not be used because of the ephemeral nature of Cloud functions. How to get this sample and this particular authorization flow work in a serverless environment? Is it a matter of implementing your own cache where you store and handle the access token and refresh token? In that case, would you suggest that cache implementation to inherit from SerializableTokenCache?

rayluo commented 3 years ago

When running Flask (or other webserver) in an Azure function (or similar) I guess the web session based cache in the sample can not be used because of the ephemeral nature of Cloud functions. How to get this sample and this particular authorization flow work in a serverless environment? Is it a matter of implementing your own cache where you store and handle the access token and refresh token?

On a high level, yes. The actual cache persistence layer has to be determined by your app's architecture. And this topic is not just about token cache. When your app is serverless or stateless, you would need to use a different way to persist your session data, regardless of whether your app requires authentication. Once you have your session mechanism ready, the current sample will automatically work again, piggybacking your chosen session solution.

From a tactical perspective, this sample currently uses a 3rd-party library, flask-session, for session management. It supports some different flavors of session backend. Those are all suitable to your situation.

Alternatively, you can also choose to encrypt and then store session on client side. The downside is the cookie size may become larger.

In that case, would you suggest that cache implementation to inherit from SerializableTokenCache?

This part is purely your implementation detail. You may inherit SerializableTokenCache to plug your persistence layer into this app's authentication engine i.e. MSAL, or you can do it in one level higher, at the entire session level, as I described above.

idg-sam commented 3 years ago

Hi @wolfit, Great question. As Ray said, there are multiple possible solutions to this issue, everything from client-side session cookies to other persistence solutions. We intend to have demonstration samples of persisting the token cache to a database and cache in the near future.

Please be aware that client-side session cookies are size-limited and IIRC, storing the token cache client-side might fail due to hitting this limit.

If your use-case doesn't need your access tokens automatically renewed by MSAL using refresh tokens for offline_access (e.g., continuous access to user's protected external resources when user is not on your app), you won't need to store the token cache anyway (i.e., you'd only need to store ID token claims and access_token and OIDC state param in the session). If this is your use-case, you can opt not to save the token cache, and don't pass it in when instantiating the msal confidential client instance.

In such a scenario, for a currently-logged-in user, you simply pass login_hint={preferred_username from idtoken} and redirect to Azure AD auth url, and a new id token + access token will be granted for the user without any need for interaction. There is a PR that will implement the login_hint functionality out of the box in this sample.

pamelafox commented 1 year ago

Related to this discussion: I've created a branch that deploys this sample using Azure App Service as the host and Azure Cache for Redis as the session storage.

https://github.com/Azure-Samples/ms-identity-python-webapp/compare/master...pamelafox:ms-identity-python-webapp:azd?expand=1

Notable changes to app_config.py:

SESSION_TYPE = "redis"
SESSION_REDIS = redis.Redis(host=os.environ['REDIS_HOST'], port=int(os.environ['REDIS_PORT']), password=os.environ['REDIS_PASSWORD'], ssl=True, ssl_cert_reqs=True)

I use Bicep and the Azure Developer CLI to set those environment variables in App Service.

A similar approach could be used for a MongoDB SESSION_TYPE (with Azure CosmosDB) or a SQLAlchemy SESSION_TYPE (with Azure PostgreSQL Flexible Server).