Giter Site home page Giter Site logo

oidcendpoint's People

Contributors

angelakis avatar jschlyter avatar nagylzs avatar nsklikas avatar peppelinux avatar rohe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

oidcendpoint's Issues

Token endpoint

Currently there are two Endpoints (as in oidcendpoint classes) that implement the token endpoint, the AccessToken (src/oidcendpoint/oidc/token.py) and RefreshAccessToken (src/oidcendpoint/oidc/refresh_token.py).

In order to implement the token endpoint in SATOSA I would have to check the grant_type in the request and call the corresponding oidcendpoint endpoint (ie if grant_type=refresh_token then use RefreshAccessToken). This decision should be made in oidcendpoint since it is the one parsing the request.

Shouldn't these two classes be merged?

Scopes per RP

Is it possible to support scopes allowed per RP/client? So that each client is allowed to only ask for a subset of supported scopes. It could be configured/saved in the client database.

Session management

My present thinking is this:

When a user completes a successful authentication at an OP a session is created.
There is one session per user_id and client_id combination.

That means that if the user sends an authentication request from another client and SSO is used then a new session is created. The 2 sessions have an authentication event in common.
If SSO was not allowed then the 2 sessions, even if they concerned the same user_id, would not have a common authentication event.

This means that at the top of the session management tree we would have a number of authentication events.

Now within a session grants can be given. These grants can lead to authorization codes, access tokens and/or refresh tokens to be issued.

So we have a hierarchy:

  1. Authentication Event
  2. Session
  3. Grant

This would allow us easy handling of single logout as well as dealing with grant management as described in
Grant Management

There are connections between grants, for instance it's useful to know which refresh token was used to issue which access token.

client_id in Access Token aud

With the help of django-oidc-op/snippts/rp_hanlder.py here I post the debugging information regarding an ordinary oidcendpoint/oidcrp session.

in OAuth2 aud it's optional, as described here:
https://tools.ietf.org/html/rfc7519#section-4.1.3

In OIDC not: https://openid.net/specs/openid-connect-core-1_0.html#IDToken

python3 snippets/rp_handler.py -c example/data/oidc_rp/conf.django.yaml -u that_user -p that_password -iss django_oidc_op
Client registration done...
Connecting to Authorization url:
 {
  "url": "https://127.0.0.1:8000/authorization?redirect_uri=https%3A%2F%2F127.0.0.1%3A8099%2Fauthz_cb%2Fdjango_oidc_op&scope=openid+that_scope+profile+email+address+phone&response_type=code&nonce=wCn0Bncr7m6sRO10P5f7SA5o&state=ytSp5K8X5XvE5RCfEFmEpHqHZVn5kYgx&code_challenge=ycWJAoBgUEH9NyRPEsUJwvRtTUAsDRMKvMecaLs9d_8&code_challenge_method=S256&client_id=1UUl6cwNigmj",
  "state": "ytSp5K8X5XvE5RCfEFmEpHqHZVn5kYgx"
}


