Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit b25eae0

Browse files
committed
implemented with arg and env
1 parent b05be84 commit b25eae0

File tree

3 files changed

+31
-4
lines changed
  • packages
    • jumpstarter-cli-common/jumpstarter_cli_common
    • jumpstarter-cli/jumpstarter_cli
    • jumpstarter/jumpstarter/config

3 files changed

+31
-4
lines changed

packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import os
23
from dataclasses import dataclass
34
from functools import wraps
45
from typing import ClassVar
@@ -12,6 +13,8 @@
1213
from joserfc.jws import extract_compact
1314
from yarl import URL
1415

16+
from jumpstarter.config.env import JMP_OIDC_CALLBACK_PORT
17+
1518

1619
def opt_oidc(f):
1720
@click.option("--issuer", help="OIDC issuer")
@@ -20,6 +23,12 @@ def opt_oidc(f):
2023
@click.option("--username", help="OIDC username")
2124
@click.option("--password", help="OIDC password")
2225
@click.option("--connector-id", "connector_id", help="OIDC token exchange connector id (Dex specific)")
26+
@click.option("--callback-port",
27+
"callback_port",
28+
type=click.IntRange(0, 65535),
29+
default=None,
30+
help="Port for OIDC callback server (0=random port)",
31+
)
2332
@wraps(f)
2433
def wrapper(*args, **kwds):
2534
return f(*args, **kwds)
@@ -71,9 +80,21 @@ async def password_grant(self, username: str, password: str):
7180
)
7281
)
7382

74-
async def authorization_code_grant(self):
83+
async def authorization_code_grant(self, callback_port: int | None = None):
7584
config = await self.configuration()
7685

86+
# Use provided port, fall back to env var, then default to 0 (OS picks)
87+
if callback_port is not None:
88+
port = callback_port
89+
else:
90+
env_value = os.environ.get(JMP_OIDC_CALLBACK_PORT)
91+
if env_value is None:
92+
port = 0
93+
elif env_value.isdigit() and int(env_value) <= 65535:
94+
port = int(env_value)
95+
else:
96+
raise click.ClickException(f"Invalid {JMP_OIDC_CALLBACK_PORT} \"{env_value}\": must be a valid port")
97+
7798
tx, rx = create_memory_object_stream()
7899

79100
async def callback(request):
@@ -86,8 +107,12 @@ async def callback(request):
86107
runner = web.AppRunner(app, access_log=None)
87108
await runner.setup()
88109

89-
site = web.TCPSite(runner, "localhost", 0)
90-
await site.start()
110+
site = web.TCPSite(runner, "localhost", port)
111+
try:
112+
await site.start()
113+
except OSError as e:
114+
await runner.cleanup()
115+
raise click.ClickException(f"Failed to start callback server on port {port}: {e}") from None
91116

92117
redirect_uri = "http://localhost:%d/callback" % site._server.sockets[0].getsockname()[1]
93118

packages/jumpstarter-cli/jumpstarter_cli/login.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ async def login( # noqa: C901
4747
issuer: str,
4848
client_id: str,
4949
connector_id: str,
50+
callback_port: int | None,
5051
unsafe,
5152
insecure_tls_config: bool,
5253
nointeractive: bool,
@@ -123,7 +124,7 @@ async def login( # noqa: C901
123124
elif username is not None and password is not None:
124125
tokens = await oidc.password_grant(username, password)
125126
else:
126-
tokens = await oidc.authorization_code_grant()
127+
tokens = await oidc.authorization_code_grant(callback_port=callback_port)
127128

128129
config.token = tokens["access_token"]
129130

packages/jumpstarter/jumpstarter/config/env.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010
JMP_LEASE = "JMP_LEASE"
1111

1212
JMP_DISABLE_COMPRESSION = "JMP_DISABLE_COMPRESSION"
13+
JMP_OIDC_CALLBACK_PORT = "JMP_OIDC_CALLBACK_PORT"

0 commit comments

Comments
 (0)