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

Getting info about JWT user #242

Closed
michaldev opened this issue Oct 1, 2020 · 34 comments
Closed

Getting info about JWT user #242

michaldev opened this issue Oct 1, 2020 · 34 comments
Labels
closed:stale Issue or PR has not seen activity recently question waiting for customer This issue is waiting for a response from the issue or PR author

Comments

@michaldev
Copy link

michaldev commented Oct 1, 2020

How Can I check information about authorized users? Like username, and others?

I've created/copied this code:

from auth0.v3.authentication.token_verifier import TokenVerifier, AsymmetricSignatureVerifier

domain = 'dev-my-id.eu.auth0.com'
client_id = 'https://my-host.com'

id_token = 'my_best_token'

jwks_url = 'https://{}/.well-known/jwks.json'.format(domain)
issuer = 'https://{}/'.format(domain)

sv = AsymmetricSignatureVerifier(jwks_url) 
tv = TokenVerifier(signature_verifier=sv, issuer=issuer, audience=client_id)
tv.verify(id_token)

It works nice, but in the documentation doesn't exist any example with the real use case of this. I search in the code of the library and I find Users class, but tokenInfo() method is deprecated (why?) and in userInfo JWT token doesn't work.

Users(domain=domain).tokeninfo(id_token) - 404 error.
Users(domain=domain).userinfo(id_token) - 401 error.

@lbalmaceda
Copy link
Contributor

The ID token already contains the user information in its payload. But before accessing that, you should first verify its precedence and that's where you use the TokenVerifier class which would throw an exception if something is invalid.
The next step would be using a JWT decoding library to access the payload of that ID token. You can inspect the ID token in a site like jwt.io to understand what information is available.

Alternatively, you could make a second request to the /userinfo endpoint but using the access token instead. The contents you see both inside the id token payload and the user info response should be the same.

@michaldev
Copy link
Author

But from can I get access token for "user info"?
On https://my-id.auth0.com/oauth/token endpoint I receive only one token - JWT-token.

@lbalmaceda
Copy link
Contributor

There are many variables that can change what you receive. Can you show me the code you're using to authenticate a user, please?

@lbalmaceda lbalmaceda added the waiting for customer This issue is waiting for a response from the issue or PR author label Oct 1, 2020
@michaldev
Copy link
Author

Zrzut ekranu z 2020-10-02 10-24-40

@StigKorsnes
Copy link

@michaldev
If you open your application in auth0 dashboard and go to advanced settings at the bottom, there is a tab of endpoints.
You use the provided acceess_token you already have with the user info endpoint. Note that it is a requirement that openid is included as a scope in order for the /userinfo endpoint to work.

