Skip to content

Commit 2a094e8

Browse files
authored
chore: clean up auth session (#452)
1 parent cf8098c commit 2a094e8

File tree

15 files changed

+69
-157
lines changed

15 files changed

+69
-157
lines changed

src/codegen/cli/auth/auth_session.py

Lines changed: 0 additions & 82 deletions
This file was deleted.

src/codegen/cli/auth/decorators.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
import click
55
import rich
66

7-
from codegen.cli.auth.auth_session import CodegenAuthenticatedSession
87
from codegen.cli.auth.login import login_routine
9-
from codegen.cli.errors import AuthError, InvalidTokenError, NoTokenError
8+
from codegen.cli.auth.session import CodegenSession
9+
from codegen.cli.auth.token_manager import TokenManager, get_current_token
10+
from codegen.cli.errors import AuthError
1011
from codegen.cli.rich.pretty_print import pretty_print_error
1112

1213

@@ -15,22 +16,23 @@ def requires_auth(f: Callable) -> Callable:
1516

1617
@functools.wraps(f)
1718
def wrapper(*args, **kwargs):
18-
session = CodegenAuthenticatedSession.from_active_session()
19+
session = CodegenSession.from_active_session()
1920

2021
# Check for valid session
21-
if not session.is_valid():
22-
pretty_print_error(f"The session at path {session.repo_path} is missing or corrupt.\nPlease run 'codegen init' to re-initialize the project.")
22+
if session is None or not session.is_valid():
23+
pretty_print_error("There is currently no active session.\nPlease run 'codegen init' to initialize the project.")
2324
raise click.Abort()
2425

25-
try:
26-
if not session.is_authenticated():
27-
rich.print("[yellow]Not authenticated. Let's get you logged in first![/yellow]\n")
28-
session = login_routine()
29-
except (InvalidTokenError, NoTokenError) as e:
30-
rich.print("[yellow]Authentication token is invalid or expired. Let's get you logged in again![/yellow]\n")
31-
session = login_routine()
32-
except AuthError as e:
33-
raise click.ClickException(str(e))
26+
if (token := get_current_token()) is None:
27+
rich.print("[yellow]Not authenticated. Let's get you logged in first![/yellow]\n")
28+
login_routine()
29+
else:
30+
try:
31+
token_manager = TokenManager()
32+
token_manager.authenticate_token(token)
33+
except AuthError:
34+
rich.print("[yellow]Authentication token is invalid or expired. Let's get you logged in again![/yellow]\n")
35+
login_routine()
3436

3537
return f(*args, session=session, **kwargs)
3638

src/codegen/cli/auth/login.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,48 +4,45 @@
44
import rich_click as click
55

66
from codegen.cli.api.webapp_routes import USER_SECRETS_ROUTE
7-
from codegen.cli.auth.auth_session import CodegenAuthenticatedSession
87
from codegen.cli.auth.token_manager import TokenManager
98
from codegen.cli.env.global_env import global_env
109
from codegen.cli.errors import AuthError
1110

1211

13-
def login_routine(token: str | None = None) -> CodegenAuthenticatedSession:
12+
def login_routine(token: str | None = None) -> str:
1413
"""Guide user through login flow and return authenticated session.
1514
1615
Args:
17-
console: Optional console for output. Creates new one if not provided.
16+
token: Codegen user access token associated with github account
1817
1918
Returns:
20-
CodegenSession: Authenticated session
19+
str: The authenticated token
2120
2221
Raises:
2322
click.ClickException: If login fails
2423
2524
"""
2625
# Try environment variable first
27-
28-
_token = token or global_env.CODEGEN_USER_ACCESS_TOKEN
26+
token = token or global_env.CODEGEN_USER_ACCESS_TOKEN
2927

3028
# If no token provided, guide user through browser flow
31-
if not _token:
29+
if not token:
3230
rich.print(f"Opening {USER_SECRETS_ROUTE} to get your authentication token...")
3331
webbrowser.open_new(USER_SECRETS_ROUTE)
34-
_token = click.prompt("Please enter your authentication token from the browser", hide_input=False)
32+
token = click.prompt("Please enter your authentication token from the browser", hide_input=False)
3533

36-
if not _token:
34+
if not token:
3735
msg = "Token must be provided via CODEGEN_USER_ACCESS_TOKEN environment variable or manual input"
3836
raise click.ClickException(msg)
3937

4038
# Validate and store token
41-
token_manager = TokenManager()
42-
session = CodegenAuthenticatedSession(token=_token)
43-
4439
try:
45-
session.assert_authenticated()
46-
token_manager.save_token(_token)
40+
token_manager = TokenManager()
41+
token_manager.authenticate_token(token)
4742
rich.print(f"[green]✓ Stored token to:[/green] {token_manager.token_file}")
48-
return session
43+
rich.print("[cyan]📊 Hey![/cyan] We collect anonymous usage data to improve your experience 🔒")
44+
rich.print("To opt out, set [green]telemetry_enabled = false[/green] in [cyan]~/.config/codegen-sh/analytics.json[/cyan] ✨")
45+
return token
4946
except AuthError as e:
5047
msg = f"Error: {e!s}"
5148
raise click.ClickException(msg)

src/codegen/cli/auth/token_manager.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
import os
33
from pathlib import Path
44

5+
from codegen.cli.api.client import RestAPI
56
from codegen.cli.auth.constants import AUTH_FILE, CONFIG_DIR
7+
from codegen.cli.errors import AuthError
68

79

810
class TokenManager:
@@ -19,6 +21,17 @@ def _ensure_config_dir(self):
1921
if not os.path.exists(self.config_dir):
2022
Path(self.config_dir).mkdir(parents=True, exist_ok=True)
2123

24+
def authenticate_token(self, token: str) -> None:
25+
"""Authenticate the token with the api."""
26+
identity = RestAPI(token).identify()
27+
if not identity:
28+
msg = "No identity found for session"
29+
raise AuthError(msg)
30+
if identity.auth_context.status != "active":
31+
msg = "Current session is not active. API Token may be invalid or may have expired."
32+
raise AuthError(msg)
33+
self.save_token(token)
34+
2235
def save_token(self, token: str) -> None:
2336
"""Save api token to disk."""
2437
try:

src/codegen/cli/commands/create/main.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55

66
from codegen.cli.api.client import RestAPI
77
from codegen.cli.auth.constants import PROMPTS_DIR
8+
from codegen.cli.auth.decorators import requires_auth
89
from codegen.cli.auth.session import CodegenSession
10+
from codegen.cli.auth.token_manager import get_current_token
911
from codegen.cli.codemod.convert import convert_to_cli
1012
from codegen.cli.errors import ServerError
1113
from codegen.cli.rich.codeblocks import format_command, format_path
1214
from codegen.cli.rich.pretty_print import pretty_print_error
1315
from codegen.cli.rich.spinners import create_spinner
1416
from codegen.cli.utils.default_code import DEFAULT_CODEMOD
15-
from codegen.cli.workspace.decorators import requires_init
1617

1718

1819
def get_prompts_dir() -> Path:
@@ -65,7 +66,7 @@ def make_relative(path: Path) -> str:
6566

6667

6768
@click.command(name="create")
68-
@requires_init
69+
@requires_auth
6970
@click.argument("name", type=str)
7071
@click.argument("path", type=click.Path(path_type=Path), default=Path.cwd())
7172
@click.option("--description", "-d", default=None, help="Description of what this codemod does.")
@@ -92,7 +93,7 @@ def create_command(session: CodegenSession, name: str, path: Path, description:
9293
if description:
9394
# Use API to generate implementation
9495
with create_spinner("Generating function (using LLM, this will take ~10s)") as status:
95-
response = RestAPI(session.token).create(name=name, query=description)
96+
response = RestAPI(get_current_token()).create(name=name, query=description)
9697
code = convert_to_cli(response.code, session.config.repository.language, name)
9798
prompt_path.parent.mkdir(parents=True, exist_ok=True)
9899
prompt_path.write_text(response.context)

src/codegen/cli/commands/deploy/main.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@
66

77
from codegen.cli.api.client import RestAPI
88
from codegen.cli.auth.decorators import requires_auth
9-
from codegen.cli.auth.session import CodegenSession
9+
from codegen.cli.auth.token_manager import get_current_token
1010
from codegen.cli.rich.codeblocks import format_command
1111
from codegen.cli.rich.spinners import create_spinner
1212
from codegen.cli.utils.codemod_manager import CodemodManager
1313
from codegen.cli.utils.function_finder import DecoratedFunction
1414

1515

16-
def deploy_functions(session: CodegenSession, functions: list[DecoratedFunction], message: str | None = None) -> None:
16+
def deploy_functions(functions: list[DecoratedFunction], message: str | None = None) -> None:
1717
"""Deploy a list of functions."""
1818
if not functions:
1919
rich.print("\n[yellow]No @codegen.function decorators found.[/yellow]\n")
2020
return
2121

2222
# Deploy each function
23-
api_client = RestAPI(session.token)
23+
api_client = RestAPI(get_current_token())
2424
rich.print() # Add a blank line before deployments
2525

2626
for func in functions:
@@ -47,7 +47,7 @@ def deploy_functions(session: CodegenSession, functions: list[DecoratedFunction]
4747
@click.argument("name", required=False)
4848
@click.option("-d", "--directory", type=click.Path(exists=True, path_type=Path), help="Directory to search for functions")
4949
@click.option("-m", "--message", help="Optional message to include with the deploy")
50-
def deploy_command(session: CodegenSession, name: str | None = None, directory: Path | None = None, message: str | None = None):
50+
def deploy_command(name: str | None = None, directory: Path | None = None, message: str | None = None):
5151
"""Deploy codegen functions.
5252
5353
If NAME is provided, deploys a specific function by that name.
@@ -70,11 +70,11 @@ def deploy_command(session: CodegenSession, name: str | None = None, directory:
7070
rich.print(f" • {func.filepath}")
7171
msg = "Please specify the exact directory with --directory"
7272
raise click.ClickException(msg)
73-
deploy_functions(session, matching, message=message)
73+
deploy_functions(matching, message=message)
7474
else:
7575
# Deploy all functions in the directory
7676
functions = CodemodManager.get_decorated(search_path)
77-
deploy_functions(session, functions)
77+
deploy_functions(functions)
7878
except Exception as e:
7979
msg = f"Failed to deploy: {e!s}"
8080
raise click.ClickException(msg)

src/codegen/cli/commands/expert/main.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from codegen.cli.api.client import RestAPI
66
from codegen.cli.auth.decorators import requires_auth
7-
from codegen.cli.auth.session import CodegenSession
7+
from codegen.cli.auth.token_manager import get_current_token
88
from codegen.cli.errors import ServerError
99
from codegen.cli.workspace.decorators import requires_init
1010

@@ -13,13 +13,13 @@
1313
@click.option("--query", "-q", help="The question to ask the expert.")
1414
@requires_auth
1515
@requires_init
16-
def expert_command(session: CodegenSession, query: str):
16+
def expert_command(query: str):
1717
"""Asks a codegen expert a question."""
1818
status = Status("Asking expert...", spinner="dots", spinner_style="purple")
1919
status.start()
2020

2121
try:
22-
response = RestAPI(session.token).ask_expert(query)
22+
response = RestAPI(get_current_token()).ask_expert(query)
2323
status.stop()
2424
rich.print("[bold green]✓ Response received[/bold green]")
2525
rich.print(response.response)

src/codegen/cli/commands/init/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def init_command(path: str | None = None, token: str | None = None, language: st
2424
# Print a message if not in a git repo
2525
path = Path.cwd() if path is None else Path(path)
2626
repo_path = get_git_root_path(path)
27+
rich.print(f"Found git repository at: {repo_path}")
2728
if repo_path is None:
2829
rich.print(f"\n[bold red]Error:[/bold red] Path={path} is not in a git repository")
2930
rich.print("[white]Please run this command from within a git repository.[/white]")
Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
import sys
2-
3-
import rich
41
import rich_click as click
52

6-
from codegen.cli.auth.auth_session import CodegenAuthenticatedSession
73
from codegen.cli.auth.login import login_routine
8-
from codegen.cli.auth.token_manager import TokenManager, get_current_token
4+
from codegen.cli.auth.token_manager import get_current_token
95

106

117
@click.command(name="login")
@@ -17,19 +13,4 @@ def login_command(token: str):
1713
msg = "Already authenticated. Use 'codegen logout' to clear the token."
1814
raise click.ClickException(msg)
1915

20-
if not token:
21-
login_routine()
22-
sys.exit(1)
23-
24-
# Use provided token or go through login flow
25-
token_manager = TokenManager()
26-
session = CodegenAuthenticatedSession(token=token)
27-
try:
28-
session.assert_authenticated()
29-
token_manager.save_token(token)
30-
rich.print(f"[green]✓ Stored token to:[/green] {token_manager.token_file}")
31-
rich.print("[cyan]📊 Hey![/cyan] We collect anonymous usage data to improve your experience 🔒")
32-
rich.print("To opt out, set [green]telemetry_enabled = false[/green] in [cyan]~/.config/codegen-sh/analytics.json[/cyan] ✨")
33-
except ValueError as e:
34-
msg = f"Error: {e!s}"
35-
raise click.ClickException(msg)
16+
login_routine(token)

0 commit comments

Comments
 (0)