More actions
Created page with "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. Adapte..." |
No edit summary |
||
| Line 1: | Line 1: | ||
{{#css: | |||
code { | |||
color: red; | |||
} | |||
}} | |||
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. | 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 | Adapted from the description of the native-flow process on https://github.com/esi/esi-docs/blob/master/docs/sso/native_sso_flow.md<br> | ||
and its associated python code examples at https://github.com/esi/esi-docs/tree/master/examples/python/sso | and its associated python code examples at https://github.com/esi/esi-docs/tree/master/examples/python/sso <br> | ||
(notably, "esi_oauth_natice.py" and "shared_flow.py") | (notably, "esi_oauth_natice.py" and "shared_flow.py") | ||
=Flow= | =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: | |||
<pre> | |||
import base64 | |||
import hashlib | |||
import secrets | |||
</pre> | |||
The initial stage is to generate our code_challenge. We'll use a couple variables to watch the process along the way: | |||
<pre> | |||
secret = secrets.token_bytes(32) | |||
print(secret) | |||
</pre> | |||
python console output:<br> | |||
<code>b'\x00\x06\xc4\x18\xa4\\J\x028\xe4\x14/\r\xcd8\xba\xf2\xb6\xd7\x07v#\x00E&\xdb`\xeei\x9f\xeb\xce'</code><br> | |||
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: | |||
<pre> | |||
random = base64.urlsafe_b64encode(secret) | |||
print(random) | |||
</pre> | |||
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.<br> | |||
python console output:<br> | |||
<code>b'AAbEGKRcSgI45BQvDc04uvK21wd2IwBFJttg7mmf684='</code><br> | |||
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:<br> | |||
<pre> | |||
m = hashlib.sha256() # make a hashed variable | |||
m.update(random) # hash 'random' into it | |||
d = m.digest() # return the digest | |||
print(d) | |||
</pre> | |||
Python console output:<br> | |||
<code>b'*}\x89k \xf5\x8f\x8a\xe3P\x97\xff\xe5\xd9\x19F\xe5\xab9\xae\x9a\xa8(\xfe\xd5Q\xc0\x11\xfb\xd6\xa0\x8c'</code><br> | |||
Once again we're in semi-representable bytestring mode. So we need to base64 URL-encode THIS next. | |||
<pre> | |||
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) | |||
</pre> | |||
Python console output:<br> | |||
<code>Kn2JayD1j4rjUJf_5dkZRuWrOa6aqCj-1VHAEfvWoIw=</code><br> | |||
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.<br> | |||
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: | |||
<pre> | |||
code_challenge = decoded.replace("=", "") | |||
print(code_challenge) | |||
</pre> | |||
Python console output:<br> | |||
<code>Kn2JayD1j4rjUJf_5dkZRuWrOa6aqCj-1VHAEfvWoIw</code><br> | |||
Again, the only thing that changed is we removed the = ... and this is now our Code-Challenge string that we can send to SSO.<br> | |||
The constructed URL looks like: | |||
<code>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 </code> | |||