Superset support for OAuth2
Superset utilizes an underlying framwork call Flask App Builder. Flask has a module that specifically supports OAuth authentication. Superset utilizes this and has built in configurations to recognize OAuth from Google, Twitter and GitHub. Our goal is to identify the best mechanism moving forward for OAuth support in OpenLMIS. OpenLMIS has built an Authentication mechanism using the Spring Security https://spring.io/projects/spring-security-oauth specifically, it implements client-credentials and implicit grant types.
How it works:
Here's a brief overview of how it works when Superset is connected to GitHub as the OAuth 2 provider.
- The user clicks a button in the Superset UI to login with GitHub credentials
- They enter their GitHub username and password and are redirected to the GitHub Authorization screen
- In this screen, they authorize Superset to access their GitHub information and click Authorize
- A code is returned to Superset which is stored in the Superset user account system
- Superset queries GitHub for a token using the code that was stored in their name. If the user already has an account in Superset, they don't do anything. If the username doesn't have an account, Superset creates it locally storing the code as the authentication credential.
OpenLMIS currently supports implicit and password (client-credentials) grant types. Currently, OpenLMIS does not support authorization code when you get a token.
Our team has done research on these grant types and built a custom security manager that gets partially the way there. However, we have identified that we would need to build a robust security manager that handles multiple use cases in order to get Superset to function with the existing OpenLMIS auth microservice. When comparing the amount of work and potential security risk, the Ona team has decided to recommend that we add authorization code base grant types when getting an access token.
We request that the OpenLMIS community evaluate the effort of updating the Auth microservice to support authorization_code based grant types when getting a token.
Here's what's working based on our testing:
It correctly goes to the Authorization endpoint asking for your username and password.URL: https://uat.openlmis.org/api/oauth/authorize? Enter your username and password (administrator|password) and click Ok
You are redirected to the UAT OAuth approval page when you give the app permissions to read and write to your OpenLMIS account. Click Approve for both and click authorize
URL: https://uat.openlmis.org/api/oauth/authorize?response_type=code&client_id=tableau-wdc (plus a redirect code)- OpenLMIS returns the code to Superset. Superset tries to get the access token at https://uat.openlmis.org/api/oauth/token?grant_type=code and it's not supported.
Core Work for OpenLMIS:
- Review Spring Security and the workflows for grant_type=code to see what it would take to implement
- The only missing piece seems to be the support on the /api/oauth/token endpoint for grant_type=code. The /api/oauth/authorize endpoint already works.
Another thing that we scoped:
We started scoping creating an independent login page that could receive an access token from OpenLMIS because we're planning on embedding it as an iFrame within OpenLMIS. We created a login page that would receive the access_token and the username. When we received this from OpenLMIS, our security page had to create an account for that user in Superset following the same principles of OAuth. At this point, we realized that we were recreating the OAuth process and stopped work on it until this decision was made. We believe that adding grant_type=code will solve this problem as well by passing in the token to the page that already supports OAuth code based authentication without having to develop a new set of security pages for Superset.
Sample OAuth Configuration in Flask
Superset, written in Flask allows for custom OAuth2 configuration. To enable this, import AUTH_OAUTH, change the authorization type to AUTH_OAUTH then define the OAUTH providers and assign the user role and enable user registration role.
The following fields need to be defined based on OpenLMIS Auth microservice:
OAUTH_PROVIDERS = [
OAUTH_PROVIDERS = [ { 'name': 'openlmis', 'icon': 'fa-google', 'token_key':'access_token', 'remote_app': { 'consumer_key': 'tableau-wdc', 'consumer_secret': 'changeme', 'request_token_params': { 'scope': 'read write' }, 'access_token_method': 'POST', 'access_token_headers': { 'Authorization':'Basic dXNlci1jbGllbnQ6Y2hhbmdlbWU==' }, 'base_url': 'https://uat.openlmis.org/api/oauth', 'access_token_url': 'https://uat.openlmis.org/api/oauth/token?grant_type=implicit', 'authorize_url': 'https://uat.openlmis.org/api/oauth/authorize?'} } ]
OpenLMIS: the global initiative for powerful LMIS software