Skip to content
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
11 changes: 11 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## Changes in version 0.2.1 (in development)

### Fixes

- Fixed `client.show_app()` forcing a redundant interactive OAuth2/PKCE login
in the app GUI even when the Python client had already authenticated
(`auth_type="login"` with a resolved token). The client config bridge in
`cuiman.app.service` now forwards an already-authenticated `"login"` config
to the app as `auth_type="token"`, so the app connects immediately using
the existing bearer token instead of discarding it and re-prompting.

## Changes in version 0.2.0

### Enchancements
Expand Down
26 changes: 23 additions & 3 deletions cuiman/src/cuiman/app/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,33 @@ def create_app_service_provider(client_config: ClientConfig) -> ServiceProvider:
}


def _effective_auth_type(client_config: ClientConfig) -> str:
"""
Resolve the auth type to forward to the app.

``"login"`` means the *Python* client performs a username/password login
to obtain a token. By the time this is called (e.g. from ``show_app()``),
that login has already happened and ``client_config.token`` holds a
resolved access token — the app doesn't need to (and can't) repeat that
login. Forwarding ``auth_type="login"`` verbatim makes the app treat it
like an interactive OAuth2/PKCE login, discarding the already-valid
token and forcing a redundant sign-in. Once a token is resolved, forward
it as ``"token"`` instead, so the app connects immediately.
"""
auth_type = client_config.auth_type or "none"
if auth_type == "login" and client_config.token:
return "token"
return auth_type


def _config_to_service_options(client_config: ClientConfig) -> dict[str, Any]:
"""
Convert a ClientConfig object to a JSON-serializable dict, which includes
only keywords applicable to the given ``auth_config.auth_type``.
"""
auth_keys = set(AuthConfig.model_fields.keys())
applicable_auth_keys = _AUTH_TYPE_TO_APPLICABLE_KEYS[
client_config.auth_type or "none"
]
effective_auth_type = _effective_auth_type(client_config)
applicable_auth_keys = _AUTH_TYPE_TO_APPLICABLE_KEYS[effective_auth_type]
auth_config_dict = {
k: v
for k, v in client_config.model_dump(
Expand All @@ -85,6 +103,8 @@ def _config_to_service_options(client_config: ClientConfig) -> dict[str, Any]:
).items()
if k not in auth_keys or k in applicable_auth_keys
}
if "auth_type" in auth_config_dict:
auth_config_dict["auth_type"] = effective_auth_type
# Additional cleanup
if "token_header" in auth_config_dict and client_config.use_bearer:
del auth_config_dict["token_header"]
Expand Down
36 changes: 26 additions & 10 deletions cuiman/tests/app/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,18 +121,13 @@ def test_create_app_service_provider():
},
{
"apiUrl": "https://process.example.test/api",
"authType": "login",
"authType": "token",
"authUrl": "https://auth.example.test/auth",
"grantType": "client_credentials",
"username": "user",
"password": "secret",
"token": "token-123",
"refreshToken": "refresh-123",
"clientId": "client-id",
"clientSecret": "client-secret",
"useBearer": True,
},
id="login-bearer-removes-token-header",
id="login-with-resolved-token-forwards-as-token-removes-token-header",
),
pytest.param(
{
Expand All @@ -145,14 +140,35 @@ def test_create_app_service_provider():
},
{
"apiUrl": "https://process.example.test/api",
"authType": "login",
"authType": "token",
"authUrl": "https://auth.example.test/auth",
"grantType": "password",
"token": "token-123",
"useBearer": False,
"tokenHeader": "X-Custom-Token",
},
id="login-custom-header-keeps-token-header",
id="login-with-resolved-token-and-custom-header-forwards-as-token",
),
pytest.param(
{
"api_url": "https://process.example.test/api",
"auth_type": "login",
"auth_url": "https://auth.example.test/auth",
"username": "user",
"password": "secret",
"client_id": "client-id",
"grant_type": "password",
},
{
"apiUrl": "https://process.example.test/api",
"authType": "login",
"authUrl": "https://auth.example.test/auth",
"grantType": "password",
"username": "user",
"password": "secret",
"clientId": "client-id",
"useBearer": True,
},
id="login-without-resolved-token-keeps-login",
),
pytest.param(
{
Expand Down
Loading