If you just want to programatically look at stuff, you can use this sdk`s authentication.GetToken class. and authentication.AsymmetricSingatureVerifier. AsymmetricSingatureVerifier.verify_signature() returns the decoded token payload. Tokenverifier uses the results from your signature verifer (payload), but does additional checks on the claims. (payload contents) However, this one does not return anything, only throws errors.

@michaldev
Copy link
Author

michaldev commented Oct 2, 2020

Maybe auth0 isn't for me.
I have a simple use case. I would like to authorize my users in my mobile application written in a flutter. I would like to show them popup (webview) for login and full option (register, forgot password, social auth) and backend (in python/fastapi) should know who is authenticated.

For using auth0 I take a decision because (propably) I don't need to use multiple native libraries for social auth on the frontend (only webview popup) and in python backend I don't need implement mailing, authentication, and others.

Additionally, I have less in app pages/screens which user see once (like login, register, forgot, reset password). Then I will have a cleaner code and I will be able to focus on other screens.

But auth0 I think that exists for others solutions (authentication for multiple websites etc.) and in my use case isn't best solution - right?

@michaldev
Copy link
Author

michaldev commented Oct 2, 2020

If you open your application in auth0 dashboard and go to advanced settings at the bottom, there is a tab of endpoints.
You use the provided acceess_token you already have with the user info endpoint. Note that it is a requirement that openid is included as a scope in order for the /userinfo endpoint to work.

And I have to get only code (from /authorize) in the mobile app and send them to python backend, then in python use code? What is the best way? I was thinking that authorization jwt for my python backend.

Code or JWT do I have to save in my backend DB?
I was thinking that in backend I don't have to store anything, and in mobile app I have to store JWT.

@lbalmaceda
Copy link
Contributor

Let me explain it briefly, although I encourage you to watch the identity labs Auth0 published. The idea is that your mobile app first gets the access token, and using it then calls your backend/API.

From the mobile app, you should be performing a "Code + PKCE" flow (read more about this flow here). That's a call to /authorize with the "response_type=code" and a challenge. The successful response would give you back a "code" value that you have to exchange along with the "code verifier" against the /oauth/token endpoint (the "code exchange" part). The result of that second request is a pair of credentials, always including the access token.

In the case of requiring to call an API (like your case), you would also pass an "audience" value in the /authorize call, so the access token that comes back is authorized to be used on that audience (and "aud" claim will be added). You'd first have to create an "Auth0 API" in your dashboard and authorize it for the Auth0 client/application that you want to using. Then, after logging in, you would call your API using that authorized access token that you received.

In your backend side, you'd receive a request authorized with the access token. You should take that access token and perform some checks before trusting the contents.

I've found that most of the above is explained in detail in this article. Note that there are many Auth0 SDKs you could use, depending on the platform, to skip many of the manual steps explained in there. You can also find many quickstarts explaining how to authenticate and call an API in the site.

@michaldev
Copy link
Author

michaldev commented Oct 2, 2020

Authorization with PKCE is failed.

Zrzut ekranu z 2020-10-02 16-43-58

Zrzut ekranu z 2020-10-02 16-41-15

The code in the second request is code from the first request-response.

Note that there are many Auth0 SDKs you could use, depending on the platform, to skip many of the manual steps explained in there

I use the FastApi.

@lbalmaceda
Copy link
Contributor

I suggest you first follow one of the mobile app quickstart to understand how the authentication is achieved https://auth0.com/docs/quickstart/native.

Once you are able to authenticate and have your access token, then move the attention to the backend / m2m https://auth0.com/docs/quickstart/backend.

@michaldev
Copy link
Author

There is not available example for Flutter. Is available one example on any auth0 blog, but with library which doesn't support Web and desktop.

I must implement it from scratch.

What is wrong with my request on screen?

@michaldev
Copy link
Author

I can get access token with other method (without PkCe). But as in post - this verification proces very well, but I don't know how can I use this for getting info about user.

@lbalmaceda
Copy link
Contributor

@michaldev you are creating an issue in the auth0-python SDK about a Flutter mobile app that is not working for you... I cannot help you with that, but you can try with the community. What I put above is general guidance and usage for any mobile scenario.

If we forget for a minute about mobile apps and assume that you have a python backend using this SDK and receive an access token through some external request, you could use that access token to call the /userinfo endpoint and get the details associated with the user that the access token is representing. That's what I put in my first comment.

@michaldev
Copy link
Author

michaldev commented Oct 2, 2020

But I must get user informations on backend (python) side. As I wrote - if I sucessed get user token (without pkce) I can't get user info in python script, because tokenInfo method is deprecated.
userInfo method doesn't work. Code from first entry in this post works good. Verify returns true, token is correctly. Problem is when I try to get info about authenticated user.

@StigKorsnes
Copy link

StigKorsnes commented Oct 5, 2020

@michaldev I'm not a contributer to the package, nor a seasoned developer, so please don't take my comments as official guidelines. I've been struggeling a couple of weeks with the docs and playing around with the various settings in the tennant dashboard, but feel the payoff is worth the effort. The more I work with it, the more I like it, and don`t think auth0 in anyway is to complex for your use-case. I am using a fast-api backend, as you. Authentication is done outside the api, but when I send a request to one of the fastapi endpoints I include the access token in the header. In the API I do validation of the access token using this package. (in the lines of what is suggested by @lbalmaceda in previous posts here).

Regarding tokeninfo() from this package have l'm guessing you already have read the docs and found this (authentication API):

/tokeninfo
This endpoint is part of the legacy authentication pipeline and will be disabled for those who use our latest, OIDC conformant, pipeline. We encourage using the /userinfo endpoint instead. For more information on the latest authentication pipeline refer to Introducing OIDC Conformant Authentication.

