Skip to content

Commit 7005cd5

Browse files
Improve the ergonomics of the 'login' CLI commands to behave more as expected.
1 parent 5b18e05 commit 7005cd5

File tree

3 files changed

+124
-49
lines changed

3 files changed

+124
-49
lines changed

src/planet_auth_utils/commands/cli/main.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ def cmd_plauth(ctx, loglevel, auth_profile):
7171
ctx.obj["AUTH"] = PlanetAuthFactory.initialize_auth_client_context(
7272
auth_profile_opt=auth_profile,
7373
# token_file_opt=token_file,
74+
use_env=True,
75+
use_configfile=True,
7476
)
7577

7678

@@ -151,16 +153,16 @@ def cmd_plauth_reset():
151153
@cmd_plauth.command("login")
152154
@opt_open_browser()
153155
@opt_qr_code()
154-
@opt_scope()
155-
@opt_audience()
156-
@opt_organization()
157-
@opt_project()
158-
@opt_profile()
159-
@opt_client_id()
160-
@opt_client_secret()
161-
@opt_api_key()
162-
@opt_username()
163-
@opt_password()
156+
@opt_scope(envvar=None)
157+
@opt_audience(envvar=None)
158+
@opt_organization(envvar=None)
159+
@opt_project(envvar=None)
160+
@opt_profile(envvar=None)
161+
@opt_client_id(envvar=None)
162+
@opt_client_secret(envvar=None)
163+
@opt_api_key(envvar=None)
164+
@opt_username(envvar=None)
165+
@opt_password(envvar=None)
164166
@opt_sops()
165167
@opt_yes_no()
166168
@click.pass_context
@@ -207,6 +209,10 @@ def cmd_plauth_login(
207209
auth_api_key_opt=auth_api_key,
208210
# auth_username_opt=auth_username,
209211
# auth_password_opt=auth_password,
212+
use_env=False,
213+
use_configfile=False,
214+
# TODO: save extras / project / org / scope
215+
# (or, should we leave such advanced cases for the oauth specific login command?)
210216
)
211217

212218
print(f"Logging in with authentication profile {override_auth_context.profile_name()}...")

src/planet_auth_utils/commands/cli/oauth_cmd.py

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@
1616
import sys
1717

1818
from planet_auth import (
19+
Auth,
1920
AuthException,
2021
FileBackedOidcCredential,
2122
OidcAuthClient,
2223
ExpiredTokenException,
2324
ClientCredentialsAuthClientBase,
2425
)
26+
from planet_auth_utils.plauth_factory import PlanetAuthFactory
2527

