Skip to content

Instantly share code, notes, and snippets.

@nevrending
Last active January 25, 2023 11:54
Show Gist options
  • Select an option

  • Save nevrending/d29e2d23f501672c6e3238d308928204 to your computer and use it in GitHub Desktop.

Select an option

Save nevrending/d29e2d23f501672c6e3238d308928204 to your computer and use it in GitHub Desktop.

Revisions

  1. nevrending revised this gist Sep 1, 2020. 1 changed file with 10 additions and 2 deletions.
    12 changes: 10 additions & 2 deletions pyjwt.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,11 @@
    ## Notice

    Originally did this research during my work for Bitwyre.
    I'm now open-sourcing this and hope this helps anyone who finds it, please let me know by commenting if this did help you!

    # JWT on Python

    The `access_token` produced by Auth Server is actually a JWT token itself, but is using a different length and algorithm than your typical JWT.
    The `access_token` produced by Auth Server (a token grant OAuth2.0) is actually a JWT token itself, but is using a different length and algorithm than your typical JWT.

    It is using `RS256` rather that the common `HS256` algo.

    @@ -25,7 +30,6 @@ pip install pyjwt
    pip install cryptography
    ```


    To verify the authenticity of a given token, the `public_key` of the `private_key` that was used to generate the token, is needed. Auth Server is the one that handles this certificate and passing the `public_key` to any 1st party app shouldn't pose a threat.

    ```python
    @@ -75,3 +79,7 @@ Notice the additional argument `audience` there, value would almost always be `2
    ```bash
    jwt.exceptions.InvalidAudienceError: Invalid audience
    ```

    #### Footnote

    (I'm the original author of this document and this previously has been forked privately here https://gist.github.com/dendisuhubdy/8f548cafc584d7178cf95e90d7cd45a2)
  2. nevrending created this gist Oct 10, 2019.
    77 changes: 77 additions & 0 deletions pyjwt.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,77 @@
    # JWT on Python

    The `access_token` produced by Auth Server is actually a JWT token itself, but is using a different length and algorithm than your typical JWT.

    It is using `RS256` rather that the common `HS256` algo.

    Try pasting the following `access_token` to [jwt.io](https://jwt.io)

    ```
    eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjdlZDhmODMzY2RlZjkzMzgxODk4ZWVlYmFkYTc3Mzg0MDhlYTU3YWRkYmM5Mzg1MDI2ZTMzYTM0OTkxMDgyOGZjNGFkZDA5MGMyYmU4MjBiIn0.eyJhdWQiOiIyIiwianRpIjoiN2VkOGY4MzNjZGVmOTMzODE4OThlZWViYWRhNzczODQwOGVhNTdhZGRiYzkzODUwMjZlMzNhMzQ5OTEwODI4ZmM0YWRkMDkwYzJiZTgyMGIiLCJpYXQiOjE1NzA3Mzk4MDUsIm5iZiI6MTU3MDczOTgwNSwiZXhwIjoxNTcwNzQwNzA1LCJzdWIiOiJjNGE3NjBhOC1kYmNmLTUyNTQtYTBkOS02YTQ0NzRiZDFiNjIiLCJzY29wZXMiOltdfQ.NDpnCkSCUMtnXhPtZX8UVTkSF-QmKqU4TH0ws3Gx-PtPL857WJTKOQHG7FS_0jPIWOiAT1rb1HwqXq1y-UZUdn8tR86Rt69QMteuER-r3tMPVBHgaUCVw6RT006gEyiVQrmD1Bb65FMXB26Vy_fDxleMlkrspGItAU0FGSd59wsl_WxdYZJF_uki9GRd3hmB86OiXjA8GflCO_gIUFhwhBdzrEzazQpgPw_LIP_r0pQF6ai8POqOFINJMfzzNW6osIaHlGHM0opoJz2q7-uHpuyfvfHQQjZQVquF0LWTHaEGxYlbLaz8wVqxoT1JWqyUrGjrUxn-a5xbBzeLjVUmJ-IRsFgsvDCv8g2QtywFqboL8RLMpGy29aOo9QPd7Ne0pqs3t4AXk8XA2Bcuo-rm7O15peByY_Grhvtw4uewTIThFGGuyjkIEs95lSABI_1fRWBJIUR2n0_x_Km95F8NJ5aIuFSMzQD-7ckfTdLUs32xh6UaVJtqSP4NwIkwDMqfiYNwhx01MMnCcgsrZIF-kO8jFCuAh9Vt1JlIwIvR7_mPEb72fcyF6vwyt46NhvXx7jXZfcFjo5x3wUopVnobbnLRHCkG5CLNtTrdyiFJAZJ57BR4mqKUIT83hLKRgGDhRLm-4Sq3ab4NxC57XMA4ha-JFaN4iJ-C7NGikSdDuJ4
    ```

    That's why we can actually use this `access_token` as a JWT token!

    Check out [PyJWT](https://pyjwt.readthedocs.io/en/latest/usage.html#encoding-decoding-tokens-with-rs256-rsa)

    ```bash
    pip install pyjwt[crypto]
    ```

    Pro Tip: If you're using `zsh` and it complained `zsh: no matches found: pyjwt[crypto]`

    ```bash
    pip install pyjwt
    pip install cryptography
    ```


    To verify the authenticity of a given token, the `public_key` of the `private_key` that was used to generate the token, is needed. Auth Server is the one that handles this certificate and passing the `public_key` to any 1st party app shouldn't pose a threat.

    ```python
    import jwt

    token = b'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjdlZDhmODMzY2RlZjkzMzgxODk4ZWVlYmFkYTc3Mzg0MDhlYTU3YWRkYmM5Mzg1MDI2ZTMzYTM0OTkxMDgyOGZjNGFkZDA5MGMyYmU4MjBiIn0.eyJhdWQiOiIyIiwianRpIjoiN2VkOGY4MzNjZGVmOTMzODE4OThlZWViYWRhNzczODQwOGVhNTdhZGRiYzkzODUwMjZlMzNhMzQ5OTEwODI4ZmM0YWRkMDkwYzJiZTgyMGIiLCJpYXQiOjE1NzA3Mzk4MDUsIm5iZiI6MTU3MDczOTgwNSwiZXhwIjoxNTcwNzQwNzA1LCJzdWIiOiJjNGE3NjBhOC1kYmNmLTUyNTQtYTBkOS02YTQ0NzRiZDFiNjIiLCJzY29wZXMiOltdfQ.NDpnCkSCUMtnXhPtZX8UVTkSF-QmKqU4TH0ws3Gx-PtPL857WJTKOQHG7FS_0jPIWOiAT1rb1HwqXq1y-UZUdn8tR86Rt69QMteuER-r3tMPVBHgaUCVw6RT006gEyiVQrmD1Bb65FMXB26Vy_fDxleMlkrspGItAU0FGSd59wsl_WxdYZJF_uki9GRd3hmB86OiXjA8GflCO_gIUFhwhBdzrEzazQpgPw_LIP_r0pQF6ai8POqOFINJMfzzNW6osIaHlGHM0opoJz2q7-uHpuyfvfHQQjZQVquF0LWTHaEGxYlbLaz8wVqxoT1JWqyUrGjrUxn-a5xbBzeLjVUmJ-IRsFgsvDCv8g2QtywFqboL8RLMpGy29aOo9QPd7Ne0pqs3t4AXk8XA2Bcuo-rm7O15peByY_Grhvtw4uewTIThFGGuyjkIEs95lSABI_1fRWBJIUR2n0_x_Km95F8NJ5aIuFSMzQD-7ckfTdLUs32xh6UaVJtqSP4NwIkwDMqfiYNwhx01MMnCcgsrZIF-kO8jFCuAh9Vt1JlIwIvR7_mPEb72fcyF6vwyt46NhvXx7jXZfcFjo5x3wUopVnobbnLRHCkG5CLNtTrdyiFJAZJ57BR4mqKUIT83hLKRgGDhRLm-4Sq3ab4NxC57XMA4ha-JFaN4iJ-C7NGikSdDuJ4'
    public_key = b'-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxgKxfus3AanXHPhoXInznJfDGHZuHDjKqWe63YxsNwJm0Ez6csnpkDXy8eKDjTj/FB7YcAgwz9X3J4G9wTEE8PIKC3HarVONfSSKHkBWYv1YKe4aJlh5R0CFcQ5ORh37PkQmPKhwK6VpvE/OUX38KgblHkDo0tLIxgcZ/n8l7nZM18TELqscvtLQ9DkSu20GGsmo85Xiy+tLlsPKXhZLYHpqGNFNGwQ9Olva29YlZQkPXf+bOH325nnLiw5gGeN1iVUR08dEJj68ZSt8S9dGSwag+4jYlQlW849b/rOQgzNqjUINabES+8zJ7cwyJYnyNhC611VpnfDTsP0vgObICNqkxDeSPgg40SAG5vzVf4adRW4WBHB6TTPGfCK5D+43gv5dsa7pu185RvvJq+ayq7gf8rINbsddY8foMugfxd4OcXLVUpXw+ohYEMpjqxYgsMS0KKCmedwkJHAD2TwdfmXeTefkZxVVCExRlCMS721rnV+F3xp1S07pmDYscqTm9LSDcubzLnuyXrGutRVNfjWQZ+HGPa7tcPDc4zlL6J6EfNnhQHUDhAtRaM4fVnFadR5Yf48A2wdaMFSyEvo9d5yCkW9uWftFftA+3ta0WIdssvTN4qOLr0AYKpYM6siP+1GWrzIIhhZUShGuC9JkIKZE/76ce+yusv7fp2OOB4kCAwEAAQ==\n-----END PUBLIC KEY-----'

    jwt.decode(token, public_key, audience='2', algorithms=['RS256'])
    ```

    Success!
    ```bash
    {'aud': '2', 'jti': '7ed8f833cdef93381898eeebada7738408ea57addbc9385026e33a349910828fc4add090c2be820b', 'iat': 1570739805, 'nbf': 1570739805, 'exp': 1570740705, 'sub': 'c4a760a8-dbcf-5254-a0d9-6a4474bd1b62', 'scopes': []}
    ```

    After the token's authenticity and validity has been proven, continue processing the request as usual.

    ### Exceptions

    Validation Failed! (from e.g wrong token)
    ```bash
    jwt.exceptions.InvalidSignatureError: Signature verification failed
    ```

    Expired Token!
    ```bash
    jwt.exceptions.ExpiredSignatureError: Signature has expired
    ```

    ## Some things to pay attention to

    ### Line Break

    A line break `\n` needs to be added after `-----BEGIN PUBLIC KEY-----` AND before `-----END PUBLIC KEY-----`!

    Otherwise you'll get

    ```bash
    ValueError: Could not deserialize key data.
    ```

    ### Audience Argument

    Notice the additional argument `audience` there, value would almost always be `2` unless there are changes on the Auth Server side, and if it's not there, you'll get

    ```bash
    jwt.exceptions.InvalidAudienceError: Invalid audience
    ```