I`m guessing you have the switch for OIDC-conformant on (which you probably should?), and this is the reason you are getting a 404.

Also tested Users.userinfo() and it works on my end.

@michaldev
Copy link
Author

michaldev commented Oct 6, 2020

This is weird. When I set scope to openid I get access_token and id_token but if I try logged with them (verify method) I got error:
web_1 | auth0.v3.exceptions.TokenValidationError: Authorized Party (azp) claim mismatch in the ID token; expected "https://appaddress.com", found "3he3K4UasIj4in2RKi6000000".
(I entry app id where it is required)

In Auth0 logs everything is ok :/

In payload by jwt.io I have:

{
  "iss": "https://dev-ehczh8rt.eu.auth0.com/",
  "sub": "auth0|5f74b17177ff6c0071999701",
  "aud": [
    "https://appaddress.com",
    "https://dev-myid.eu.auth0.com/userinfo"
  ],
  "iat": 1602014699,
  "exp": 1602101099,
  "azp": "3he3K4UasIj4in20000000000",
  "scope": "openid"
}

But Users(domain=domain).userinfo(auth_token) returns for me username. Why now verification is failed?

@michaldev
Copy link
Author

Maybe problem is in two audiences? Python-auth0 supports two audiences?

@michaldev
Copy link
Author

michaldev commented Oct 10, 2020

There is details: https://vimeo.com/466912574
@lbalmaceda

@StigKorsnes
Copy link

@michaldev
The codebase and readme indicates that TokenVerifier is aimed for ID Tokens only. In addition the docstring says it verifies according to an openid spec. Given this, it might be a bit optimistic to assume it would work equally well for access tokens. Mentioned this in my first post, and also raised an issue on this. Also suggested you use AsymmetricSignatureVerfier alone, although this one only checks signature (as far as I can see). You would have to write additional checks your self, to my understanding. The lack of response from auth0-team is a bit alarming...

@lbalmaceda
Copy link
Contributor

When the ID token has multiple audiences, the azp claim is added by the server and must be verified against the client id value. The ID token verifier accepts this value as the "audience" parameter. The ID token can have as many audiences as it needs, as long as the one you require is listed there and also matches the azp value. That's the error you are seeing above when you attempt to verify the ID token.

Watching your recording, you mention that you are using a "native app" client id for this python backend app and that's where I'm lost. You should be using a different client id, not reusing the same, as this is a separate application. Each must have its right application type depending on what they represent: a mobile app, a backend API, etc. It looks like you are trying to call your API from the mobile app. There's a quickstart showing exactly that, which I already recommended above, have you checked that?

Python API Quickstart -> https://auth0.com/docs/quickstart/backend/python/01-authorization

This is how it should look:

  • Your mobile app would authenticate and get an access token with an audience for your API.
  • Your API would receive a request and check that the access token is set in the authorization header, for example. Validate the signature, the audience, etc. This python SDK doesn't provide a verifier for access tokens.
  • You could then call /userinfo with that received access token and obtain the user information that the scope grants access to, OR, you could call the management API using the sub value received (the auth0 user ID) and get the complete user profile.

If your use case is different to what I understood, or need a prompt response, you should reach out instead to our support team or ask in the Auth0 community so they can help you better. Cheers

@michaldev
Copy link
Author

michaldev commented Oct 13, 2020

This is not clearly "quickstart". Without my use case. That presents machine-to-machine (any client_credentials).

I've created now separated applications in my auth0 panel:

  • WEB (machine to machine)
  • Native

But I have problem still.
auth0.v3.exceptions.TokenValidationError: Audience (aud) claim mismatch in the ID token; expected "fYAWWX04VdjZdj4LtKvnEqFaLlnD92fb" but found "3he3K4UasIj4in2RKi6NytQWZKWLrsEY"

3he3K4UasIj4in2RKi6NytQWZKWLrsEY - this is native app ID.
fYAWWX04VdjZdj4LtKvnEqFaLlnD92fb - this is machine to machine app ID.

In /authorize - I have '3he3K4UasIj4in2RKi6NytQWZKWLrsEY' - native ID.
In /oauth/token I have '3he3K4UasIj4in2RKi6NytQWZKWLrsEY' - native ID.

In python script I have 'machine to machine' ID - fYAWWX04VdjZdj4LtKvnEqFaLlnD92fb.

I've tried all combinations with application id. Always errors in token verification.

@lbalmaceda
Copy link
Contributor

Don't use the ID token verifier in your backend. The backend API should not receive an ID token from the mobile app, only an access token.

@michaldev
Copy link
Author

Verification by access_token returns error the same like id_token.

@StigKorsnes
Copy link

StigKorsnes commented Oct 14, 2020

@michaldev He is saying DON'T use TokenVerifier.verify for anything else than validating id tokens. Here is roughly what I'm doing at the moment.

from auth0.v3.authentication.token_verifier import AsymmetricSignatureVerifier
from auth0.v3.exceptions import TokenValidationError
from fastapi import HTTPException,status

signature_verifier=AsymmetricSignatureVerifier(jwks_url=jwks_url)

async def validate_token_signature(api_key:str=Depends(rann_oauth))->AccessTokenPayload:   
    try:
        decoded_token=signature_verifier.verify_signature(token=api_key)
        if decoded_token.get("exp")<time.time():
            raise TokenValidationError("Token has expired")
       #Some other checks?
        
    except TokenValidationError as e:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=str(e),
            headers={"WWW-Authenticate": f"Bearer"},
        )

    return AccessTokenPayload(**decoded_token)

@michaldev
Copy link
Author

It works, but this is secure? If I'm not compare token with auth0 server?

@StigKorsnes
Copy link

StigKorsnes commented Oct 14, 2020

@michaldev Well, this is my reasoning (again as mentioned in the first post):
A TokenVerifier instance has an AsymmetricSignatureVerifier property (sv from the example). The TokenVerifier instance will, when you invoke tv.verify, first call sv.verify_signature to get the payload. If this does not raise an error, the signature is ok. After this step, tv.Verify will then do checks on the claims according to the open id spec. Please see the code to confirm this.

Alternatively you could get some additional checs from sv.verify_signature by changing some of the options on the class (DISABLE_JWT_CHECKS), but since you can not pass on arguments to jwt.decode only a few would work. This would be a hack, and would not reccomend it. Just mentioning it to motivate you to dig a bit around in the code to see what is going on :)

It is a bit unfortunate that the sdk does not have an access_token verifier. Auth0 has an example online https://auth0.com/docs/quickstart/backend/python/01-authorization . That one uses python jose. While it has similar interface (jwt.decode) it accepts the key set as a dict, while this sdk uses pyjwt, but you can not pass on arguments like leeway,audience, issuer etc. to the jwt.decode function. In my oppinon this sdk should support it, and the docs online should use this sdk in the examples. Everyone knows security is an all elusive topic to grasp, an in my oppinon the current state just adds to the confusion.

As far as I can tell, I see two possible options when it comes to validating an access token:

  1. Use this sdk to verify the signature. Pros: You get the cached keyset if you define the instance outside of the function-call. Cons: Additional check on claims must be implemented yourself. (See the code for tv.Verify for hints on how to)
  2. Drop this sdk, and implement the example. Pros, you at least get checks on audience,issuer and expiry out of the box. Cons: You would have to store or cache the keyset if you want to avoid hitting the jwks url for every request which depends on it.

I guess there are more libraries/sdk's for validating jwt`s than mentioned here, but hopefully @lbalmaceda will confirm my two options are viable solutions.

