verdan / flaskoidc Goto Github PK
View Code? Open in Web Editor NEWA wrapper of Flask with pre-configured OIDC support
License: Apache License 2.0
A wrapper of Flask with pre-configured OIDC support
License: Apache License 2.0
We are using Amundsen with OIDC but have SSL terminated on an ingress(nginx) that sits in front of the servers. We run gunicorn and pass the option --forwarded-allow-ips *
to enable gunicorn to know that the original request used https
. This worked correctly for version 1.0.4. When we upgrade to version 1.0.6, the redirects were to http
not to https
.
Please let us know if you need more information.
You have recently introduced generic env variable based configuration injection at
Lines 40 to 48 in 3400bc3
This is instant relief to pass n no of configs directly to flask-oidc
.
But flask app often needs configs of various types. Example
OIDC_ID_TOKEN_COOKIE_TTL
OIDC_CLIENT_SECRETS
(it is union type of either string, i.e. json file path or dict holding the actual configs)CUSTOM_SECURITY_MANAGER
Recently I was implementing a purely env variable based keycloak integration for apache superset, and built something similar.
A snippet of the environment variables looked like :
ENV_SERIALIZED_VARS: OAUTH_PROVIDERS, AUTH_USER_REGISTRATION, OPENID_PROVIDERS, OIDC_CLIENT_SECRETS, ENABLE_PROXY_FIX
ENV_PYTHON_IMPORT_VARS: CUSTOM_SECURITY_MANAGER, AUTH_TYPE
SUPERSET__ENABLE_PROXY_FIX: "true"
SUPERSET__CUSTOM_SECURITY_MANAGER: fab_oidc.security:SupersetOIDCSecurityManager
SUPERSET__AUTH_TYPE: flask_appbuilder.security.manager:AUTH_OID
SUPERSET__AUTH_USER_REGISTRATION: "true"
SUPERSET__AUTH_USER_REGISTRATION_ROLE: {{ .Values.auth.initialRole }}
SUPERSET__OIDC_CLIENT_SECRETS: |+
{
"web": {
"issuer": "{{ .Values.keycloak.authServerUrl }}",
... # omitted for brevity
}
}
The shortcut code that I had added into superset is something like :
class EnvInjector:
ENV_VAR_PATTERN = r"SUPERSET__(?P<KEY>.*)"
SERIALIZED_FIELDS = re.split(r"\s+|,\s*", os.getenv("ENV_SERIALIZED_VARS", ""))
PYTHON_IMPORT_FIELDS = re.split(r"\s+|,\s*", os.getenv("ENV_PYTHON_IMPORT_VARS", ""))
@classmethod
def is_superset_config_key(cls, key: str) -> bool:
return re.match(cls.ENV_VAR_PATTERN, key) is not None
@classmethod
def extract_key(cls, key: str) -> str:
return re.search(cls.ENV_VAR_PATTERN, key).group('KEY')
@classmethod
def extract_value(cls, prefixed_key: str, value: str) -> object:
import json
from werkzeug.utils import import_string
key = cls.extract_key(prefixed_key)
if key in cls.SERIALIZED_FIELDS or prefixed_key in cls.SERIALIZED_FIELDS:
return json.loads(value)
if key in cls.PYTHON_IMPORT_FIELDS or prefixed_key in cls.PYTHON_IMPORT_FIELDS:
return import_string(value)
return value
@classmethod
def get_configs(cls) -> dict:
return {cls.extract_key(k): cls.extract_value(k, v)
for k, v in os.environ.items()
if cls.is_superset_config_key(k)}
@classmethod
def inject_into(cls, obj):
configs = cls.get_configs()
for k, v in configs.items():
setattr(obj, k, v)
# In case of seuperset, config is the entire module. In case of flaskoidc it is just an object
import sys
__module__ = sys.modules[__name__]
EnvInjector.inject_into(__module__)
Container stops running in both amundsenmetadata and amundsensearch because of this error:
amundsensearch | Using requested Flask module flaskoidc and class FlaskOIDC amundsensearch | Traceback (most recent call last): amundsensearch | File "search_service/search_wsgi.py", line 15, in <module> amundsensearch | application = create_app(config_module_class=config_module_class) amundsensearch | File "/app/search_service/__init__.py", line 67, in create_app amundsensearch | app = class_obj(__name__, **flask_kwargs_dict) amundsensearch | File "/usr/local/lib/python3.7/site-packages/flaskoidc/__init__.py", line 75, in __init__ amundsensearch | self.db = SQLAlchemy(self) amundsensearch | File "/usr/local/lib/python3.7/site-packages/flask_sqlalchemy/__init__.py", line 758, in __init__ amundsensearch | _include_sqlalchemy(self, query_class) amundsensearch | File "/usr/local/lib/python3.7/site-packages/flask_sqlalchemy/__init__.py", line 112, in _include_sqlalchemy amundsensearch | for key in module.__all__: amundsensearch | AttributeError: module 'sqlalchemy' has no attribute '__all__'
looks like to get ride of this we need to upgrade the flask_sqlalchemy to >=3.0.0 which ofcouse rises dependency conflict and containers can not be built!
Hi,
In our project we are using Amundsen as our platform to search metadata in our DB's.
We are using authentication with OKTA on the organization.
This is the first time using flaskoidc.
At this point, we have successfully setup authentication at startup.
We would like to change the expiration time of the token, since we saw that by now there is an issue with refreshment_token
.
https://github.com/verdan/flaskoidc#known-issues
We are using version
flaskoidc==0.2.3
Our question is if there is a way to change the default value we are having from our authentication with flaskoidc configuration?
Hi, I'm trying to use your middleware in my Flask API, authenticating with Keycloak, but I'm getting an error on token = json.loads(token)
. It seems like it's skipping a "decode jwt" step.
Below is my api setup -- am I using this wrong?
import os
# Got a lookup error when trying the `CustomConfig` approach from README
os.environ['FLASK_DEBUG'] = 'True'
os.environ['FLASK_OIDC_PROVIDER_NAME'] = 'keycloak'
os.environ['FLASK_OIDC_CLIENT_ID'] = 'api'
os.environ['FLASK_OIDC_CLIENT_SECRET'] = os.environ['API_CLIENT_SECRET']
os.environ['FLASK_OIDC_CONFIG_URL'] = 'http://keycloak:8080/auth/realms/global/.well-known/openid-configuration'
os.environ['FLASK_OIDC_USER_ID_FIELD'] = 'preferred_username'
from flask import g
from flaskoidc import FlaskOIDC
app = FlaskOIDC(__name__)
@app.route("/hello")
def hello():
return g.oidc_token_info
Hi Verdan,
Not sure if this is the right place to be writing this as it is specifically an Amundsen issue but I followed the link from your stemma.ai article.
I am attempting to use Azure AD to provide authentication for our Amundsen instance. I have followed the instructions in your article and I think it is mostly working. When I go to the home page I am initially redirected to the Azure login page. Authentication appears to work correctly and I am directed back to the Amundsen home page. However when I attempt to search nothing happens in the frontend. I can see in the logs that I am getting the following error:
File "/usr/local/lib/python3.7/site-packages/amundsen_frontend-4.0.1-py3.7.egg/amundsen_application/log/action_log.py", line 85, in _build_metrics
metrics['user'] = flask_app.config['AUTH_USER_METHOD'](flask_app).email
AttributeError: 'dict' object has no attribute 'email'
With my limited python debugging capabilty I can see that the get_auth_user() function is returning the dict:
{'_schema': ['"display_name", "full_name", or "email" must be provided']}
It might imply that we are not getting an email field back from the Azure server but when I check the config url it definitely lists "email" in the claims_supported list.
When I experimented with different values for FLASK_OIDC_USER_ID_FIELD I got:
Make sure to set the proper 'FLASK_OIDC_USER_ID_FIELD' env variable to match with your OIDC Provider.'email' is not present in the response from OIDC Provider.
Available Keys are: (aud, iss, iat, nbf, exp, name, nonce, oid, preferred_username, rh, sub, tid, uti, ver).
The claims_supported list from the config url is:
claims_supported: [ "sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email" ]
I'm not sure where to go from here.
Any help appreciated.
Cheers,
John
I am getting CONFIG_URL is not set error. Can you guide how to setup? I am trying to setup in superset
Having a field like last_login_timestamp
to store the last time User logged into the application would be useful to do analysis on the number of active users and similar trends. Would be happy to make this addition if community finds it useful as well.
This requires a bit of manual debugging otherwise. Not sure where in the library it would make sense to put this
Right now the support is given for a few OpenIDConnect configurations. But some important ones are missed.
Another problem is hardcoded value of SESSION_TYPE.
Support providing any number of configurations without knowing the keys. Some important ones are :
Follow a pattern of FLASK_OIDC_some_magic_key=same_fancy_value
to generate BaseConfig.som_magic_key = some_fancy_value
and scrape all env vars.
Its good to have default values but bad to hardcode those and not giving a choice to override.
Tomorrow if flask_oidc introduces a new config variable, u dont have to maintain ur lib for that.
I was using amundsen frontend with flaskoidc as auth layer wrapper, and following is the kubernetes deployment architecture :
Browser(Client) --(HTTPS)--> Nginx Ingress Controller --(HTTP, port:5000)--> Amundsen-Frontend-Pod
So flask_oidc was detecting the wrong redirect_uri, (which I was able to fix by using proxy_redirect, aka find and replace in location header for 3xx) as well as it is sending a "state" variable which holds the redirect_uri in a JWT. So no escape by hacking the nginx router. Only solution is injecting OVERWRITE_REDIRECT_URI.
Also as I'm using k8s, things are under extreme isolation and high security. So I dont want to slow down my website with a sqlite session store. So want SESSION_TYPE also configurable.
echo " OVERWRITE_REDIRECT_URI = os.environ.get('FLASK_OIDC_OVERWRITE_REDIRECT_URI', 'False') " \
>> /usr/local/lib/python3.7/site-packages/flaskoidc/config.py
I'm facing an issue where after some time, visiting an endpoint results in an infinite loop of 302 redirects to login and logout. I believe the issue is as follows (if you think this doesn't make sense, please tell me):
Normally, the following happens:
The problem is that a user may have multiple sessions in the table. The app uses .first()
to get just one token from the table, but that means it will get the oldest token. So if a user's session has expired, then when they next visit they will be
3
)I don't know enough about the implementation to be sure as to the solution, but I imagine that either 1) you order the results (descending) by the expires_at
column (.order_by(OAuth2Token.expires_at.desc())
), or 2) make sure to delete the token if it has expired (I guess as an else block in _fetch_token
).
If you prefer, I'm happy to submit a PR if you indicate which direction the solution should take.
With my app behind a reverse proxy, and using path base routing upstream I need to add or specify the callback URL. How do I do that?
default: http://whatever/oidc_callback
I need: http://whatever/catalog/oidc_callback
Hi all !
I have been struggling with using Google as an authentication provider. Indeed, I've read many confusing things.
So what Google Cloud Platform API should I use ? I tried Identity Platform but I don't seem to get the necessary information for configuring my client app using flaskoidc (Amundsen) ?
Is there a simpler way of doing this with Google ?
Thanks
Line 8 in c609373
In setup.py
, flaskoidc requires a very specific version of the requests
package (not the newest version). If you have the newest version of requests installed, flaskoidc won't install (at least not using tools like poetry).
Would it be possible to change the setup.py
file to not require versions specifically (or something like requests>=2.25.1
)?
(this applies to the other requirements as well, but the mismatch I got right now was because of the requests
package)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.