Git Product home page Git Product logo

flask-oidc's Introduction

flask-oidc's People

Contributors

abompard avatar alex avatar bkabrda avatar cfehr247 avatar grakic avatar jeremycline avatar marcvs avatar mprahl avatar ncoghlan avatar puiterwijk avatar pypingou avatar relrod avatar rusox89 avatar ryansmith940 avatar sayanchowdhury avatar stanleykylee avatar steelpangolin avatar trevex 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  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  avatar  avatar  avatar  avatar

flask-oidc's Issues

Make JSON rendering configurable for error responses

Combining flask-oidc with Flask-RESTful for Anitya, we've run into a problem where the error responses that flask-oidc pre-renders to JSON strings are then rendered again as encoded JSON text responses by Flask-RESTful: fedora-infra/anitya#443

The lowest impact solution that occurred to me is to have accept_token accept an extra parameter (defaulting to json.dumps) that defines how the error rendering happens.

Document how to implement a persistent credential store

I see in the code that a memory based credential store is used by default and the existing docs mention in passing what to do but this is not clear to me. Could you document how to implement this via OIDC_CREDENTIALS_STORE?

"Note that you should probably provide the library with a place to store the credentials it has retrieved for the user. These need to be stored in a place where the user themselves or an attacker can not get to them. To provide this, give an object that has setitem and getitem dict APIs implemented as second argument to the init() call. Without this, the library will only work on a single thread, and only retain sessions until the server is restarted."
https://flask-oidc.readthedocs.io/en/latest/#flask_oidc.OpenIDConnect.init_app

State crashes due bad url encoding

Illegal character in query at index 42: http://localhost:5000/oidc_callback?state={"csrf_token": "OaYJMU9HSJVpZyJoJMcXCtwgQAG2xxx/", "destination": "eyJhbGciOiJIUzxxx.Imh0dHA6Ly9sb2NhbGhvc3Q6NTAwMC9wcml2YXRlIg.hjUDDvxxxEZpV5ILsdNvealTfdDUgoYPflpxxxupMRQ"}

The method redirect_to_auth_server where it does extra_params=urlencode(extra_params), is not working as expected.

userinfo_uri is not required... its optional but the library requires it...

Could there be option to configure the use of the userinfo_uri or not to use it?

CoreOS OpenId Connect Provider (https://github.com/coreos/dex) didn't implement the userinfo endpoint because it's optional.

In order to get this library to work with coreos openId connect provider, I had to comment out lines 188 thru 199
(https://github.com/puiterwijk/flask-oidc/blob/master/flask_oidc/__init__.py#L188-L199) for it to work with coreos openid connect provider.

The token is not refreshed after about 72 minutes

I don't know if it's the expected behaviour or not...
I have this flask app (Fedora Hubs) that is designed to be left open for a while and auto-refreshes the page contents. To avoid the user being disconnected, I have setup a call to a /ping URL that the Javascript makes every minute. This /ping url is not decorated, but since it's inside the Flask app and flask_oidc is used, it should benefit from the before_request hook that is automatically setup.

This hook calls authenticate_or_redirect() which should automatically refresh the OIDC token using the refresh token, except it apparently never happens, or does not work. I'll try to print some stuff to see what's wrong here.

Project dead?

Been no activity for some time, and no new cut of the latest version to pypi. Can we assume that this depo is dead?

oidc-register fails under Python 3.5

I'm attempting to adapt the Hubs OIDC code for use in Anitya, and ran into the following error trying to run oidc-register under a Python 3.5 venv:

$ oidc-register --debug https://iddev.fedorainfracloud.org/ http://localhost:5000
Error discovering OP information
the JSON object must be str, not 'bytes'
ERROR:oidc-register:Error caught when discovering OP information:
Traceback (most recent call last):
  File "/home/ncoghlan/.virtualenvs/anitya/lib/python3.5/site-packages/flask_oidc/registration_util.py", line 65, in main
    OP = discovery.discover_OP_information(args.provider_url)
  File "/home/ncoghlan/.virtualenvs/anitya/lib/python3.5/site-packages/flask_oidc/discovery.py", line 43, in discover_OP_information
    return json.loads(content)
  File "/usr/lib64/python3.5/json/__init__.py", line 312, in loads
    s.__class__.__name__))