The Authorization endpoint returns a HTML authentication form with a token
 {
  "token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJXdG9SekV4VXkxak9GVXlSV2hwZUdkbFREWlBaME55TW1ka05ERlFaakJSUzJreVQwaExVazVJUVEifQ.eyJhdXRobl9jbGFzc19yZWYiOiAib2lkY2VuZHBvaW50LnVzZXJfYXV0aG4uYXV0aG5fY29udGV4dC5JTlRFUk5FVFBST1RPQ09MUEFTU1dPUkQiLCAicXVlcnkiOiAicmVkaXJlY3RfdXJpPWh0dHBzJTNBJTJGJTJGMTI3LjAuMC4xJTNBODA5OSUyRmF1dGh6X2NiJTJGZGphbmdvX29pZGNfb3Amc2NvcGU9b3BlbmlkK3RoYXRfc2NvcGUrcHJvZmlsZStlbWFpbCthZGRyZXNzK3Bob25lJnJlc3BvbnNlX3R5cGU9Y29kZSZub25jZT13Q24wQm5jcjdtNnNSTzEwUDVmN1NBNW8mc3RhdGU9eXRTcDVLOFg1WHZFNVJDZkVGbUVwSHFIWlZuNWtZZ3gmY29kZV9jaGFsbGVuZ2U9eWNXSkFvQmdVRUg5TnlSUEVzVUp3dlJ0VFVBc0RSTUt2TWVjYUxzOWRfOCZjb2RlX2NoYWxsZW5nZV9tZXRob2Q9UzI1NiZjbGllbnRfaWQ9MVVVbDZjd05pZ21qIiwgInJldHVybl91cmkiOiAiaHR0cHM6Ly8xMjcuMC4wLjE6ODA5OS9hdXRoel9jYi9kamFuZ29fb2lkY19vcCIsICJpc3MiOiAiaHR0cHM6Ly8xMjcuMC4wLjE6ODAwMCIsICJpYXQiOiAxNTk1OTMyNzI3fQ.VKnZZmWuHuOZjgaUUn7A5X5TjZaGeuuv8AjpwMkYdtmpxr31GEEOnmltjU3burmIZV1qOZC4vnRTZntAXO8GflwRkjtKBPvewGqkz4etHVZEkHZ3nKMG8zolFuU7xdYuV9wUok0ZzNh52qWcLhGOHTvBsfHB5gN7JXYSKF33Ii1JlwYL--nJLuIQRvV2MjyzzS01GGJ_Zlk2zWaox7MsWQeTcFk4HBnfaGc1ugjVJsMqpNwRmWvronVvU-93MvfVK46lhUQlvJuZNRJ2tlHc3JVvCDYmTfFk-MVlt_LhuTk90_u1G35lpX0klLavdgkOorUheJVVPsqCj9aME0GdqQ",
  "url": "verify/oidc_user_login/"
}


The Authorization returns a HttpRedirect (302) to https://127.0.0.1:8099/authz_cb/django_oidc_op?state=ytSp5K8X5XvE5RCfEFmEpHqHZVn5kYgx&scope=openid+that_scope+profile+email+address+phone&code=Z0FBQUFBQmZJQUE0YWFwUkdiMzhQM3oxNTkzNDZ4QlRQZjNlbUNIeXIwM1kwSkVZSHRzc0pueE01dndyZ2YxZXdzRVVGWGFlTXNmOFFGM3I3cW5iMEE5Uk9xXzFJQzRuN0tOd0ZrVzJwYlk3M2xIa3pCRGh4eUgySTRIaE9aVlhQSDFGenFwTHduR0NDc0tmSUJ3d3RsaXdLRldIMjQ1STcxRU5oWUE1WHIwb3B4ZWU2V1ZldndsRjBSWU1wOUF4N3owcDFWV2QzSDZtcUQzU0JKUW5qemxPdzFOdE5SWnJ4VXJ3N3hpM0dlYTZSYkROSmZyNURQWT0%3D&session_state=bc627c1120c4bb6fc3c6296d24fe926c9740b0f7944ce0e0c55c65b6055b5085.w9fW3DOoKcYD3nvU&iss=https%3A%2F%2F127.0.0.1%3A8000&client_id=1UUl6cwNigmj
 {}


Bearer Access Token
 "eyJhbGciOiJFUzI1NiIsImtpZCI6IlQwZGZTM1ZVYUcxS1ZubG9VVTQwUXpJMlMyMHpjSHBRYlMxdGIzZ3hZVWhCYzNGaFZWTlpTbWhMTUEifQ.eyJzaWQiOiAiYzBlY2QxMTFjMTM5MmM1N2M2YjE3MWZkMmNiYjJkMzFjMGM2NjUyOGVhN2QwZGFlZTNkODk2YTgiLCAidHR5cGUiOiAiVCIsICJzdWIiOiAiMDc2ZWNjYTk0ZmU0NTQ2N2I0NDM1ZDhlZWFkMjE4OGFkMzc3MWUxMGZmNjcyY2UxOTMwYzA0YWE4NjI0MTgxYyIsICJpc3MiOiAiaHR0cHM6Ly8xMjcuMC4wLjE6ODAwMCIsICJpYXQiOiAxNTk1OTMyNzI4LCAiZXhwIjogMTU5NTkzNjMyOCwgImF1ZCI6IFsiMVVVbDZjd05pZ21qIiwgImh0dHBzOi8vMTI3LjAuMC4xOjgwMDAiXX0.tAyozYfL6EpbZ0v_31_pm6MbeuD5RSILqZuIyObks_vJEzUOU1qqi4zxt4jz05s002u8y795NZPMqlgjpNNWFw"


Access Token
 {
  "sid": "c0ecd111c1392c57c6b171fd2cbb2d31c0c66528ea7d0daee3d896a8",
  "ttype": "T",
  "sub": "076ecca94fe45467b4435d8eead2188ad3771e10ff672ce1930c04aa8624181c",
  "iss": "https://127.0.0.1:8000",
  "iat": 1595932728,
  "exp": 1595936328,
  "aud": [
    "1UUl6cwNigmj",
    "https://127.0.0.1:8000"
  ]
}


ID Token
 {
  "sub": "076ecca94fe45467b4435d8eead2188ad3771e10ff672ce1930c04aa8624181c",
  "auth_time": 1595932727,
  "acr": "oidcendpoint.user_authn.authn_context.INTERNETPROTOCOLPASSWORD",
  "nonce": "wCn0Bncr7m6sRO10P5f7SA5o",
  "iss": "https://127.0.0.1:8000",
  "iat": 1595932728,
  "exp": 1595933028,
  "aud": [
    "1UUl6cwNigmj"
  ]
}


Userinfo endpoint result:
 {
  "email": "[email protected]",
  "given_name": "Giuseppe",
  "family_name": "De Marco",
  "gender": "male",
  "birthdate": "2020-07-26",
  "updated_at": 1595931659,
  "sub": "076ecca94fe45467b4435d8eead2188ad3771e10ff672ce1930c04aa8624181c"
}

Check if Configuration is valid

Hello everybody, this is a long-term issue.
During the past year I lived on my skin many jwtconnect breakable upgrades and at this moment an user can still add some options in oidcendpoint configuration.yml, that do not belong anymore to the release he's currently using.

An example is http_params changed then in httpc_params and others as we seen from v0.13.0 to v1.0.1.
I think that a configuration schema validator could help as newcomers users as developers to get some warnings or, in best cases, exception when service starts. This would avoid to run oidcendpoint in a inconsistent way, no more errors during oauth2/oidc sessions would happens.

in django we also use a special command called diffsettings that shows up with configuration fields that would not belong to one's well known in the system core.

A configuration object with a validation method, that starts first of all.
This code would also put a base to the documentation that could be estracted directly from the configuration schema definition.
What else?

Bug in Token handler info

The info method tries to find the correct handler for the token by using a try-except and moving to the next token type (order) in case of some exceptions. However, if the access or refresh token is configured to be a JWT, the exception raised is cryptojwt.exception.BadSyntax which is not handled.

This is the point where cryptojwt.exception.BadSyntax, but I am not sure if there are other exceptions that should be added too:

except (KeyError, WrongTokenType, InvalidToken, UnknownToken):

"none" authentication method broken

Is "none" authn method still supported? Because the recently introduced client_auth_setup requires a known method or a class and None raises an exception when it's about to be imported.

Check here:

res.append(importer(item)(endpoint_context))

I can still configure an endpoint to not have client authentication by not providing a client_authn_method argument in the kwargs, however I can see the None method in the CLIENT_AUTHN_METHOD dict and also here:

if "none" in self.client_authn_method:

So I am not sure if it's still supported or not.

Suggested solution:
Handle None case in client_auth_setup.

Master/develop branches have diverged

The master and the develop branch have diverged. Master should be the state of develop that was last released. Someone should look into that and we should be careful not to push commits to master no more. We should only commit to develop through PRs and then merge to master when we want to release a new version.

PKCE issues

We have found two (corner case) issues with PKCE:

  • In configurations with PKCE enabled as not essential, and plain challenge method not supported, authentication requests without PKCE parameters fail. The reason behind this is that because the challenge method is missing, plain is set and afterwards a check for the code_challenge_method validity fails, because we do not support plain. I suppose that this check must be done only in case code_challenge is included in the request.

  • add_pkce_support method assumes both authorization and token endpoints are configured so it makes no checks before trying to add PKCE hook methods. As a result it crashes if either is missing. I propose that instead of crashing we make the required checks and in the case either is missing, we log a warning and skip the configuration.

