[go: up one dir, main page]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is this library applicable for use with openid-connect providers other than okta? #38

Open
evgenyfadeev opened this issue Nov 28, 2021 · 9 comments

Comments

@evgenyfadeev
Copy link
evgenyfadeev commented Nov 28, 2021

Just a question - as is the title. I'm looking for a generic implementation of openid-connect. Can the token verifier be configured for use with other oidc providers?

Thanks!

@serhiibuniak-okta
Copy link
Contributor

@evgenyfadeev This library designed to be used with Okta, thus some features (for example, get jwk for verifying signature) may not work with other oidc providers (the only working case - URI for jwk is being constructed in the same way).
On the other hand, it is possible to use this library and override all methods you might need. Also, there is a separate module https://github.com/okta/okta-jwt-verifier-python/blob/master/okta_jwt_verifier/jwt_utils.py which have common methods such as parse_token and should work with any jwt.

@evgenyfadeev
Copy link
Author
evgenyfadeev commented Nov 29, 2021

Thanks! Perhaps you could set the jwk_url as an additional parameter to IDTokenVerifier and AccessTokenVerifier? I could make a PR for that...

@serhiibuniak-okta
Copy link
Contributor

That doesn't align with our design. On the other hand you can inherit and override anything.

@evgenyfadeev
Copy link
Author

This worked for me - a subclass of BaseJWTVerifier that receives jwks_uri and overrides the _construct_jwks_uri method.

Later to validate the tokens I called the verify_access_token and verify_id_token the same way as in the

Btw, not sure why you're not relying on the discovery data for the jwks_uri instead of making assumptions about this url.

from okta_jwt_verifier import BaseJWTVerifier

class JWTVerifier(BaseJWTVerifier):
    """Wrapper around the `BaseJwtVerifier`
    main purpose is to allow passing the `jwks_uri`
    and not be making an assumption about the url structure.
    """

    def __init__(self,
                 issuer=None,
                 client_id=None,
                 jwks_uri=None,
                 audience=None,
                 max_retries=1,
                 request_timeout=30,
                 max_requests=10,
                 leeway=120,
                 cache_jwks=True,
                 proxy=None):

        self.jwks_uri = jwks_uri
        super().__init__(issuer=issuer,
                         client_id=client_id,
                         audience=audience,
                         max_retries=max_retries,
                         request_timeout=request_timeout,
                         max_requests=max_requests,
                         leeway=leeway,
                         cache_jwks=cache_jwks,
                         proxy=proxy)

    def _construct_jwks_uri(self):
        """Bypasses the okta assumptions"""
        return self.jwks_uri`

@serhiibuniak-okta
Copy link
Contributor
serhiibuniak-okta commented Nov 30, 2021

@evgenyfadeev I'm glad that you've found a solution. Do you mean an additional network call by "discovery data for the jwks_uri"? In this case, we don't need unnecessary network calls: this jwks uri for Okta's orgs has been designed in that way and hasn't been changed for years.
What I see from your approach - you can tweak it easily for your needs, I believe, it's fine. I can suggest one more approach: basically, in order to validate token you need to perform the following steps:

  1. Parse token
  2. Verify claims
  3. Verify signature (for this step you need jwk, so you can use anything you want/need to get it)

All of these steps are defined within okta_jwt_verifier/jwt_utils.py, so in theory you can write something like this (it's just a sample pseudocode):

from okta_jwt_verifier import JWTUtils

def verify_access_token(token, jwk):
    headers, claims, signing_input, signature = JWTUtils.parse_token(token)
    JWTUtils.verify_claims(claims, claims_to_verify=['iss', 'aud', 'exp'])
    JWTUtils.verify_signature(token, jwk)

@kadhir-p44
Copy link

@ALL can we use this library in flask based web application synchronously since most methods are asynchronous in my case i need verify signature each and every call but my public key is available in remote server.

 do i need to do following steps 
 
          1. I need to download JWK from server then  I need to pass key explicitly
               JWTUtils.verify_signature(token, jwk)

@serhiibuniak-okta
Copy link
Contributor

@kadhir-p44 There are few options:

  1. https://flask.palletsprojects.com/en/2.0.x/async-await/
  2. use asyncio.run to call async methods
  3. maybe something else is possible within Flask, but need to investigate

@kadhir-p44
Copy link
kadhir-p44 commented Feb 24, 2022

@kadhir-p44 There are few options:

  1. https://flask.palletsprojects.com/en/2.0.x/async-await/
  2. use asyncio.run to call async methods
  3. maybe something else is possible within Flask, but need to investigate

@serhiibunaik

my concern is every request i need call asyncio.run () eventually it create another thread and i need to wait for result and it cause slow down my service isn't it? i expect something like java library

meanwhile i am getting following error on python 3.8 and ubuntu 20.04

okta_jwt_verifier.exceptions.JWTValidationException: 0, message='Attempt to decode JSON with unexpected mimetype: text/plain;charset=utf-8', url=URL('https://****/oauth2/v1/keys')

@bretterer
Copy link
Collaborator

Regarding your error you are receiving. Can you run:

import aiohttp
import asyncio


async def main():
    keys_url = 'https://{YOUR_ORG}/oauth2/default/v1/keys'
    async with aiohttp.ClientSession() as session:
        async with await session.get(keys_url) as resp:
            print(await resp.json())


asyncio.run(main())

And let us know what is returned? If the above returns an error still, could you try swapping the resp.json to be await resp.json(content_type=None)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants