Skip to content

Commit be702dc

Browse files
feat(auth): add save_credentials support
1 parent 3f6e730 commit be702dc

File tree

9 files changed

+39
-65
lines changed

9 files changed

+39
-65
lines changed

.stats.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 100
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-92e99f12ad95d7c00047c3f8226cd705174c68c555b42f137f3051768fdf76ae.yml
3-
openapi_spec_hash: 886e96ba621aecde2a3920825771f260
4-
config_hash: 82f0a04081a3ab7111d3a9c68cd3ff2b
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-82fd51be8dc9b6ad425425f9eb747dd337df494a030d03d37f101e2236c85548.yml
3+
openapi_spec_hash: ab396816e2b7da9938d42ec5a41cc8e1
4+
config_hash: 27c0ea01aeb797a1767af139851c5b66

api.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,6 @@ Types:
242242

243243
```python
244244
from kernel.types.auth import (
245-
LoginRequest,
246245
LoginResponse,
247246
ManagedAuth,
248247
ManagedAuthCreateRequest,

src/kernel/resources/auth/connections.py

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def create(
6363
health_check_interval: int | Omit = omit,
6464
login_url: str | Omit = omit,
6565
proxy: connection_create_params.Proxy | Omit = omit,
66+
save_credentials: bool | Omit = omit,
6667
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
6768
# The extra values given here take precedence over values defined on the client or passed to this method.
6869
extra_headers: Headers | None = None,
@@ -116,6 +117,9 @@ def create(
116117
proxy: Proxy selection. Provide either id or name. The proxy must belong to the
117118
caller's org.
118119
120+
save_credentials: Whether to save credentials after every successful login. Defaults to true.
121+
One-time codes (TOTP, SMS, etc.) are not saved.
122+
119123
extra_headers: Send extra headers
120124
121125
extra_query: Add additional query parameters to the request
@@ -135,6 +139,7 @@ def create(
135139
"health_check_interval": health_check_interval,
136140
"login_url": login_url,
137141
"proxy": proxy,
142+
"save_credentials": save_credentials,
138143
},
139144
connection_create_params.ConnectionCreateParams,
140145
),
@@ -318,7 +323,6 @@ def login(
318323
id: str,
319324
*,
320325
proxy: connection_login_params.Proxy | Omit = omit,
321-
save_credential_as: str | Omit = omit,
322326
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
323327
# The extra values given here take precedence over values defined on the client or passed to this method.
324328
extra_headers: Headers | None = None,
@@ -336,8 +340,6 @@ def login(
336340
proxy: Proxy selection. Provide either id or name. The proxy must belong to the
337341
caller's org.
338342
339-
save_credential_as: If provided, saves credentials under this name upon successful login
340-
341343
extra_headers: Send extra headers
342344
343345
extra_query: Add additional query parameters to the request
@@ -350,13 +352,7 @@ def login(
350352
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
351353
return self._post(
352354
f"/auth/connections/{id}/login",
353-
body=maybe_transform(
354-
{
355-
"proxy": proxy,
356-
"save_credential_as": save_credential_as,
357-
},
358-
connection_login_params.ConnectionLoginParams,
359-
),
355+
body=maybe_transform({"proxy": proxy}, connection_login_params.ConnectionLoginParams),
360356
options=make_request_options(
361357
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
362358
),
@@ -367,7 +363,7 @@ def submit(
367363
self,
368364
id: str,
369365
*,
370-
fields: Dict[str, str],
366+
fields: Dict[str, str] | Omit = omit,
371367
mfa_option_id: str | Omit = omit,
372368
sso_button_selector: str | Omit = omit,
373369
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -446,6 +442,7 @@ async def create(
446442
health_check_interval: int | Omit = omit,
447443
login_url: str | Omit = omit,
448444
proxy: connection_create_params.Proxy | Omit = omit,
445+
save_credentials: bool | Omit = omit,
449446
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
450447
# The extra values given here take precedence over values defined on the client or passed to this method.
451448
extra_headers: Headers | None = None,
@@ -499,6 +496,9 @@ async def create(
499496
proxy: Proxy selection. Provide either id or name. The proxy must belong to the
500497
caller's org.
501498
499+
save_credentials: Whether to save credentials after every successful login. Defaults to true.
500+
One-time codes (TOTP, SMS, etc.) are not saved.
501+
502502
extra_headers: Send extra headers
503503
504504
extra_query: Add additional query parameters to the request
@@ -518,6 +518,7 @@ async def create(
518518
"health_check_interval": health_check_interval,
519519
"login_url": login_url,
520520
"proxy": proxy,
521+
"save_credentials": save_credentials,
521522
},
522523
connection_create_params.ConnectionCreateParams,
523524
),
@@ -701,7 +702,6 @@ async def login(
701702
id: str,
702703
*,
703704
proxy: connection_login_params.Proxy | Omit = omit,
704-
save_credential_as: str | Omit = omit,
705705
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
706706
# The extra values given here take precedence over values defined on the client or passed to this method.
707707
extra_headers: Headers | None = None,
@@ -719,8 +719,6 @@ async def login(
719719
proxy: Proxy selection. Provide either id or name. The proxy must belong to the
720720
caller's org.
721721
722-
save_credential_as: If provided, saves credentials under this name upon successful login
723-
724722
extra_headers: Send extra headers
725723
726724
extra_query: Add additional query parameters to the request
@@ -733,13 +731,7 @@ async def login(
733731
raise ValueError(f"Expected a non-empty value for `id` but received {id!r}")
734732
return await self._post(
735733
f"/auth/connections/{id}/login",
736-
body=await async_maybe_transform(
737-
{
738-
"proxy": proxy,
739-
"save_credential_as": save_credential_as,
740-
},
741-
connection_login_params.ConnectionLoginParams,
742-
),
734+
body=await async_maybe_transform({"proxy": proxy}, connection_login_params.ConnectionLoginParams),
743735
options=make_request_options(
744736
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
745737
),
@@ -750,7 +742,7 @@ async def submit(
750742
self,
751743
id: str,
752744
*,
753-
fields: Dict[str, str],
745+
fields: Dict[str, str] | Omit = omit,
754746
mfa_option_id: str | Omit = omit,
755747
sso_button_selector: str | Omit = omit,
756748
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.

src/kernel/types/auth/connection_create_params.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ class ConnectionCreateParams(TypedDict, total=False):
6363
Provide either id or name. The proxy must belong to the caller's org.
6464
"""
6565

66+
save_credentials: bool
67+
"""Whether to save credentials after every successful login.
68+
69+
Defaults to true. One-time codes (TOTP, SMS, etc.) are not saved.
70+
"""
71+
6672

6773
class Credential(TypedDict, total=False):
6874
"""Reference to credentials for the auth connection.

src/kernel/types/auth/connection_login_params.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ class ConnectionLoginParams(TypedDict, total=False):
1414
Provide either id or name. The proxy must belong to the caller's org.
1515
"""
1616

17-
save_credential_as: str
18-
"""If provided, saves credentials under this name upon successful login"""
19-
2017

2118
class Proxy(TypedDict, total=False):
2219
"""Proxy selection.

src/kernel/types/auth/connection_submit_params.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
from __future__ import annotations
44

55
from typing import Dict
6-
from typing_extensions import Required, TypedDict
6+
from typing_extensions import TypedDict
77

88
__all__ = ["ConnectionSubmitParams"]
99

1010

1111
class ConnectionSubmitParams(TypedDict, total=False):
12-
fields: Required[Dict[str, str]]
12+
fields: Dict[str, str]
1313
"""Map of field name to value"""
1414

1515
mfa_option_id: str

src/kernel/types/auth/managed_auth.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ class ManagedAuth(BaseModel):
105105
profile_name: str
106106
"""Name of the profile associated with this auth connection"""
107107

108+
save_credentials: bool
109+
"""Whether credentials are saved after every successful login.
110+
111+
One-time codes (TOTP, SMS, etc.) are not saved.
112+
"""
113+
108114
status: Literal["AUTHENTICATED", "NEEDS_AUTH"]
109115
"""Current authentication status of the managed profile"""
110116

@@ -202,6 +208,9 @@ class ManagedAuth(BaseModel):
202208
post_login_url: Optional[str] = None
203209
"""URL where the browser landed after successful login"""
204210

211+
proxy_id: Optional[str] = None
212+
"""ID of the proxy associated with this connection, if any."""
213+
205214
sso_provider: Optional[str] = None
206215
"""SSO provider being used (e.g., google, github, microsoft)"""
207216

src/kernel/types/credential.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ class Credential(BaseModel):
2929
has_totp_secret: Optional[bool] = None
3030
"""Whether this credential has a TOTP secret configured for automatic 2FA"""
3131

32+
has_values: Optional[bool] = None
33+
"""Whether this credential has stored values (email, password, etc.)"""
34+
3235
sso_provider: Optional[str] = None
3336
"""
3437
If set, indicates this credential should be used with the specified SSO provider

tests/api_resources/auth/test_connections.py

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def test_method_create_with_all_params(self, client: Kernel) -> None:
5050
"id": "id",
5151
"name": "name",
5252
},
53+
save_credentials=True,
5354
)
5455
assert_matches_type(ManagedAuth, connection, path=["response"])
5556

@@ -262,7 +263,6 @@ def test_method_login_with_all_params(self, client: Kernel) -> None:
262263
"id": "id",
263264
"name": "name",
264265
},
265-
save_credential_as="my-netflix-login",
266266
)
267267
assert_matches_type(LoginResponse, connection, path=["response"])
268268

@@ -305,10 +305,6 @@ def test_path_params_login(self, client: Kernel) -> None:
305305
def test_method_submit(self, client: Kernel) -> None:
306306
connection = client.auth.connections.submit(
307307
id="id",
308-
fields={
309-
"email": "user@example.com",
310-
"password": "secret",
311-
},
312308
)
313309
assert_matches_type(SubmitFieldsResponse, connection, path=["response"])
314310

@@ -331,10 +327,6 @@ def test_method_submit_with_all_params(self, client: Kernel) -> None:
331327
def test_raw_response_submit(self, client: Kernel) -> None:
332328
response = client.auth.connections.with_raw_response.submit(
333329
id="id",
334-
fields={
335-
"email": "user@example.com",
336-
"password": "secret",
337-
},
338330
)
339331

340332
assert response.is_closed is True
@@ -347,10 +339,6 @@ def test_raw_response_submit(self, client: Kernel) -> None:
347339
def test_streaming_response_submit(self, client: Kernel) -> None:
348340
with client.auth.connections.with_streaming_response.submit(
349341
id="id",
350-
fields={
351-
"email": "user@example.com",
352-
"password": "secret",
353-
},
354342
) as response:
355343
assert not response.is_closed
356344
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -366,10 +354,6 @@ def test_path_params_submit(self, client: Kernel) -> None:
366354
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
367355
client.auth.connections.with_raw_response.submit(
368356
id="",
369-
fields={
370-
"email": "user@example.com",
371-
"password": "secret",
372-
},
373357
)
374358

375359

@@ -406,6 +390,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) ->
406390
"id": "id",
407391
"name": "name",
408392
},
393+
save_credentials=True,
409394
)
410395
assert_matches_type(ManagedAuth, connection, path=["response"])
411396

@@ -618,7 +603,6 @@ async def test_method_login_with_all_params(self, async_client: AsyncKernel) ->
618603
"id": "id",
619604
"name": "name",
620605
},
621-
save_credential_as="my-netflix-login",
622606
)
623607
assert_matches_type(LoginResponse, connection, path=["response"])
624608

@@ -661,10 +645,6 @@ async def test_path_params_login(self, async_client: AsyncKernel) -> None:
661645
async def test_method_submit(self, async_client: AsyncKernel) -> None:
662646
connection = await async_client.auth.connections.submit(
663647
id="id",
664-
fields={
665-
"email": "user@example.com",
666-
"password": "secret",
667-
},
668648
)
669649
assert_matches_type(SubmitFieldsResponse, connection, path=["response"])
670650

@@ -687,10 +667,6 @@ async def test_method_submit_with_all_params(self, async_client: AsyncKernel) ->
687667
async def test_raw_response_submit(self, async_client: AsyncKernel) -> None:
688668
response = await async_client.auth.connections.with_raw_response.submit(
689669
id="id",
690-
fields={
691-
"email": "user@example.com",
692-
"password": "secret",
693-
},
694670
)
695671

696672
assert response.is_closed is True
@@ -703,10 +679,6 @@ async def test_raw_response_submit(self, async_client: AsyncKernel) -> None:
703679
async def test_streaming_response_submit(self, async_client: AsyncKernel) -> None:
704680
async with async_client.auth.connections.with_streaming_response.submit(
705681
id="id",
706-
fields={
707-
"email": "user@example.com",
708-
"password": "secret",
709-
},
710682
) as response:
711683
assert not response.is_closed
712684
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -722,8 +694,4 @@ async def test_path_params_submit(self, async_client: AsyncKernel) -> None:
722694
with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"):
723695
await async_client.auth.connections.with_raw_response.submit(
724696
id="",
725-
fields={
726-
"email": "user@example.com",
727-
"password": "secret",
728-
},
729697
)

0 commit comments

Comments
 (0)