11import json
2+ import os
23from dataclasses import dataclass
34from functools import wraps
45from typing import ClassVar
1213from joserfc .jws import extract_compact
1314from yarl import URL
1415
16+ from jumpstarter .config .env import JMP_OIDC_CALLBACK_PORT
17+
1518
1619def 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
0 commit comments