-
-
Notifications
You must be signed in to change notification settings - Fork 131
feat: add OAuth support for external applications #2251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
m0wer
wants to merge
8
commits into
stackernews:master
Choose a base branch
from
m0wer:login_with_stacker_news
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
I'm using this small script to test: import http.server
import socketserver
import urllib.parse
import webbrowser
import requests
import base64
import hashlib
import os
# --- Configuration ---
CLIENT_ID = "69d418adf758cd8c53920fdd3514cde2f1070d4325c9b07aa11efab7813a8e97"
CLIENT_SECRET = "a0ce7042b260705697620a79260e7ad345730c53e89157cbb243dae401a54aba"
REDIRECT_URI = "http://localhost:5000/callback"
AUTHORIZATION_URL = "http://localhost:3000/api/oauth/authorize"
TOKEN_URL = "http://localhost:3000/api/oauth/token"
SCOPES = "wallet:read profile:read" # Space-separated list of scopes
# --- PKCE Functions ---
def generate_code_verifier():
return base64.urlsafe_b64encode(os.urandom(32)).rstrip(b'=').decode('utf-8')
def generate_code_challenge(code_verifier):
s256 = hashlib.sha256(code_verifier.encode('utf-8')).digest()
return base64.urlsafe_b64encode(s256).rstrip(b'=').decode('utf-8')
# --- Global variables to store the code and verifier ---
authorization_code = None
pkce_code_verifier = generate_code_verifier()
pkce_code_challenge = generate_code_challenge(pkce_code_verifier)
class OAuthCallbackHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
global authorization_code
parsed_url = urllib.parse.urlparse(self.path)
query_params = urllib.parse.parse_qs(parsed_url.query)
if parsed_url.path == "/callback" and "code" in query_params:
authorization_code = query_params["code"][0]
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(b"<html><body><h1>Authorization successful!</h1><p>You can close this tab.</p></body></html>")
print(f"Received authorization code: {authorization_code}")
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b"Not Found")
def start_local_server():
PORT = 5000
with socketserver.TCPServer(("", PORT), OAuthCallbackHandler) as httpd:
print(f"Serving at port {PORT} to catch OAuth redirect...")
httpd.handle_request()
def main():
print("--- Starting OAuth 2.0 Authorization Code Flow with PKCE ---")
# 1. Construct Authorization URL
auth_params = {
"client_id": CLIENT_ID,
"redirect_uri": REDIRECT_URI,
"response_type": "code",
"scope": SCOPES,
"code_challenge": pkce_code_challenge,
"code_challenge_method": "S256"
}
auth_query_string = urllib.parse.urlencode(auth_params)
full_authorization_url = f"{AUTHORIZATION_URL}?{auth_query_string}"
print(f"\nOpening authorization URL in your browser. Please approve the request:")
print(full_authorization_url)
webbrowser.open(full_authorization_url)
# 2. Start local server to catch the redirect
start_local_server()
if not authorization_code:
print("Error: Did not receive an authorization code.")
return
# 3. Exchange Authorization Code for Access Token
print("\nExchanging authorization code for access token...")
token_data = {
"grant_type": "authorization_code",
"code": authorization_code,
"redirect_uri": REDIRECT_URI,
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"code_verifier": pkce_code_verifier
}
try:
response = requests.post(TOKEN_URL, data=token_data)
response.raise_for_status() # Raise an exception for HTTP errors
token_info = response.json()
print("\n--- Access Token Response ---")
print(f"Access Token: {token_info.get('access_token')}")
print(f"Token Type: {token_info.get('token_type')}")
print(f"Expires In: {token_info.get('expires_in')} seconds")
print(f"Refresh Token: {token_info.get('refresh_token')}")
print(f"Scope: {token_info.get('scope')}")
except requests.exceptions.HTTPError as e:
print(f"HTTP Error during token exchange: {e}")
print(f"Response content: {e.response.text}")
except Exception as e:
print(f"An error occurred: {e}")
return
# 4. Use the Access Token to make an API call
print("\n--- Making API Call ---")
api_url = "http://localhost:3000/api/oauth/wallet/balance"
headers = {
"Authorization": f"Bearer {token_info.get('access_token')}"
}
try:
api_response = requests.get(api_url, headers=headers)
api_response.raise_for_status()
api_data = api_response.json()
print("API call successful!")
print("Response:")
print(api_data)
except requests.exceptions.HTTPError as e:
print(f"HTTP Error during API call: {e}")
print(f"Response content: {e.response.text}")
except Exception as e:
print(f"An error occurred during API call: {e}")
if __name__ == "__main__":
main()
|
Maybe the rate limiting should be removed completely. I'm not really sure about how the production environment is deployed. If there are multiple workers of the backend, this rate limiter won't be enough since it is in memory and won't be shared across workers. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
This PR implements a complete OAuth 2.0 authorization server, enabling external applications to securely integrate with SN accounts and wallets. The implementation follows RFC 6749 standards, including the Authorization Code Grant with PKCE, providing robust security for both users and client applications.
Closes #2119
Key Features:
/settings/oauth-applications
) allows developers to register, update, and manage their OAuth applications./oauth/consent
) where users can review and approve the permissions requested by an application before granting access.read
,profile:read
) and wallet permissions (wallet:read
,wallet:send
,wallet:receive
)./api/oauth/wallet/
allow authorized applications to check balances, create invoices, and initiate payments on behalf of the user, without ever accessing their credentials directly.TODO
approved
flag onOAuthApplication
is present, but there's no UI or process for an admin to approve new applications. This needs to be built. Or should apps be approved by default? Or is it OK to run a manual DB query?Screenshots
Checklist
Are your changes backward compatible? Please answer below:
Yes, this is completely backward compatible. All new functionality is additive:
/api/oauth/
namespace./settings/oauth-applications
,/oauth/consent
) that don't affect existing functionality.On a scale of 1-10 how well and how have you QA'd this change and any features it might affect? Please answer below:
TODO
For frontend changes: Tested on mobile, light and dark mode? Please answer below:
TODO
Did you introduce any new environment variables? If so, call them out explicitly here:
No new environment variables are required. The implementation uses existing variables:
NEXTAUTH_URL
for constructing OAuth redirect URLs.