Authorization response invalid content-type when using form_post

The authorization endpoint allows three possible response_modes: query, fragment and form_post.

According to https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html#FormPostResponseMode:

In this mode, Authorization Response parameters are encoded as HTML form values that are auto-submitted in the User Agent, and thus are transmitted via the HTTP POST method to the Client, with the result parameters being encoded in the body using the application/x-www-form-urlencoded format

BUT when an authorization request is made using response_mode = form_post the endpoint will return a response with content-type = application/x-www-form-urlencoded (it should be text/html). The content-type is set in

content_type = "application/x-www-form-urlencoded"

I don't know if this affects any other endpoints, so I'm not sure how this should be fixed.

logout_all_clients may get no session

If the user reloads during logout, logout_all_clients() may get None back from usids and the call fails. Perhaps we should fail gracefully if no sessions are found?

Token introspection endpoint responses are inconsistent

I think there is a bug regarding the token introspection endpoint responses in the case of inactive/invalid tokens.

The TokenIntrospectionResponse class is only used when the token is valid (active = True) when it should be used in all cases. The return value should always be {"response_args": _resp}.

Custom scopes require userinfo endpoint

To define custom scopes the userinfo endpoint needs to be defined, but in the case of implicit flow the userinfo isn't needed. Shouldn't custom scopes be decoupled from userinfo?

In addition, custom scopes are not taken into account when defining the supported scopes in the authorization endpoint (only the scopes defined in SCOPE2CLAIMS are used there), is this intentional? IMO custom scopes should be added to the default supported scopes.

response_info not initialized correctly sometimes

In authorization's post_authentication method, there's some error handling before creating response_info and filling the return_uri entry etc., for example here:

response_info, "access_denied", "{}".format(err.args)

The response_info is initialized in line 555 and in 566 the return_uri is filled.

As a result, when do_response is called, there's an exception raised in

resp = _response.request(kwargs["return_uri"], True)

as return_uri is considered to always be there.

We could say that the app using oidcendpoint could fill the return_uri to avoid the error, but I feel it would be better if in post_authentication the response_info was filled at the beginning rather than in the middle of the method.

Token introspection endpoint response for invalid requests

Right now, the token introspection endpoint responds with active = False in all requests even if, for example, the token parameter is missing. Should the case of invalid or malformed requests be handled in another way, e.g. with a HTTP 400?

The RFC states "Note that a properly formed and authorized query for an inactive or otherwise invalid token (or a token the protected resource is not allowed to know about) is not considered an error response by this specification.", however a request without a token parameter probably should not be considered properly formed.

Client Registration issue with Bearer Header/Body

I believe that the client registration should be able to use the BearerHeader client_authn_method for simple authentication.

However, if configured to use it, it never checks the token as there is no get_client_id_from_token method implemented in oidcendpoint/oidc/registration.py. The method is called here

