Compare commits

...

3 Commits

Author SHA1 Message Date
Robert Brennan 469059eaf8 Update openhands/server/listen.py 2024-11-08 15:30:11 -05:00
openhands 9888184289 Add JWT signing to GitHub auth cookie
- Sign GitHub auth cookie with JWT using config.jwt_secret
- Add expiration time to JWT payload
- Validate JWT signature and expiration in middleware
- Maintain backward compatibility with X-GitHub-Token header
2024-11-08 20:28:37 +00:00
openhands 3356753f79 Add cookie-based GitHub authentication caching
- Add cookie in /authenticate endpoint with 1-hour expiration
- Check for cookie in attach_session middleware before calling GitHub API
- Support cookie auth in WebSocket endpoint
- Maintain backward compatibility with X-GitHub-Token header
2024-11-08 20:23:07 +00:00
+48 -10
View File
@@ -2,6 +2,7 @@ import asyncio
import os import os
import re import re
import tempfile import tempfile
import time
import uuid import uuid
import warnings import warnings
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
@@ -60,7 +61,7 @@ from openhands.events.serialization import event_to_dict
from openhands.events.stream import AsyncEventStreamWrapper from openhands.events.stream import AsyncEventStreamWrapper
from openhands.llm import bedrock from openhands.llm import bedrock
from openhands.runtime.base import Runtime from openhands.runtime.base import Runtime
from openhands.server.auth import get_sid_from_token, sign_token from openhands.server.auth import get_sid_from_token, sign_token, jwt_encode, jwt_decode
from openhands.server.middleware import LocalhostCORSMiddleware, NoCacheMiddleware from openhands.server.middleware import LocalhostCORSMiddleware, NoCacheMiddleware
from openhands.server.session import SessionManager from openhands.server.session import SessionManager
@@ -204,12 +205,34 @@ async def attach_session(request: Request, call_next):
response = await call_next(request) response = await call_next(request)
return response return response
github_token = request.headers.get('X-GitHub-Token') # First check for auth cookie
if not await authenticate_github_user(github_token): signed_token = request.cookies.get('github_auth')
return JSONResponse( github_token = None
status_code=status.HTTP_401_UNAUTHORIZED,
content={'error': 'Not authenticated'}, if signed_token:
) try:
# Verify and decode the JWT token
cookie_data = jwt_decode(signed_token, config.jwt_secret)
github_token = cookie_data.get('github_token')
except Exception:
# If token is invalid or expired, ignore it
github_token = None
# If no valid cookie, fall back to header
if not github_token:
github_token = request.headers.get('X-GitHub-Token')
# If no header token either, return error
if not github_token:
return JSONResponse(
status_code=status.HTTP_401_UNAUTHORIZED,
content={'error': 'Not authenticated'},
)
# If using header token, verify with GitHub
if not await authenticate_github_user(github_token):
return JSONResponse(
status_code=status.HTTP_401_UNAUTHORIZED,
content={'error': 'Not authenticated'},
)
if not request.headers.get('Authorization'): if not request.headers.get('Authorization'):
logger.warning('Missing Authorization header') logger.warning('Missing Authorization header')
@@ -864,10 +887,25 @@ async def authenticate(request: Request):
content={'error': 'Not authorized via GitHub waitlist'}, content={'error': 'Not authorized via GitHub waitlist'},
) )
response = JSONResponse( # Create a signed JWT token with 1-hour expiration
status_code=status.HTTP_200_OK, content={'message': 'User authenticated'} cookie_data = {
) 'github_token': token,
'exp': int(time.time()) + 3600 # 1 hour expiration
}
signed_token = jwt_encode(cookie_data, config.jwt_secret)
response = JSONResponse(
status_code=status.HTTP_200_OK, content={'message': 'User authenticated'})
# Set secure cookie with signed token
response.set_cookie(
key="github_auth",
value=signed_token,
max_age=3600, # 1 hour in seconds
httponly=True,
secure=True,
samesite="strict"
)
return response return response