TypeError: the JSON object must be str, not 'bytes'

"No module named flask.ext"

The documentation and the code examples direct me to write:

from flask.ext.oidc import OpenIDConnect

With flask-oidc == 1.4.0 this will produce the error ModuleNotFoundError: No module named 'flask.ext'

It appears one needs to write

from flask_oidc import OpenIDConnect

instead.

Please let me emphasize that as a newbie to python-flask and oidc it is especially frustrating to have introducing tutorial code giving errors like this :P

Issues with how basic auth is being done when in Python 3

I'm seeing a problem anywhere this library is using client secret basic as the auth mechanism. The issue boils down to:

 bas = 'foo:bar'
 bytes = bytearray(bas, 'utf-8')
 s = 'Basic %s' % b64encode(bytes)
 s
"Basic b'Zm9vOmJhcg=='"
 s2 = 'Basic %s' % b64encode(bytes).decode('utf-8')
 s2
'Basic Zm9vOmJhcg=='

In python 3.6.4, serializing bytes is outputting b'actual_value', which is then failing authentication. Adding a .decode('utf-8') results in a string, and the b'' isn't added anymore. This doesn't manifest in python 2.

The following works in both python 2 and 3:

 bas = 'foo:bar'
 bytes = bytearray(bas, 'utf-8')
 s = str('Basic %s' % b64encode(bytes).decode('utf-8'))
 s

Would this be something you're willing to incorporate? I can get a PR ready for this if so. (I think this affects #44 as well).

I made some changes

Hey Jeremy,

sorry for the undescriptive title. Since Google is shutting down their OpenID provider and I'm the master of procrastination, I had to hurry porting some of my apps that relied on it for login over to OpenID Connect. I found your project here, but wanted to minimize the changes I had to make in the individual apps. So, I made some changes.

I didn't think opening a pull request against your repo would have been a good idea as the changes are obviously backwards-incompatible, but I still wanted to let you know as a courtesy.

Most of the changes are contained in this commit if you fancy cherry-picking anything from it:
passy@26c6721

Thanks for the project!

JWT stored in Cookie limits JWT length to 4kb

When any ID token or user info endpoint call results in data greater than 4kb the cookie storage fails. This limits the authorization and identity data that can be relayed by the authorization server to the client arbitrarily.

OIDC id token cookie path set to / and unconfigurable

I am deploying my testing OIDC webapp which is mounted to /oidedemo in uwsgi, but I found that the OIDC id token cookie is set to / instead of /oidcdemo.

As per flask Response.set_cookie, if "path" is not set it default to /, which I believe it's unreasonable for privacy and security. (I have several independent application running on each server, and it's better that every application cannot read other's cookies)

I believe flask-oidc should let the user to configure cookie path instead of missing it entirely.

Need stability for non-json responses from IdP

Sometimes the IdP returns non-json results on the UserInfo page (most notably when it was signed) as it will be a JWT.
We should handle those responses and error out (another ticket will be filed to support signed UserInfo responses).

Refresh token

Does it exist a method to only refresh the token?

I would like to refresh information in the credentials store (access_token and refresh_token) with an API, I can read the tokens and using them to obtain a new token. How can I write it to credentials store?

Allow extra parameters when constructing auth_url

We have no way of easily modifying the extra parameters when constructing the auth_url.

In OpenIDConnect::redirect_to_auth_server there is a local variable that adds parameters to the auth url -
extra_params = { 'state': urlsafe_b64encode(json.dumps(state).encode('utf-8')), }

I currently modify the auth_url via my client_secrets.json, but it would be useful to do this programmatically (e.g. In production I would like to add the parameter prompt=consent but not in development)

Clock skew

