Giter Club home page Giter Club logo

flaskoidc's Introduction

flaskoidc's People

Contributors

cccs-eric avatar danucas avatar jacobnosal avatar mgorsk1 avatar ozandogrultan avatar verdan avatar vilhelmprytz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

flaskoidc's Issues

OAuth2Token model missing "last_login_timestamp" field

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.

Infinite login/logout loop if `expires` of _earliest_ session is earlier than current time

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:

  1. When a user first visits, a session is created for them in the database.
  2. When a user next visits, they are authenticated from the session in the database, and their token / session is refreshed as well.
  3. If the session has expired, the user is logged out, and redirected to login.

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

  1. logged out (because the token has expired)
  2. logged in (as expected)
  3. logged out again (because the "first" / earliest token is still expired and wasn't deleted)
  4. they are logged back in again (and continue again from 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.

There are many more configurations which can be injected from env, e.g OVERWRITE_REDIRECT_URI

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.

Where?

https://github.com/verdan/flaskoidc/blob/76d168d680d11bd29456825075a4b5e71c79b7bf/flaskoidc/config.py

What?

Support providing any number of configurations without knowing the keys. Some important ones are :

  1. SESSION_TYPE
  2. OVERWRITE_REDIRECT_URI
  3. LOG_LEVEL

How?

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.

Context?

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.

Current hack

echo "    OVERWRITE_REDIRECT_URI = os.environ.get('FLASK_OIDC_OVERWRITE_REDIRECT_URI', 'False') " \
    >>  /usr/local/lib/python3.7/site-packages/flaskoidc/config.py

Issues using Azure AD

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

How do I set the Callback Route?

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

sqlalchemy version mismatch

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!

`json.loads(token)` decode error because token is JWT, not json

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

V 1.0.6 does not respect HTTPS protocol when forwarded from a proxy.

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.

[Feature request] Give way for injecting non-string configs from environment

You have recently introduced generic env variable based configuration injection at

for key, value in dict(os.environ).items():
if (
key.startswith(OIDC_ATTR_KEY) or
key.startswith(FLASK_SESSION_ATTR_KEY) or
key.startswith(SQLALCHEMY_ATTR_KEY)
):
locals()[key] = value
elif key in EXCEPTIONAL_KEYS:
locals()[key] = value

This is instant relief to pass n no of configs directly to flask-oidc.
But flask app often needs configs of various types. Example

  1. Config of non string type. OIDC_ID_TOKEN_COOKIE_TTL
  2. Config of dict type. OIDC_CLIENT_SECRETS (it is union type of either string, i.e. json file path or dict holding the actual configs)
  3. A python class/function/object. CUSTOM_SECURITY_MANAGER

Suggestions:

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__)

How to create a compatible OIDC provider on Google ?

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

Hard-coded version requirements causes tools like poetry to fail

"requests==2.25.1",

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)

Set expiration time

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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.