Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ async def password_grant(self, username: str, password: str):
)
)

async def authorization_code_grant(self, callback_port: int | None = None):
async def authorization_code_grant(self, callback_port: int | None = None, prompt: str | None = None):
config = await self.configuration()

# Use provided port, fall back to env var, then default to 0 (OS picks)
Expand Down Expand Up @@ -120,7 +120,12 @@ async def callback(request):

client = self.client(redirect_uri=redirect_uri)

uri, state = client.create_authorization_url(config["authorization_endpoint"])
# Add prompt parameter if force requested
auth_params = {}
if prompt:
auth_params["prompt"] = prompt

uri, state = client.create_authorization_url(config["authorization_endpoint"], **auth_params)

print("Please open the URL in browser: ", uri)

Expand Down
4 changes: 2 additions & 2 deletions packages/jumpstarter-cli/jumpstarter_cli/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ def _print_token_status(remaining: float) -> None:

if remaining < 0:
click.echo(click.style(f"Status: EXPIRED ({duration} ago)", fg="red", bold=True))
click.echo(click.style("Run 'jmp login' to refresh your credentials.", fg="yellow"))
click.echo(click.style("Run 'jmp login --force' to refresh your credentials.", fg="yellow"))
elif remaining < TOKEN_EXPIRY_WARNING_SECONDS:
click.echo(click.style(f"Status: EXPIRING SOON ({duration} remaining)", fg="red", bold=True))
click.echo(click.style("Run 'jmp login' to refresh your credentials.", fg="yellow"))
click.echo(click.style("Run 'jmp login --force' to refresh your credentials.", fg="yellow"))
elif remaining < 3600:
click.echo(click.style(f"Status: Valid ({duration} remaining)", fg="yellow"))
else:
Expand Down
16 changes: 12 additions & 4 deletions packages/jumpstarter-cli/jumpstarter_cli/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
@click.option("-e", "--endpoint", type=str, help="Enter the Jumpstarter service endpoint.", default=None)
@click.option("--namespace", type=str, help="Enter the Jumpstarter exporter namespace.", default=None)
@click.option("--name", type=str, help="Enter the Jumpstarter exporter name.", default=None)
@click.option(
"--force",
is_flag=True,
help="Force fresh login",
default=False,
)
@opt_oidc
# client specific
# TODO: warn if used with exporter
Expand Down Expand Up @@ -52,6 +58,7 @@ async def login( # noqa: C901
insecure_tls_config: bool,
nointeractive: bool,
allow,
force: bool,
):
"""Login into a jumpstarter instance"""

Expand Down Expand Up @@ -124,7 +131,8 @@ async def login( # noqa: C901
elif username is not None and password is not None:
tokens = await oidc.password_grant(username, password)
else:
tokens = await oidc.authorization_code_grant(callback_port=callback_port)
prompt = "login" if force else None
tokens = await oidc.authorization_code_grant(callback_port=callback_port, prompt=prompt)

config.token = tokens["access_token"]

Expand All @@ -138,10 +146,11 @@ async def login( # noqa: C901
case "exporter_config":
ExporterConfigV1Alpha1.save(config, value) # ty: ignore[invalid-argument-type]


@blocking
async def relogin_client(config: ClientConfigV1Alpha1):
"""Relogin into a jumpstarter instance"""
client_id = "jumpstarter-cli" # TODO: store this metadata in the config
client_id = "jumpstarter-cli" # TODO: store this metadata in the config
try:
issuer = decode_jwt_issuer(config.token)
except Exception as e:
Expand All @@ -151,7 +160,6 @@ async def relogin_client(config: ClientConfigV1Alpha1):
oidc = Config(issuer=issuer, client_id=client_id)
tokens = await oidc.authorization_code_grant()
config.token = tokens["access_token"]
ClientConfigV1Alpha1.save(config) # ty: ignore[invalid-argument-type]
ClientConfigV1Alpha1.save(config) # ty: ignore[invalid-argument-type]
except Exception as e:
raise ReauthenticationFailed(f"Failed to re-authenticate: {e}") from e

Loading