With my PC clock 5 seconds behind the true time, authentication fails at is_id_token_valid, "steps 9, 10". I think there ought to be some allowance for clock skew.

(But to fix it locally, I set my clock forward 5.5. seconds.)

(Actually, what's the problem with the local machine's system time being before the authentication provider's system time at the creation of the token?)

access_token and resource_server

For access_token based API calls, there's no way if getting any information, as get_access_token uses the sub of the id_token. If a user's not logged in (e.g. API call with Bearer token), then it blows up. Looking up the access token from the header, and passing it to oidc._get_token_info(token) does introspect and give the token details though. Is there something wrong with this approach?

Resource server documentation fix

In the Resource server paragraph, the docs state:

"Also, if you have implemented an API that can should be able to accept tokens
issued by the OpenID Connect provider, just decorate those API functions with. . . "

I believe this should read:

"Also, if you have implemented an API that should be able to accept tokens
issued by the OpenID Connect provider, just decorate those API functions with. . ."

I would be happy to make this change if it is needed.

Make OP information discovery endpoint configurable

While trying to use oidc-register with an instance of ipsilon, I failed to use oidc-register to retrieve client secrets. The reason for that was that ipsilon (in default config) doesn't respond to the endpoint hardcoded at [1]. Therefore I'd like to propose that the name of the endpoint (e.g. the current .well-known/openid-configuration) should be configurable (e.g. with a commandline argument).

(What worked for me was replacing that value with openidc/wellknown_openid_configuration).
Thanks for considering!

[1]

'%s/.well-known/openid-configuration' % OP_uri)

oidc_id_token cookie > 4k ... ignored by chrome

Possibly related to #42

Using flask-oidc in a web application against our enterprise ID server. Our id token includes group information that can be quite extensive in the case the user belongs to 100s of groups or more.

In one particular case, we're generating a cookie of size 4119 bytes.. and it appears chrome is ignoring this cookie, and therefore we get thrown into a 302 re-direct loop as the client repeatedly tries to access /, gets redirected to the oidc callback which creates a cookie chrome is going to ignore, and the browser tries / again...

I believe the assumption that the cookie (serialized JWT oidc_id_token) will be < 4k is flawed, and if so, not sure there is a clean work around. In our case, I was able to subclass the JWT serializer, and remove all the group membership info before serialization. This works, but binds us to knowing the internals of this package as well as what is being returned by our auth server.

additional info... chrome fails, firefox/ie work w/ the 4119 byte cookie above. other users have more group information associated with them, but haven't been through the web app yet.

Internal server error from Ipsilon when calling user_getfield()

Working on Anitya's OIDC integration, I'm sending the access token to the server as part of the request's query string. The token validation decorator correctly finds it there, and sets the flask global g.oidc_token_info. However, the global that the rest of flask-oidc is looking for is g.oidc_id_token, and it never checks g.oidc_token_info at all (even as a fallback), so I need to pass the access token explictly to the API as follows: APP.oidc.user_getfield('user_id', access_token).

access_token is retrieved from the request using flask-reqparse:

_BASE_ARG_PARSER = reqparse.RequestParser(trim=True, bundle_errors=True)
_BASE_ARG_PARSER.add_argument('access_token', type=str)

And then in the request itself, the initial parser is created via _BASE_ARG_PARSER.copy()

But when I do that, the call to user_getfield fails with the following traceback:

  File "/home/ncoghlan/fedoradevel/anitya/anitya/api_v2.py", line 65, in post
    user_id=APP.oidc.user_getfield('user_id', access_token),
  File "/home/ncoghlan/.virtualenvs/anitya/lib/python3.5/site-packages/flask_oidc/__init__.py", line 167, in user_getfield
    info = self.user_getinfo([field], access_token)
  File "/home/ncoghlan/.virtualenvs/anitya/lib/python3.5/site-packages/flask_oidc/__init__.py", line 196, in user_getinfo
    all_info = self._retrieve_userinfo(access_token)
  File "/home/ncoghlan/.virtualenvs/anitya/lib/python3.5/site-packages/flask_oidc/__init__.py", line 241, in _retrieve_userinfo
    info = _json_loads(content)
  File "/home/ncoghlan/.virtualenvs/anitya/lib/python3.5/site-packages/flask_oidc/__init__.py", line 52, in _json_loads
    return json.loads(content)
  File "/usr/lib64/python3.5/json/__init__.py", line 319, in loads
    return _default_decoder.decode(s)
  File "/usr/lib64/python3.5/json/decoder.py", line 339, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib64/python3.5/json/decoder.py", line 357, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

The debug logs show the following response from Ipsilon:

flask_oidc: DEBUG: Retrieved user info: b'<!DOCTYPE html>\n<!--[if IE 8]><html class="ie8 login-pf"><![endif]-->\n<!--[if gt IE 8]><!-->\n<html class="login-pf">\n<!--<![endif]-->\n  <head>\n    <title>Internal Error</title>\n    <meta charset="UTF-8">\n    <meta name="viewport" content="width=device-width, initial-scale=1.0">\n    <link href="/ui/css/ipsilon.css" rel="stylesheet" media="screen, print">\n    <link href="/ui/css/styles.css" rel="stylesheet" media="screen, print">\n  </head>\n  <body>\n    <a href="/" id="badge" tabindex="-1">\n      <img src="/ui/img/logo.svg" alt="Ipsilon IdP" />\n    </a>\n    <div class="container">\n      <div class="row">\n        <div class="col-sm-12">\n          <div id="brand">\n            <img src="/ui/img/brand-lg.png" alt="Ipsilon">\n          </div>\n        </div>\n        \n<div class="col-sm-12">\n  <h1>500 - Internal Server Error</h1>\n  \n    <p>Ipsilon encountered an unexpected internal error while trying to\n       fulfill your request.</p>\n  \n  <p>Please retry again.</p>\n  <p>If the error persists, contact the server administrator to resolve\n     the problem.</p>\n</div>\n\n      </div>\n    </div>\n  </body>\n</html>'

Traceback on errors during the refresh process

On Hubs I get this traceback sometimes:

Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/flask/app.py", line 1988, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/lib/python3.6/site-packages/flask/app.py", line 1641, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/lib/python3.6/site-packages/flask/app.py", line 1544, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/usr/lib/python3.6/site-packages/flask/app.py", line 1637, in full_dispatch_request
    rv = self.preprocess_request()
  File "/usr/lib/python3.6/site-packages/flask/app.py", line 1837, in preprocess_request
    rv = func()
  File "/usr/lib/python3.6/site-packages/flask_oidc/__init__.py", line 324, in _before_request
    self.authenticate_or_redirect()
  File "/usr/lib/python3.6/site-packages/flask_oidc/__init__.py", line 362, in authenticate_or_redirect
    credentials.refresh(httplib2.Http())
  File "/usr/lib/python3.6/site-packages/oauth2client/client.py", line 545, in refresh
    self._refresh(http)
  File "/usr/lib/python3.6/site-packages/oauth2client/client.py", line 749, in _refresh
    self._do_refresh_request(http)
  File "/usr/lib/python3.6/site-packages/oauth2client/client.py", line 786, in _do_refresh_request
    self.access_token = d['access_token']
KeyError: 'access_token'

I've printed the value of the variable d and it's {'error_description': 'Something went wrong refreshing', 'error': 'invalid_grant'}.

Logout

What's the right way to logout the user? Explicitly expire the oidc_id_token cookie?

add documentation for usage with custom identity provider

Hi,

I would like to use this project to test an OpenID Connect Identity Provider that I am developing, based on the Django OIDC Provider.

Not all Identity Providers provide a client_secrets.json file, so it isn't clear to me how to get that or what values to put in it.

Also, when setting up the client in my identity provider, I need to provide information about which flow is being used, and what the redirect URI(s) are, and I'm not sure what to put in.

If the README had just a few sentences about how to use this with a custom identity provider, that would be great.

Thanks.

