How to make Python script receive OAuth2.0 Code?

Original link: https://mabbs.github.io/2023/08/01/auth.html

The simpler, the more complex.

cause

Recently, I am writing a Python script that runs on Windows, and I need to do a function similar to third-party login. Generally speaking, things like this use OAuth2.0 for authentication. The platform I access is also using this thing without any suspense. The key point is to obtain the authorization code Code of the third-party website, and then use it in exchange for User Info. So I was thinking how to do it better?

Solution

1. Use URI Schemes

I think VSCode seems to use URI Scheme when implementing similar functions. It is to register a pseudo-protocol in the system, and then use this pseudo-protocol to call the program to obtain Code. The method is very simple. First, import it in the registry:

 Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\mayx] "URL Protocol"="D:\\mayx.exe" @="MayxProtocol" [HKEY_CLASSES_ROOT\mayx\DefaultIcon] @="D:\\mayx.exe,1" [HKEY_CLASSES_ROOT\mayx\shell] [HKEY_CLASSES_ROOT\mayx\shell\open] [HKEY_CLASSES_ROOT\mayx\shell\open\command] @="\"D:\\mayx.exe\" \"%1\""

Then write a Python script and package it with Pyinstaller:

 import sys print ( sys . argv [ 1 ])

Finally, fill in the callback address as “mayx://get”, so that after the authentication is completed, the program will be called like this: “D:\mayx.exe mayx://get?code=something”, and then use urllib to do a simple analysis It’s finished.

Doesn’t this look very simple? It looks really simple, but unfortunately the pit is relatively big. The first one is that some antivirus software like the registry above will refuse to import, because things like “shell\open\command” are too abused by viruses. Too much, unless the software has passed a digital signature or some certification, it cannot be imported at all under normal circumstances. Second, do all the third-party platforms support pseudo-protocol calling, but I tried it and the platform I use does support it ? , except that the anti-virus software is annoying, the others are fine.

2. Use http service to monitor

Many cross-platform programs, like some programs that mainly run on Linux, like to start a simple web service when getting Code. In fact, if this method is used, it may be more convenient and less prone to problems.

Implemented using Flask

At first, I thought about using Flask to implement this function. It is also simple to implement, and it only takes a few lines to write:

 from flask import Flask , request code = "" app = Flask ( __name__ ) @ app . route ( '/getcode' ) def get (): global code code = request . args . get ( "code" ) shutdown = request . environ [ "werkzeug.server.shutdown" ] shutdown () return "OK" app . run ( host = "127.0.0.1" , port = 8000 ) print ( code )

It looks really simple, and the function is perfect, but there is a problem that I always feel that using Flask to do such a simple thing is really overkill, and it takes up a lot of space to package it into a program. In addition, this method has been deprecated Yes, I feel very uncomfortable. So how can you be happy if you are not happy?

Use socket implementation

I remembered the final homework socket-bbs I wrote at the end of the previous semester. This is a simple forum implemented with sockets. Wouldn’t it be great to implement such a simple function with sockets? So I wrote one: (This code has not been tested, because this method cannot be used, so the code was deleted, and this was re-done)

 import socket import urllib.parse server = socket . socket ( socket . AF_INET , socket . SOCK_STREAM ) #打开一个网络连接server . bind (( '127.0.0.1' , 8000 )) #绑定要监听的端口server . listen ( 5 ) # 设置最大的连接数量为5 code = "" while True : sock , addr = server . accept () # 建立客户端连接data = sock . recv ( 8192 ). decode ( 'utf-8' ). split ( ' \r\n ' ) #接收TCP数据,数据以字符串的形式返还if not data [ 0 ]: sock . close () # 关闭连接continue url = urllib . parse . urlparse ( data [ 0 ]. split ()[ 1 ]) if url . path == '/getcode' : query = urllib . parse . parse_qs ( self . data ) code = query [ "code" ][ 0 ] sock . send (( "HTTP/1.0 200 OK" + ' \r\n ' ). encode ( 'utf-8' )) sock . send (( "Content-Type: text/html; charset=utf-8" + ' \r\n ' ). encode ( 'utf-8' )) sock . send ( ' \r\n ' . encode ( 'utf-8' )) sock . send ( "OK" . encode ( 'utf-8' )) #发送TCP数据sock . close () # 关闭连接break else : sock . send (( "HTTP/1.0 404 Not Found" + ' \r\n ' ). encode ( 'utf-8' )) sock . send (( "Content-Type: text/html; charset=utf-8" + ' \r\n ' ). encode ( 'utf-8' )) sock . send ( ' \r\n ' . encode ( 'utf-8' )) sock . send ( "Not Found" . encode ( 'utf-8' )) #发送TCP数据sock . close () # 关闭连接print ( code )

Does it look more complicated? In fact, most of the time it seems to work quite normally, but I don’t know why I get stuck when I visit for the first time for no reason. After changing it for a long time, my head gets bigger, so I have to give up this method…

Implemented using http.server

At this time, I suddenly thought of the http.server module that comes with python that I usually use to transfer files. Since this module comes with it, the size should not be too big, right? Then I wrote one:

 from http.server import BaseHTTPRequestHandler , ThreadingHTTPServer import urllib.parse code = "" class Resquest ( BaseHTTPRequestHandler ): timeout = 5 def do_GET ( self ): url = urllib . parse . urlparse ( self . path ) if url . path == "/getuser" : self . send_response ( 200 ) self . send_header ( "Content-type" , "text/html" ) # 设置服务器响应头code = urllib . parse . parse_qs ( url . query )[ "code" ][ 0 ] buf = '''OK''' self . wfile . write ( buf . encode ()) self . server . shutdown () else : self . send_response ( 404 ) self . send_header ( "Content-type" , "text/html" ) # 设置服务器响应头self . end_headers () buf = '''Not Found''' self . wfile . write ( buf . encode ()) host = ( "127.0.0.1" , 8000 ) server = ThreadingHTTPServer ( host , Resquest ) print ( "Starting server, listen at: %s:%s" % host ) server . serve_forever () server . socket . close () print ( code )

Finally, I packaged it and tried it, and the function is normal. Although it is still a bit bigger, it is smaller than Flask. ? . In fact, when I wrote this code, I didn’t know to add server.socket.close() at the end. When I wrote it at the beginning, I always encountered that the port would not be released before the program was executed, which was very annoying. There is no good result in direct search, but it will be solved immediately after asking ChatGPT ? ,it says:

When you stop the server by calling server.shutdown() , it stops accepting new connections and closes existing connections. However, due to the design of Python’s socket library, the socket object does not immediately release the port. Instead, it will be in the TIME_WAIT state for a period of time to ensure that all pending data in the network is properly delivered or discarded. This is a requirement of a network protocol called the “TCP TIME_WAIT” state.

AI is really convenient…

feelings

Solving such a small problem but not being able to find the most suitable solution for a while, is this a side effect of knowing too much? ? , but in the end, the AI ​​is still powerful, and it solved my problem at once.

This article is transferred from: https://mabbs.github.io/2023/08/01/auth.html
This site is only for collection, and the copyright belongs to the original author.