2628
from .options import (
2729
opt_audience,
@@ -32,6 +34,7 @@
3234
opt_open_browser,
3335
opt_organization,
3436
opt_password,
37+
opt_profile,
3538
opt_project,
3639
opt_refresh,
3740
opt_scope,
@@ -44,14 +47,18 @@
4447
from .jwt_cmd import json_dumps_for_jwt_dict, hazmat_print_jwt
4548

4649

47-
def _check_client_type(ctx):
48-
if not isinstance(ctx.obj["AUTH"].auth_client(), OidcAuthClient):
50+
def _check_auth_client_type(plauth_context: Auth):
51+
if not isinstance(plauth_context.auth_client(), OidcAuthClient):
4952
raise click.ClickException(
5053
f'"oauth" auth commands can only be used with OAuth type auth profiles.'
51-
f' The current profile "{ctx.obj["AUTH"].profile_name()}" is of type "{ctx.obj["AUTH"].auth_client()._auth_client_config.meta()["client_type"]}".'
54+
f' The current profile "{plauth_context.profile_name()}" is of type "{plauth_context.auth_client()._auth_client_config.meta()["client_type"]}".'
5255
)
5356

5457

58+
def _check_auth_client_type_for_click_ctx(click_ctx):
59+
_check_auth_client_type(click_ctx.obj["AUTH"])
60+
61+
5562
@click.group("oauth", invoke_without_command=True)
5663
@click.pass_context
5764
def cmd_oauth(ctx):
@@ -62,27 +69,27 @@ def cmd_oauth(ctx):
6269
click.echo(ctx.get_help())
6370
sys.exit(0)
6471

65-
_check_client_type(ctx)
66-
6772

6873
@cmd_oauth.command("login")
6974
@opt_open_browser()
7075
@opt_qr_code()
71-
@opt_scope()
72-
@opt_audience()
73-
@opt_organization()
74-
@opt_project()
75-
@opt_username()
76-
@opt_password()
77-
@opt_client_id()
78-
@opt_client_secret()
76+
@opt_profile(envvar=None)
77+
@opt_scope(envvar=None)
78+
@opt_audience(envvar=None)
79+
@opt_organization(envvar=None)
80+
@opt_project(envvar=None)
81+
@opt_username(envvar=None)
82+
@opt_password(envvar=None)
83+
@opt_client_id(envvar=None)
84+
@opt_client_secret(envvar=None)
7985
@opt_sops()
8086
@opt_yes_no()
81-
@opt_extra()
87+
@opt_extra(envvar=None)
8288
@click.pass_context
83-
@recast_exceptions_to_click(AuthException, ValueError)
89+
@recast_exceptions_to_click(AuthException, ValueError, FileNotFoundError)
8490
def cmd_oauth_login(
8591
ctx,
92+
auth_profile,
8693
scope,
8794
audience,
8895
open_browser,
@@ -113,9 +120,24 @@ def cmd_oauth_login(
113120
# a particular organization at login when the user belongs to more than one.
114121
login_extra["organization"] = organization
115122

116-
current_auth_context = ctx.obj["AUTH"]
117-
print(f"Logging in with authentication profile {current_auth_context.profile_name()}...")
118-
current_auth_context.login(
123+
# Like the root login command, the expected behavior of the oauth "login" command is
124+
# to do what it is told, not fall back to defaults and user prefs in unexpected ways.
125+
# We ignore env vars and the config file.
126+
# root_cmd_auth_context = ctx.obj["AUTH"]
127+
override_auth_context = PlanetAuthFactory.initialize_auth_client_context(
128+
auth_profile_opt=auth_profile,
129+
auth_client_id_opt=auth_client_id,
130+
auth_client_secret_opt=auth_client_secret,
131+
# auth_username_opt=auth_username,
132+
# auth_password_opt=auth_password,
133+
use_env=False,
134+
use_configfile=False,
135+
# TODO: Save extras/project/organization/scopes in context / created auth client config.
136+
)
137+
_check_auth_client_type(override_auth_context)
138+
139+
print(f"Logging in with authentication profile {override_auth_context.profile_name()}...")
140+
override_auth_context.login(
119141
requested_scopes=scope,
120142
requested_audiences=audience,
121143
allow_open_browser=open_browser,
@@ -128,7 +150,7 @@ def cmd_oauth_login(
128150
extra=login_extra,
129151
)
130152
print("Login succeeded.") # Errors should throw.
131-
post_login_cmd_helper(override_auth_context=current_auth_context, use_sops=sops, prompt_pre_selection=yes)
153+
post_login_cmd_helper(override_auth_context=override_auth_context, use_sops=sops, prompt_pre_selection=yes)
132154

133155

134156
@cmd_oauth.command("refresh")
@@ -143,6 +165,7 @@ def cmd_oauth_refresh(ctx, scope):
143165
from what is currently possessed, but you will never be granted
144166
more scopes than what the user has authorized.
145167
"""
168+
_check_auth_client_type_for_click_ctx(ctx)
146169
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
147170
auth_client = ctx.obj["AUTH"].auth_client()
148171
saved_token.load()
@@ -162,6 +185,7 @@ def cmd_oauth_list_scopes(ctx):
162185
163186
This command will query the auth server for available scopes that may be requested.
164187
"""
188+
_check_auth_client_type_for_click_ctx(ctx)
165189
auth_client = ctx.obj["AUTH"].auth_client()
166190
available_scopes = auth_client.get_scopes()
167191
available_scopes.sort()
@@ -178,6 +202,7 @@ def cmd_oauth_discovery(ctx):
178202
"""
179203
Look up OAuth server discovery information.
180204
"""
205+
_check_auth_client_type_for_click_ctx(ctx)
181206
auth_client = ctx.obj["AUTH"].auth_client()
182207
discovery_json = auth_client.oidc_discovery()
183208
print_obj(discovery_json)
@@ -192,6 +217,7 @@ def cmd_oauth_validate_access_token_remote(ctx, human_readable):
192217
Validate the access token. Validation is performed by calling
193218
out to the auth provider's token introspection network service.
194219
"""
220+
_check_auth_client_type_for_click_ctx(ctx)
195221
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
196222
auth_client = ctx.obj["AUTH"].auth_client()
197223
saved_token.load()
@@ -226,6 +252,7 @@ def cmd_oauth_validate_access_token_local(ctx, audience, scope, human_readable):
226252
Access tokens are intended for consumption by resource servers,
227253
and may be opaque to the client.
228254
"""
255+
_check_auth_client_type_for_click_ctx(ctx)
229256
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
230257
auth_client = ctx.obj["AUTH"].auth_client()
231258
saved_token.load()
@@ -246,6 +273,7 @@ def cmd_oauth_validate_id_token_remote(ctx, human_readable):
246273
Validate the ID token. Validation is performed by calling
247274
out to the auth provider's token introspection network service.
248275
"""
276+
_check_auth_client_type_for_click_ctx(ctx)
249277
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
250278
auth_client = ctx.obj["AUTH"].auth_client()
251279
saved_token.load()
@@ -269,6 +297,7 @@ def cmd_oauth_validate_id_token_local(ctx, human_readable):
269297
While validation is performed locally, network access is still
270298
required to obtain the signing keys from the auth provider.
271299
"""
300+
_check_auth_client_type_for_click_ctx(ctx)
272301
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
273302
auth_client = ctx.obj["AUTH"].auth_client()
274303
saved_token.load()
@@ -287,6 +316,7 @@ def cmd_oauth_validate_refresh_token_remote(ctx, human_readable):
287316
Validate the refresh token. Validation is performed by calling
288317
out to the auth provider's token introspection network service.
289318
"""
319+
_check_auth_client_type_for_click_ctx(ctx)
290320
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
291321
auth_client = ctx.obj["AUTH"].auth_client()
292322
saved_token.load()
@@ -315,6 +345,7 @@ def cmd_oauth_revoke_access_token(ctx):
315345
access tokens are accepted as bearer tokens, or double verified against
316346
the auth services.
317347
"""
348+
_check_auth_client_type_for_click_ctx(ctx)
318349
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
319350
auth_client = ctx.obj["AUTH"].auth_client()
320351
saved_token.load()
@@ -333,6 +364,7 @@ def cmd_oauth_revoke_refresh_token(ctx):
333364
revoke the current access token, which may remain potent until its
334365
natural expiration time if not also revoked.
335366
"""
367+
_check_auth_client_type_for_click_ctx(ctx)
336368
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
337369
auth_client = ctx.obj["AUTH"].auth_client()
338370
saved_token.load()
@@ -346,6 +378,7 @@ def cmd_oauth_userinfo(ctx):
346378
"""
347379
Look up user information from the auth server using the access token.
348380
"""
381+
_check_auth_client_type_for_click_ctx(ctx)
349382
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
350383
auth_client = ctx.obj["AUTH"].auth_client()
351384
saved_token.load()
@@ -363,6 +396,7 @@ def cmd_oauth_print_access_token(ctx, refresh):
363396
"""
364397
Show the current OAuth access token. Stale tokens will be automatically refreshed.
365398
"""
399+
_check_auth_client_type_for_click_ctx(ctx)
366400
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
367401
saved_token.load()
368402

@@ -400,6 +434,7 @@ def cmd_oauth_decode_jwt_access_token(ctx, human_readable):
400434
This function will not work for authorization servers that issue
401435
access tokens in other formats.
402436
"""
437+
_check_auth_client_type_for_click_ctx(ctx)
403438
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
404439
hazmat_print_jwt(saved_token.access_token(), human_readable=human_readable)
405440

@@ -414,6 +449,7 @@ def cmd_oauth_decode_jwt_id_token(ctx, human_readable):
414449
VALIDATION IS PERFORMED. This function is intended for local
415450
debugging purposes.
416451
"""
452+
_check_auth_client_type_for_click_ctx(ctx)
417453
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
418454
hazmat_print_jwt(saved_token.id_token(), human_readable=human_readable)
419455

@@ -430,5 +466,6 @@ def cmd_oauth_decode_jwt_refresh_token(ctx, human_readable):
430466
This function will not work for authorization servers that issue
431467
refresh tokens in other formats.
432468
"""
469+
_check_auth_client_type_for_click_ctx(ctx)
433470
saved_token = FileBackedOidcCredential(None, ctx.obj["AUTH"].token_file_path())
434471
hazmat_print_jwt(saved_token.refresh_token(), human_readable=human_readable)

0 commit comments

Comments
 (0)