Issue with custom certification authority

Hi.

flask-oidc==1.3.0
python 3.6.5
flask 0.12.2
ubuntu 16.4.3

I setup everything to login with keycloak. Everything worked as expected if run against keycloak over HTTP.

Then I configured keycloak over HTTPS with a certificate created from a self signed CA:

  • flask redirect the browser to keycloak (no SSL issue because I imported the custom CA on my laptop)
  • I can do the login phase on keycloak
  • keycloak redirect the browser to flask (/oidc_callback)
  • flask return http code 500, error:
::ffff:172.18.0.11 - - [2018-03-31 21:19:23] "GET / HTTP/1.1" 302 2133 0.031266
Traceback (most recent call last):
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/gevent/pywsgi.py", line 935, in handle_one_response
    self.run_application()
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/gevent/pywsgi.py", line 908, in run_application
    self.result = self.application(self.environ, self.start_response)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/flask_oidc/__init__.py", line 650, in _oidc_callback
    credentials = flow.step2_exchange(code)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/oauth2client/_helpers.py", line 133, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/oauth2client/client.py", line 2054, in step2_exchange
    http, self.token_uri, method='POST', body=body, headers=headers)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/oauth2client/transport.py", line 282, in request
    connection_type=connection_type)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/httplib2/__init__.py", line 1514, in request
    (response, content) = self._request(conn, authority, uri, request_uri, method, body, headers, redirections, cachekey)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/httplib2/__init__.py", line 1264, in _request
    (response, content) = self._conn_request(conn, request_uri, method, body, headers)
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/httplib2/__init__.py", line 1187, in _conn_request
    conn.connect()
  File "/root/.virtualenvs/spy-registry-kjDCNykl/lib/python3.6/site-packages/httplib2/__init__.py", line 1013, in connect
    self.sock = self._context.wrap_socket(sock, server_hostname=self.host)
  File "/opt/python36/lib/python3.6/ssl.py", line 407, in wrap_socket
  _context=self, _session=session)
 File "/opt/python36/lib/python3.6/ssl.py", line 814, in __init__
   self.do_handshake()
 File "/opt/python36/lib/python3.6/ssl.py", line 1068, in do_handshake
   self._sslobj.do_handshake()
 File "/opt/python36/lib/python3.6/ssl.py", line 689, in do_handshake
   self._sslobj.do_handshake()
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:833)
Sat Mar 31 21:19:23 2018 {'REMOTE_ADDR': '::ffff:172.18.0.11', 'REMOTE_PORT': '46558', 'HTTP_HOST': '10.100.100.10:6000', (hidden keys: 30)} failed with SSLError

I suppose the problem is in these rows (flask_oidc/__init__.py, method _oidc_callback):

        # make a request to IdP to exchange the auth code for OAuth credentials 
        flow = self._flow_for_request()                                         
        credentials = flow.step2_exchange(code)                                 
        id_token = credentials.id_token 

I think flask tries to communicate with keycloak and the SSL handshake fails.

Custom CA cert is imported in system ca store where flask runs.

I tried to use the following env vars:

  • REQUESTS_CA_BUNDLE
  • PYTHONHTTPSVERIFY
  • SSL_CERT_DIR
  • SSL_CERT_FILE

but no way to get rid of the issue.

I tried to follow the code, but I missed myself in it.

Thanks

Cut new build to pypi?

I was wondering if you could please incorporate #72 and get a new build on PyPi? Last one was in April. Thank you!

TODO: step 2: check issuer

This smells like a massive security flaw. What does it mean? What impact does not checking issuer have? Can flask-oidc be considered secure if it's not checking the issuer?

Allow client_secrets as a dictionary

Currently the load_secrets function will always look for a file to open and read. Is there a reason for not allowing this function to load the secrets from a dictionary passed in instead of loading from a file?

I do not want to save a static file on my root directory with all the secrets. I would like to be able to obtain the secrets programatically and not have to store any secrets on the file system.

Redirect to original page upon authentication