auth_info["client_id"] = get_client_id_from_token(
.

As a result it returns "" and no exception is raised.

I tried implementing a get_client_id_from_token method similarly to userinfo's one, but then there's a problem with unauthenticated registration as the No token exception is raised, because it cannot find a client_id in the request (correctly) and there is a get_client_id_from_token implemented. I think the last check should be corrected.

elif not client_id and get_client_id_from_token:

Non-default claims in IDToken

The claims that will be added in the id token are chosen via the by_schema function in oidcendpoint/userinfo.py. Which by default uses the oidcmsg.oidc.IdToken class to choose whether a claim should be added or not.

I am not sure if this is intentional or not, because if extra claims/scopes are added then they will not be returned in the IDToken since they are not in the default OIDC claims, unless a custom id_token_schema is provided in the configuration.

I think that the claims shouldn't be filtered like this (perhaps the call to by_schema should be removed altogether).

I hope the problem I describe is clear, do you think this should be changed or should we simply extend the id_token_schema to include our extra claims?

Problems with Token Introspection

There are some issues regarding the token introspection endpoint:

  • Currently, do_jws method returns a reply in case of any exception, which manages to pass all checks and return active = True! I think any valid jwt with a wrong algorithm or an unknown key id will raise an exception in do_jws and return active = true. (Maybe do_jws should instead return None in that case)
  • The do_access_token fails to do any time checks.
  • Tokens are never checked for revocation (black_list).
  • self.endpoint_context.sdb[token] in do_access_token raises KeyError in the case of not-existing token. This is not handled (the if/else could be changed to a try except)
  • The rfc states that tokens should be within its given time window of validity, which means that the iss/nbf should be checked also to be before the current time.

This may be a little off-topic, but we are also interested in allowing configurable claims per client for the introspection response.

Unavailable scopes - behaviour expected

I succesfully tested allowed_scopes and I found a default behaviour in oidcendpoint and oidcRP that I'd like to discuss.
When OidcRP have in its configuration some scopes not supported by OP (as found in Provider discovery) it simply omit them and send a authn request with which found available in provider discovery.

oidcendpoint when get a authz request with unavailable scopes, it simply ignore these and release its defaults (openid profile email address phone). The resulting access token could be used by a Client/RP (or a faulty RS) to give access to a resource for which the OP doesn't release the token for (simply ignoring the authz response and the auth code, no token introspection and the worst things ...).

The same behaviour if it have some allowed_scopes defined for a client_id that ask for a scope not configured for it.

In oidc.authorization we put a filter that increase this check:

oidcendpoint/oidc/authorization.py(682)process_request()
-> _cid = request_info["client_id"]
(Pdb) ll
672  	    def process_request(self, request_info=None, **kwargs):
673  	        """ The AuthorizationRequest endpoint
674  	
675  	        :param request_info: The authorization request as a dictionary
676  	        :return: dictionary
677  	        """
678  	
679  	        if isinstance(request_info, AuthorizationErrorResponse):
680  	            return request_info
681  	
682  ->	        _cid = request_info["client_id"]
683  	        cinfo = self.endpoint_context.cdb[_cid]

we have request_info e cinfo as follow

(Pdb) request_info
<oidcmsg.oidc.AuthorizationRequest object at 0x7feeb9e2a3d0>
(Pdb) request_info.__dict__
{'_dict': {'redirect_uri': 'https://127.0.0.1:8099/authz_cb/django_oidc_op', 'scope': ['openid', 'that_scope', 'profile', 'email', 'address', 'phone'], 'response_type': ['code'], 'nonce': '1mWXHQc7WEpe4HCOimA3s6Ko', 'state': 'P1y9a0KTqm1znr4ALOu31sNN6t8yXhRg', 'code_challenge': 'hz3Nx5jHHPdgaDUN6zuI8v9e4JOwHxo34FUFsCAtp3k', 'code_challenge_method': 'S256', 'client_id': '1UUl6cwNigmj'}, 'lax': False, 'jwt': None, 'jws_header': None, 'jwe_header': None, 'verify_ssl': True}

and

(Pdb) cinfo
{'id': 1, 'client_id': '1UUl6cwNigmj', 'client_salt': 'vWRsHApW', 'registration_access_token': 'dyIinaJe3gWFwJPqL2dVUHKKGcZhJdrv', 'registration_client_uri': 'https://127.0.0.1:8000/registration_api?client_id=1UUl6cwNigmj', 'client_id_issued_at': 1595799959, 'client_secret': '78be88872d5877c4ddb209335f4eb2fc5118a481a195a454c8b2ebcb', 'client_secret_expires_at': 1598391959, 'application_type': 'web', 'token_endpoint_auth_method': 'client_secret_basic', 'jwks_uri': 'https://127.0.0.1:8099/static/jwks.json', 'contacts': ['[email protected]'], 'grant_types': ['authorization_code'], 'response_types': ['code'], 'post_logout_redirect_uris': [], 'redirect_uris': [('https://127.0.0.1:8099/authz_cb/django_oidc_op', {})], 'allowed_scopes': ['that_custom_scope', 'openid']}

@rohe I suggest to put a simple filter here, somethings like:

        # this prevents that authz would be released for unavailable scopes
        for scope in request_info['scope']:
            if scope not in client_allowed_scopes:
                _msg = '{} requested an unauthorized scope ({})'
                logger.warning(_msg.format(cinfo['client_id'],
                                           scope))
                raise UnAuthorizedClientScope()

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.