def login(self, user: User) -> None: # type: ignore [override] engine = cast(Any, import_module(settings.SESSION_ENGINE)) # Create a fake request to store login details. request = HttpRequest() if self.session: request.session = self.session else: request.session = engine.SessionStore() auth.login(user, request) # Save the session values. request.session.save() # Set the cookie to represent the session. session_cookie = settings.SESSION_COOKIE_NAME self.cookies[session_cookie] = request.session.session_key cookie_data = { "max-age": None, "path": "/", "domain": settings.SESSION_COOKIE_DOMAIN, "secure": settings.SESSION_COOKIE_SECURE or None, "expires": None, } self.cookies[session_cookie].update(cookie_data)
def process_login_request(request: HttpRequest) -> Union[Success, Error]: session_oauth_state = request.POST.get("serverState", None) request_oauth_state = request.POST.get("clientState", None) if (not session_oauth_state or not request_oauth_state or session_oauth_state != request_oauth_state): return Error( error="OAuthStateMismatch", error_description="State parameters must match.", ) # handle errors if request.POST.get("error"): return Error( error=request.POST["error"], error_description=request.POST.get("error_description") or "", ) code = request.POST.get("code") if not code: return Error( error="OAuthMissingCode", error_description="Payload should have a code parameter.", ) payload = dict( client_id=settings.KODIAK_API_GITHUB_CLIENT_ID, client_secret=settings.KODIAK_API_GITHUB_CLIENT_SECRET, code=code, ) access_res = requests.post("https://github.com/login/oauth/access_token", payload, timeout=5) try: access_res.raise_for_status() except (requests.HTTPError, requests.exceptions.Timeout): return Error(error="OAuthServerError", error_description="Failed to fetch access token.") access_res_data = dict(parse_qsl(access_res.text)) access_token_error = access_res_data.get("error") if access_token_error: return Error( error=access_token_error, error_description=access_res_data.get("error_description", ""), ) access_token = access_res_data.get("access_token") if not access_token: return Error( error="OAuthMissingAccessToken", error_description="OAuth missing access token.", ) # fetch information about the user using their oauth access token. user_data_res = requests.get( "https://api.github.com/user", headers=dict(authorization=f"Bearer {access_token}"), timeout=5, ) try: user_data_res.raise_for_status() except (requests.HTTPError, requests.exceptions.Timeout): return Error( error="OAuthServerError", error_description= "Failed to fetch account information from GitHub.", ) user_data = user_data_res.json() github_login = user_data["login"] github_account_id = int(user_data["id"]) existing_user: Optional[User] = User.objects.filter( github_id=github_account_id).first() if existing_user: existing_user.github_login = github_login existing_user.github_access_token = access_token existing_user.save() user = existing_user else: user = User.objects.create( github_id=github_account_id, github_login=github_login, github_access_token=access_token, ) # TODO(chdsbd): Run this in as a background job if the user is an existing # user. try: user.sync_accounts() except SyncAccountsError: logger.warning("sync_accounts failed", exc_info=True) # ignore the errors if we were an existing user as we can use old data. if not existing_user: return Error( error="AccountSyncFailure", error_description="Failed to sync GitHub accounts for user.", ) auth.login(user, request) return Success()