I'm using the library against keycloak for authentication to secure the REST API endpoints. If a user accesses one of many REST API endpoints, he is redirected to the keycloak page. Following are the settings, of which only OVERWRITE_REDIRECT_URI matters for this issue:

app.config.update({ 'SECRET_KEY': 'keycloak client secret key', 'TESTING': True, 'DEBUG': True, 'OIDC_CLIENT_SECRETS': resource_filename(__name__, 'client_secrets.json'), 'OIDC_ID_TOKEN_COOKIE_SECURE': False, 'OIDC_REQUIRE_VERIFIED_EMAIL': False, 'OIDC_OPENID_REALM': '<realm name>', 'OIDC_INTROSPECTION_AUTH_METHOD': 'client_secret_post', 'OIDC_TOKEN_TYPE_HINT': 'access_token', 'OVERWRITE_REDIRECT_URI': 'http://myappp:8000' })

oidc.init_app(app)

If I access an endpoint such as https://myapp:8000/api/v1/myendpoint, then the user is upon authentication redirected to the above configured value of OVERWRITE_REDIRECT_URI. However, I'd like the redirection to be to the URI from which the request originated i.e. https://myapp:8000/api/v1/myendpoint

Is it possible assure that the user is redirected to the original page or not?

No OpenIDConnect method to obtain access/refresh token?

It appears that OpenIDConnect has no object method to obtain access/refresh token. The only way I can figure out appears to be getting the credentials from credentials_store:

oidc = OpenIDConnect()
(...code validating/obtaining id_token...)
sub = oidc.user_getfield("sub")
credentials = oidc.credentials_store[sub]
access_token = credentials["access_token"]
refresh_token = credentials["refresh_token"]

I believe flask-oidc should provide method to obtain access/refresh token easily.

500 when server redeploys with different secret key

Hi,
because of a security incident, I had to replace SECRET_KEY of my server (Pagure 4.0.3) using flask-oidc 1.4.0. On redeploy with the new secret key, previously logged in users started getting this error:

[Thu May 24 09:00:44.419300 2018] [:error] [pid 31] 2018-05-24 09:00:44,351 [ERROR] flask.app: Exception on /rpms/setup [GET]
[Thu May 24 09:00:44.419316 2018] [:error] [pid 31] Traceback (most recent call last):
[Thu May 24 09:00:44.419318 2018] [:error] [pid 31]   File "/usr/lib/python2.7/site-packages/flask/app.py", line 2292, in wsgi_app
[Thu May 24 09:00:44.419320 2018] [:error] [pid 31]     response = self.full_dispatch_request()
[Thu May 24 09:00:44.419322 2018] [:error] [pid 31]   File "/usr/lib/python2.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
[Thu May 24 09:00:44.419324 2018] [:error] [pid 31]     rv = self.handle_user_exception(e)
[Thu May 24 09:00:44.419326 2018] [:error] [pid 31]   File "/usr/lib/python2.7/site-packages/flask/app.py", line 1718, in handle_user_exception
[Thu May 24 09:00:44.419328 2018] [:error] [pid 31]     reraise(exc_type, exc_value, tb)
[Thu May 24 09:00:44.419330 2018] [:error] [pid 31]   File "/usr/lib/python2.7/site-packages/flask/app.py", line 1811, in full_dispatch_request
[Thu May 24 09:00:44.419332 2018] [:error] [pid 31]     rv = self.preprocess_request()
[Thu May 24 09:00:44.419333 2018] [:error] [pid 31]   File "/usr/lib/python2.7/site-packages/flask/app.py", line 2087, in preprocess_request
[Thu May 24 09:00:44.419335 2018] [:error] [pid 31]     rv = func()
[Thu May 24 09:00:44.419337 2018] [:error] [pid 31]   File "/usr/lib/python2.7/site-packages/flask_oidc/__init__.py", line 406, in _before_request
[Thu May 24 09:00:44.419338 2018] [:error] [pid 31]     self.authenticate_or_redirect()
[Thu May 24 09:00:44.419340 2018] [:error] [pid 31]   File "/usr/lib/python2.7/site-packages/flask_oidc/__init__.py", line 426, in authenticate_or_redirect
[Thu May 24 09:00:44.419342 2018] [:error] [pid 31]     id_token = self._get_cookie_id_token()
[Thu May 24 09:00:44.419344 2018] [:error] [pid 31]   File "/usr/lib/python2.7/site-packages/flask_oidc/__init__.py", line 352, in _get_cookie_id_token
[Thu May 24 09:00:44.419346 2018] [:error] [pid 31]     return self.cookie_serializer.loads(id_token_cookie)
[Thu May 24 09:00:44.419347 2018] [:error] [pid 31]   File "/usr/lib/python2.7/site-packages/itsdangerous.py", line 795, in loads
[Thu May 24 09:00:44.419349 2018] [:error] [pid 31]     self, s, salt, return_header=True)
[Thu May 24 09:00:44.419351 2018] [:error] [pid 31]   File "/usr/lib/python2.7/site-packages/itsdangerous.py", line 749, in loads
[Thu May 24 09:00:44.419353 2018] [:error] [pid 31]     self.make_signer(salt, self.algorithm).unsign(want_bytes(s)),
[Thu May 24 09:00:44.419354 2018] [:error] [pid 31]   File "/usr/lib/python2.7/site-packages/itsdangerous.py", line 374, in unsign
[Thu May 24 09:00:44.419356 2018] [:error] [pid 31]     payload=value)
[Thu May 24 09:00:44.419358 2018] [:error] [pid 31] BadSignature: Signature 'o3c13YeSK_1_Mqrf9t7HoCi-tSXlkCBl6fW2JC0RHA8' does not match

