get auth code from URL response - Python

Viewed 1664

I'm using OneDrive Python SKD in order to handle authentication with OneDrive SDK. The authentication is done as:

import onedrivesdk
from onedrivesdk.helpers import GetAuthCodeServer

redirect_uri = "http://localhost:8080/"
client_secret = "your_app_secret"

client = onedrivesdk.get_default_client(client_id='your_client_id',
                                        scopes=['wl.signin',
                                                'wl.offline_access',
                                                'onedrive.readwrite'])

auth_url = client.auth_provider.get_auth_url(redirect_uri)

#this will block until we have the code
code = GetAuthCodeServer.get_auth_code(auth_url, redirect_uri)

client.auth_provider.authenticate(code, redirect_uri, client_secret)

However since I use a EC2 instance to run this authentication, and furthermore I don't want to utilize a browser just for that, the code blocks indefinitely. Here's the get_auth_code from Microsoft:

def get_auth_code(auth_url, redirect_uri):
    """Easy way to get the auth code. Wraps up all the threading
    and stuff. Does block main thread.
    Args:
        auth_url (str): URL of auth server
        redirect_uri (str): Redirect URI, as set for the app. Should be 
            something like "http://localhost:8080" for this to work.
    Returns: 
        str: A string representing the auth code, sent back by the server
    """
    HOST, PORT = urlparse(redirect_uri).netloc.split(':')
    PORT = int(PORT)
    # Set up HTTP server and thread
    code_acquired = threading.Event()
    s = GetAuthCodeServer((HOST, PORT), code_acquired, GetAuthCodeRequestHandler)    
    th = threading.Thread(target=s.serve_forever)
    th.start()
    webbrowser.open(auth_url)
    # At this point the browser will open and the code
    # will be extracted by the server
    code_acquired.wait()  # First wait for the response from the auth server
    code = s.auth_code
    s.shutdown()
    th.join()
    return code

I want to return the code. Here's a sample of auth_url:

https://login.live.com/oauth20_authorize.srf?scope=wl.offline_access+onedrive.readwrite&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&response_type=code&client_id='your_client_id'

When I enter that URL in the browser, I get the code back:

http://localhost:8080/?code=Mb0bba7d1-adbc-9c1d-f790-3709cd0b9f16

SO I want to avoid that cumbersome process to get the code back by using requests. How can I accomplish that?

1 Answers

I know this is an old question, but I was struggling with the same problem - I wanted to get the code using requests library. I managed to do it somehow, but I'm doubting that this isn't a very sustainable solution. Hopefully, after reading my solution you will understand better how the authentication works and you might find an improved solution.

I have a Python Flask app with mySQL database. Occasionally, I want to create a backup of the database and send the backup file to my OneDrive, plus I want to fire this process inside my Flask App.

First, I registered my App at Microsoft Application Registration Portal and added a new platform Web with redirect url http://localhost:8080/signin-microsoft. I gave the app read and write permissions and stored the Application Id (client_id) and Application Secret (client_secret).

Second, I added a new route to my Flask App. Note that my Flask App is running on localhost:8080.

@app.route("/signin-microsoft", methods=['GET'])
def get_code():
    return 'Yadda'

Third, I replicated the HTTP request header created by my browser to my requests.get call. That is, I opened Chrome, pasted auth_url to address bar, hit enter, inspected the request header and copied its content to my code.

r = requests.get(auth_url, 
headers = {"Host" : "login.live.com",
           "Connection" : "keep-alive",
           "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
           "Accept-Encoding" : "gzip, deflate, br",
           "Upgrade-Insecure-Requests" : "1",
           "Accept-Language" : "fi-FI,fi;q=0.9,en-US;q=0.8,en;q=0.7",
           "User-agent" : "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
           "Cookie": (SUPER LONG WONT PASTE HERE)})

Fourth, I parsed the code from url the request was redirected.

re_url = r.url
code = re_url.split('code=')[-1]

Here is the final code:

redirect_uri = 'http://localhost:8080/signin-microsoft'
client_secret = CLIENT_SECRET
client_id = CLIENT_ID
api_base_url='https://api.onedrive.com/v1.0/'
scopes=['wl.signin', 'wl.offline_access', 'onedrive.readwrite']

http_provider = onedrivesdk.HttpProvider()
auth_provider = onedrivesdk.AuthProvider(
http_provider = http_provider, client_id=client_id, scopes=scopes)

client = onedrivesdk.OneDriveClient(api_base_url, auth_provider, http_provider)
auth_url = client.auth_provider.get_auth_url(redirect_uri)
r = requests.get(auth_url, 
    headers = {"Host" : "login.live.com",
               "Connection" : "keep-alive",
               "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
               "Accept-Encoding" : "gzip, deflate, br",
               "Upgrade-Insecure-Requests" : "1",
               "Accept-Language" : "fi-FI,fi;q=0.9,en-US;q=0.8,en;q=0.7",
               "User-agent" : "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
               "Cookie": (SUPER LOOONG)})
re_url = r.url
code = re_url.split('code=')[-1]
client.auth_provider.authenticate(code, redirect_uri, client_secret)

I think that there are two main points here: You need a HTTP server that listens redirect uri (in Microsoft's example they used HTTPServer from http.server) and you need to get the headers of the request right. Without the headers, the request won't redirect correctly and you won't get the code!

Related