App開瀏覽器讓user操作卻可以接到callback是怎麼做到的?

(不是web背景)
最近突然想到有些app會打開預設瀏覽器讓user做登入或是授權之類的動作,
但卻可以在user操作完之後收到callback?
但app和瀏覽器之間應該沒有IPC啊?這是怎麼做到的?

經過一陣子的研究發現是透過local建立一個http server監聽callback url,在叫起browser的時候把callback url夾帶在url parameter內,讓server端在做完之後把user redirect到callback url來達成的。

微軟寫在身份驗證服務的說明文件: Redirect URI (reply URL) outline and restrictions

知道這個特性之後就決定用python寫個POC來練手一下,之後如果有需要用到就可以直接拿來用。

Server:

from http.server import BaseHTTPRequestHandler, HTTPServer
import urllib.parse

class SimplePageHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        parsed = urllib.parse.urlparse(self.path)
        params = urllib.parse.parse_qs(parsed.query)
        callback_url = params.get('callback', [''])[0]

        html = f"""
        <html>
        <body>
            <h1>Click to trigger callback to: {callback_url}</h1>
            <form action="{callback_url}">
                <button type="submit">Go to Callback URL</button>
            </form>
        </body>
        </html>
        """

        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        self.wfile.write(html.encode('utf-8'))

if __name__ == '__main__':
    print("====== SERVER ======")
    server_address = ('0.0.0.0', 8000)
    print(f"Starting server at port {server_address[1]}")
    print(f"Visit in browser:")
    print(f"http://<domain>:{server_address[1]}/?callback=<your_callback_url>")
    httpd = HTTPServer(server_address, SimplePageHandler)
    httpd.serve_forever()

Client:

from http.server import BaseHTTPRequestHandler, HTTPServer

class CallbackHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path.startswith("/callback"):
            print(f"Received callback at {self.path}")
            self.send_response(200)
            self.send_header('Content-type', 'text/plain')
            self.end_headers()
            self.wfile.write(b'===== CLIENT =====\n')
            self.wfile.write(b'Callback received!')
        else:
            self.send_response(404)
            self.end_headers()

if __name__ == '__main__':
    print("===== CLIENT =====")
    server_address = ('localhost', 9000)
    print(f"Starting client callback server at http://{server_address[0]}:{server_address[1]}/callback")
    httpd = HTTPServer(server_address, CallbackHandler)
    httpd.serve_forever()

嘗試把server.py丟到區網上的raspberry pi 3裡面執行,從PC打開client.py跟網頁,並把callback URL帶過去,確實可以這樣用。



留言