Users who weren't logged in previously could access the server and log in just fine. I think this error should be caught by flask-oidc and user logged out as consequence of it. Does that make sense?

Issue when working with Hydra

I'm looking to use this with Hydra. When handling oidc_callback, I get an error:

127.0.0.1 - - [22/Nov/2017 17:48:25] "GET /oidc_callback?code=EOG_WJio6_5E632BPlq8AMbV7z_gwIKTbYWxz5NIvgw.ULGZ9UBhO26ZtBds-wnMJ5rMN9wC35NQbV-CEIk7RYA&scope=openid&state=eyJjc3JmX3Rva2VuIjogIjJZR2JsbzR5TGlqc0lSZ2NvZXplU3dFcDhHMmFwUklBIiwgImRlc3RpbmF0aW9uIjogImV5SmhiR2NpT2lKSVV6STFOaUo5LkltaDBkSEE2THk5c2IyTmhiR2h2YzNRNk5UQXdNQzlpYjI4aS5xUU9OYmNyNlVuVGROY1ltLXQwa3ZUam9JcnFQbDAzbUxVU1hSQjNQTFhFIn0%3D HTTP/1.1" 500 -
Error on request:
Traceback (most recent call last):
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/werkzeug/serving.py", line 209, in run_wsgi
    execute(self.server.app)
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/werkzeug/serving.py", line 197, in execute
    application_iter = app(environ, start_response)
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/flask_oidc/__init__.py", line 650, in _oidc_callback
    credentials = flow.step2_exchange(code)
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/oauth2client/_helpers.py", line 133, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/mnt/e/ashicX/.conda/envs/flask/lib/python3.6/site-packages/oauth2client/client.py", line 2089, in step2_exchange
    raise FlowExchangeError(error_msg)
oauth2client.client.FlowExchangeError: invalid_requestThe request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed

On the hydra logs, I see this:

time="2017-11-22T17:48:25Z" level=info msg="started handling request" method=POST remote="172.30.0.1:42492" request=/oauth2/token
time="2017-11-22T17:48:25Z" level=error msg="An error occurred" error="HTTP authorization header missing or invalid: The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed"
time="2017-11-22T17:48:25Z" level=info msg="completed handling request" measure#http://localhost:4444.latency=126861 method=POST remote="172.30.0.1:42492" request=/oauth2/token status=400 text_status="Bad Request" took="126.861µs"

