Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib50/_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"MissingToolError",
"TimeoutError",
"ConnectionError",
"RejectedHonestyPromptError"
"RejectedHonestyPromptError",
"InvalidTokenError"
]


Expand Down Expand Up @@ -110,4 +111,9 @@ class InvalidSignatureError(Error):

class RejectedHonestyPromptError(Error):
"""A ``lib50.Error`` signalling the honesty prompt was rejected by the user."""
pass


class InvalidTokenError(Error):
"""A ``lib50.Error`` signalling that the GitHub token is invalid or expired."""
pass
37 changes: 36 additions & 1 deletion lib50/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import pexpect
import re
import requests
import sys
import termcolor
import termios
Expand All @@ -13,7 +14,7 @@

from . import _
from . import _api as api
from ._errors import ConnectionError, InvalidBranchError, RejectedHonestyPromptError
from ._errors import ConnectionError, InvalidBranchError, InvalidTokenError, RejectedHonestyPromptError

__all__ = ["User", "authenticate", "logout"]

Expand Down Expand Up @@ -249,6 +250,16 @@ def _authenticate_https(org, repo=None):
print(termcolor.colored(prompt, color="yellow", attrs=["bold"]))
logout()
sys.exit(1)

Comment thread
rongxin-liu marked this conversation as resolved.
# Validate that the token is actually working
try:
_validate_github_token(password)
except InvalidTokenError:
msg = _("There seems to be an issue authenticating with your GitHub token."\
" Please visit https://cs50.dev/restart to restart your codespace and try again.")
print(termcolor.colored(msg, color="yellow", attrs=["bold"]))
logout()
sys.exit(1)
Comment thread
rongxin-liu marked this conversation as resolved.

# Otherwise, get credentials from cache if possible
if username is None or password is None:
Expand Down Expand Up @@ -331,6 +342,30 @@ def _show_gh_changes_warning():
_show_gh_changes_warning.showed = True


def _validate_github_token(token):
"""Validate a GitHub token by making an authenticated request to the GitHub API."""
try:
response = requests.get(
"https://api.github.com/user",
headers={
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"X-GitHub-Api-Version": "2022-11-28"
},
timeout=10
)

if response.status_code in (401, 403):
raise InvalidTokenError()
elif not response.ok:
raise ConnectionError(f"Could not validate GitHub token. Received status code: {response.status_code}")

except requests.exceptions.Timeout:
raise ConnectionError("Connection to GitHub timed out while validating token.")
except requests.exceptions.RequestException as e:
raise ConnectionError(f"Could not connect to GitHub to validate token: {e}")
Comment thread
rongxin-liu marked this conversation as resolved.

Comment thread
rongxin-liu marked this conversation as resolved.

def _prompt_username(prompt="Username: "):
"""Prompt the user for username."""
try:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@
python_requires=">= 3.10",
packages=["lib50"],
url="https://github.com/cs50/lib50",
version="3.2.0",
version="3.2.1",
include_package_data=True
)