@lbalmaceda
Copy link
Contributor

The TokenVerifier is only meant to verify ID tokens following the OIDC spec.

If your app is a backend app, and you receive a request that contains an access token, you must verify:

  • the token algorithm ("alg") and signature
  • the token expiration ("exp" claim)
  • the audience ("aud" claim)

You could use the SignatureVerifier and the JwksFetcher classes if you want. Those expose one public method each to respectively verify an algorithm name and signature or get the JWK Set. You'd have to add the logic to check the remaining conditions. We don't have plans to support this officially, but as mentioned above, there are other JWT libraries that can help you verify a token.

@stale
Copy link

stale bot commented Jan 30, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you have not received a response for our team (apologies for the delay) and this is still a blocker, please reply with additional information or just a ping. Thank you for your contribution! 🙇‍♂️

@stale stale bot added the closed:stale Issue or PR has not seen activity recently label Jan 30, 2021
@cledesma
Copy link

cledesma commented Feb 4, 2021

Hello! Any updates here? I can see that this issue is also present for the PHP token verifier, and the team fixed it here
auth0/auth0-PHP#422
auth0/auth0-PHP#428

Are we also fixing it for the Python library? I am having the same issues as @michaldev, and I am only following your documentation

@stale stale bot removed the closed:stale Issue or PR has not seen activity recently label Feb 4, 2021
@lbalmaceda
Copy link
Contributor

@cledesma we still don't have in the roadmap introducing a verifier for Access Tokens on the python SDK. What is your use case?

@stale
Copy link

stale bot commented Jun 2, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you have not received a response for our team (apologies for the delay) and this is still a blocker, please reply with additional information or just a ping. Thank you for your contribution! 🙇‍♂️

@stale stale bot added the closed:stale Issue or PR has not seen activity recently label Jun 2, 2021
@stale stale bot closed this as completed Jun 9, 2021
@authereal
Copy link

Hi I've a similar use case as auth0/auth0-PHP#422 and am wondering if a similar approach to the one that the PHP SDK went with can be used: auth0/auth0-PHP#428.
Would you accept a PR for this?

@authereal
Copy link

@lbalmaceda friendly ping :)

@lbalmaceda
Copy link
Contributor

@authereal If the ID token that you are trying to validate is not compliant with the OIDC spec, I doubt we will accept a feature request to support that. The ID token verifier implemented here follows the OIDC spec.

If you are suggesting a different feature, please open a separate issue so we can review it. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed:stale Issue or PR has not seen activity recently question waiting for customer This issue is waiting for a response from the issue or PR author
Projects
None yet
Development

No branches or pull requests

5 participants