It appears hydra wants "code" to be in the Authorization Header. ory/hydra#174 would suggest as much. Is there a way around this? It appears the (now deprecated) oauth client is putting the code in the POST body.

Deprecate this library

Google recently deprecate oauth2client in favor of google-auth. Since this library leans so heavily on the oauth2client, it might be good to deprecate this library or, at the very least, warn of the deprecation of oauth2client.

Python 3 incompatibility

Running oidc-discover on Python3 can lead to a traceback:

ERROR:oidc-register:Error caught when discovering OP information:
Traceback (most recent call last):
  File "[...]/lib/python3.5/site-packages/flask_oidc/registration_util.py", line 65, in main
    OP = discovery.discover_OP_information(args.provider_url)
  File "[...]/lib/python3.5/site-packages/flask_oidc/discovery.py", line 43, in discover_OP_information
    return json.loads(content)
  File "/usr/lib64/python3.5/json/__init__.py", line 312, in loads
    s.__class__.__name__))
TypeError: the JSON object must be str, not 'bytes'

This is because httplib2 returns the content as bytes, not strings, and json.loads() expects strings. I have a patch to fix this, I'll submit a PR right now.

cannot "get" on a list - only on a dict

When trying to initialize flask-oidc in my application, I am seeing this error:
screen shot 2018-06-14 at 2 54 40 pm

When debugging, I can see in the source code for flask-oidc that the dict recieved from my client_secrets.json file is being converted to a list, then below the code is trying to access 'issuer' from that list.
Since you cannot get key 'issuer' from a list of values, this is breaking the application.

screen shot 2018-06-14 at 2 53 01 pm

The application is using python 3.6.

Token introspection uses a wrong value for token_type_hint

The _get_token_infomethod of the OpenIDConnect class currently uses 'Bearer' as
the value for the token_type_hint parameter.
See:

'token_type_hint': 'Bearer'}

According to the spec, only access_token or refresh_token are allowed as values for the token_type_hint parameter.

See:
https://tools.ietf.org/html/rfc7662

Values for this field are defined in the "OAuth Token Type Hints" registry defined
in OAuth Token Revocation [RFC7009].

The typ field in the JWT must be mapped to the following token_type_hint values:

  • typ: Bearer --> access_token
  • typ: Refresh --> refresh_token

Another option may be to not set the token_type_hint parameter at all.

See also:
https://connect2id.com/products/server/docs/api/token-introspection

I stumbled upon this issue whilst trying to use Keycloak in combination with flask-oidc. When I send the token info request without the token_type_hint parameter (by patching the flask-oidc lib) then the token info can be retrieved from Keycloak token_introspection_endpoint.

Key reuse is potentially dangerous

flask-oidc creates two JWS signers using the Flask secret key:

# create signers using the Flask secret key
self.extra_data_serializer = JSONWebSignatureSerializer(
app.config['SECRET_KEY'])
self.cookie_serializer = TimedJSONWebSignatureSerializer(
app.config['SECRET_KEY'])

In addition, Flask itself creates a signer [using the same key].(https://github.com/pallets/flask/blob/7e714bd28b6e96d82b2848b48cf8ff48b517b09b/flask/sessions.py#L330-L332)

In general, it's unsafe to reuse a cryptographic key for multiple purposes. The proper construction is to use a KDF like HKDF to derive separate keys for each application.

I'm not sure if this is actually exploitable in this case, but if it is, one shape would look like:

  • Because each of these signers is signing with an HMAC over the same key, the output of any one of them could be fed back into another, which will then accept the signature, since it's signed using the same key.
  • This potentially means that an attacker could e.g. swap the session cookie and the OIDC_ID_TOKEN_COOKIE_NAME cookie, which is certainly surprising at best.

I haven't verified that the outputs are sufficiently compatible for the attack to actually work, but ideally the code wouldn't rely on details of slightly-differing output formats for an important security property.

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.