User:Rayanth/Sandbox3
This page currently hosts some python code exploration to help a user of Tweetfleet with understanding how it goes through the Native-flow OAuth Authentication to SSO.
Adapted from the description of the native-flow process on https://github.com/esi/esi-docs/blob/master/docs/sso/native_sso_flow.md
and its associated python code examples at https://github.com/esi/esi-docs/tree/master/examples/python/sso
(notably, "esi_oauth_natice.py" and "shared_flow.py")
Flow
per the docs, the flow follows this process:
- create a 32-byte random string
- base64 urlencode that string
- sha256 hash that
- base64 urlencode the hash
- send the hash to SSO as a code_challenge
then after directing the user to log in and getting callback
- take the original base64 urlencoded string (pre-hash)
- urlencode THAT
- send it to SSO as code_verifier with the token
The code
Rather than paste the code of https://github.com/esi/esi-docs/blob/master/examples/python/sso/esi_oauth_native.py here, i'll use a breakdown i ran in python console, using that code with actual values:
We start with general imports. these are part of python base library, so no need to pip install:
import base64 import hashlib import secrets
The initial stage is to generate our code_challenge. We'll use a couple variables to watch the process along the way:
secret = secrets.token_bytes(32) print(secret)
python console output:
b'\x00\x06\xc4\x18\xa4\\J\x028\xe4\x14/\r\xcd8\xba\xf2\xb6\xd7\x07v#\x00E&\xdb`\xeei\x9f\xeb\xce'
This represents a byte-string, similar to a byte array in other languages. some bytes happen to fall in the readable range, others are shown as hex value.
Next, we need to base64 urlencode this. python provides that function in one:
random = base64.urlsafe_b64encode(secret) print(random)
Take careful note of the variable 'random' -- we'll be using it later in the code_verifier. this is AFTER it has been base64_urlencoded once.
python console output:
b'AAbEGKRcSgI45BQvDc04uvK21wd2IwBFJttg7mmf684='
Again, this string is in bytes representation, it's just all urlsafe so it's all legible. the python SHA256 library wants a "bytes like object" so we leave it in this form.
now to hash it with SHA256:
m = hashlib.sha256() # make a hashed variable m.update(random) # hash 'random' into it d = m.digest() # return the digest print(d)
Python console output:
b'*}\x89k \xf5\x8f\x8a\xe3P\x97\xff\xe5\xd9\x19F\xe5\xab9\xae\x9a\xa8(\xfe\xd5Q\xc0\x11\xfb\xd6\xa0\x8c'
Once again we're in semi-representable bytestring mode. So we need to base64 URL-encode THIS next.
urlsafe = base64.urlsafe_b64encode(d) print(urlsafe) <pre> Python console output:<br> <code>b'Kn2JayD1j4rjUJf_5dkZRuWrOa6aqCj-1VHAEfvWoIw='</code><br> This is still byte-string, so let's make it a string with decode()<br> <pre> decoded = urlsafe.decode() print(decoded)
Python console output:
Kn2JayD1j4rjUJf_5dkZRuWrOa6aqCj-1VHAEfvWoIw=
Note the only difference was the variable type, it's now an actual string. But the = sign on the end could be problematic if we send it as a parameter in the URL string rather than in form fields.
One feature of Base64 encoding is that it always results in a string with a length that is a multiple of 4. the = on the end is just padding to make sure it's of a multiple of 4 in length. The server knows this, so if we send the string without the right length, it will pad the string again before decoding it. So let's just chop the = off:
code_challenge = decoded.replace("=", "") print(code_challenge)
Python console output:
Kn2JayD1j4rjUJf_5dkZRuWrOa6aqCj-1VHAEfvWoIw
Again, the only thing that changed is we removed the = ... and this is now our Code-Challenge string that we can send to SSO.
The constructed URL looks like:
https://login.eveonline.com/v2/oauth/authorize/?response_type=code&redirect_uri=https%3A%2F%2Flocalhost%2Fcallback%2F&client_id=12345abcde&state=unique-state&code_challenge=Kn2JayD1j4rjUJf_5dkZRuWrOa6aqCj-1VHAEfvWoIw&code_challenge_method=S256