Skip to content

Commit e3b8dd1

Browse files
feat: Pause managed auth logins for human verification on unsolvable captchas
Stainless-Generated-From: aa783743888796bf459bccf27bcdc797cb3a5522
1 parent eb43d31 commit e3b8dd1

6 files changed

Lines changed: 93 additions & 6 deletions

File tree

‎src/kernel/resources/auth/connections.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def create(
7272
credential: connection_create_params.Credential | Omit = omit,
7373
health_check_interval: int | Omit = omit,
7474
health_checks: bool | Omit = omit,
75+
hitl: bool | Omit = omit,
7576
login_url: str | Omit = omit,
7677
proxy: connection_create_params.Proxy | Omit = omit,
7778
record_session: bool | Omit = omit,
@@ -150,6 +151,12 @@ def create(
150151
the automatic flow (since re-auth is only triggered by a failed scheduled health
151152
check). Defaults to true.
152153
154+
hitl: Whether a login blocked by a CAPTCHA the browser cannot solve pauses for a
155+
person instead of failing. The flow moves to AWAITING_HUMAN_INTERVENTION with a
156+
`human_intervention` choice; the person completes the challenge in
157+
`live_view_url`, then submits that choice to resume. Reauthentication never
158+
pauses. Defaults to false.
159+
153160
login_url: Optional login page URL to skip discovery
154161
155162
proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients.
@@ -183,6 +190,7 @@ def create(
183190
"credential": credential,
184191
"health_check_interval": health_check_interval,
185192
"health_checks": health_checks,
193+
"hitl": hitl,
186194
"login_url": login_url,
187195
"proxy": proxy,
188196
"record_session": record_session,
@@ -246,6 +254,7 @@ def update(
246254
credential: connection_update_params.Credential | Omit = omit,
247255
health_check_interval: int | Omit = omit,
248256
health_checks: bool | Omit = omit,
257+
hitl: bool | Omit = omit,
249258
login_url: str | Omit = omit,
250259
proxy: connection_update_params.Proxy | Omit = omit,
251260
record_session: bool | Omit = omit,
@@ -297,6 +306,9 @@ def update(
297306
on the automatic flow (since re-auth is only triggered by a failed scheduled
298307
health check).
299308
309+
hitl: Whether a login blocked by a CAPTCHA the browser cannot solve pauses for a
310+
person instead of failing
311+
300312
login_url: Login page URL. Set to empty string to clear.
301313
302314
proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients.
@@ -328,6 +340,7 @@ def update(
328340
"credential": credential,
329341
"health_check_interval": health_check_interval,
330342
"health_checks": health_checks,
343+
"hitl": hitl,
331344
"login_url": login_url,
332345
"proxy": proxy,
333346
"record_session": record_session,
@@ -736,6 +749,7 @@ async def create(
736749
credential: connection_create_params.Credential | Omit = omit,
737750
health_check_interval: int | Omit = omit,
738751
health_checks: bool | Omit = omit,
752+
hitl: bool | Omit = omit,
739753
login_url: str | Omit = omit,
740754
proxy: connection_create_params.Proxy | Omit = omit,
741755
record_session: bool | Omit = omit,
@@ -814,6 +828,12 @@ async def create(
814828
the automatic flow (since re-auth is only triggered by a failed scheduled health
815829
check). Defaults to true.
816830
831+
hitl: Whether a login blocked by a CAPTCHA the browser cannot solve pauses for a
832+
person instead of failing. The flow moves to AWAITING_HUMAN_INTERVENTION with a
833+
`human_intervention` choice; the person completes the challenge in
834+
`live_view_url`, then submits that choice to resume. Reauthentication never
835+
pauses. Defaults to false.
836+
817837
login_url: Optional login page URL to skip discovery
818838
819839
proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients.
@@ -847,6 +867,7 @@ async def create(
847867
"credential": credential,
848868
"health_check_interval": health_check_interval,
849869
"health_checks": health_checks,
870+
"hitl": hitl,
850871
"login_url": login_url,
851872
"proxy": proxy,
852873
"record_session": record_session,
@@ -910,6 +931,7 @@ async def update(
910931
credential: connection_update_params.Credential | Omit = omit,
911932
health_check_interval: int | Omit = omit,
912933
health_checks: bool | Omit = omit,
934+
hitl: bool | Omit = omit,
913935
login_url: str | Omit = omit,
914936
proxy: connection_update_params.Proxy | Omit = omit,
915937
record_session: bool | Omit = omit,
@@ -961,6 +983,9 @@ async def update(
961983
on the automatic flow (since re-auth is only triggered by a failed scheduled
962984
health check).
963985
986+
hitl: Whether a login blocked by a CAPTCHA the browser cannot solve pauses for a
987+
person instead of failing
988+
964989
login_url: Login page URL. Set to empty string to clear.
965990
966991
proxy: Deprecated. Use browser.proxy. Retained during migration for existing clients.
@@ -992,6 +1017,7 @@ async def update(
9921017
"credential": credential,
9931018
"health_check_interval": health_check_interval,
9941019
"health_checks": health_checks,
1020+
"hitl": hitl,
9951021
"login_url": login_url,
9961022
"proxy": proxy,
9971023
"record_session": record_session,

‎src/kernel/types/auth/connection_create_params.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ class ConnectionCreateParams(TypedDict, total=False):
104104
triggered by a failed scheduled health check). Defaults to true.
105105
"""
106106

107+
hitl: bool
108+
"""
109+
Whether a login blocked by a CAPTCHA the browser cannot solve pauses for a
110+
person instead of failing. The flow moves to AWAITING_HUMAN_INTERVENTION with a
111+
`human_intervention` choice; the person completes the challenge in
112+
`live_view_url`, then submits that choice to resume. Reauthentication never
113+
pauses. Defaults to false.
114+
"""
115+
107116
login_url: str
108117
"""Optional login page URL to skip discovery"""
109118

‎src/kernel/types/auth/connection_follow_response.py‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,20 @@ class ManagedAuthStateEventChoice(BaseModel):
3131
"""Human-readable choice label."""
3232

3333
type: Literal[
34-
"mfa_method", "sso_provider", "sign_in_method", "auth_method", "identifier_method", "account", "other"
34+
"mfa_method",
35+
"sso_provider",
36+
"sign_in_method",
37+
"auth_method",
38+
"identifier_method",
39+
"account",
40+
"human_intervention",
41+
"other",
3542
]
36-
"""Choice type."""
43+
"""Choice type.
44+
45+
A `human_intervention` choice confirms the person finished the challenge in the
46+
live view.
47+
"""
3748

3849
context: Optional[str] = None
3950
"""Context captured for a choice."""
@@ -181,7 +192,14 @@ class ManagedAuthStateEvent(BaseModel):
181192
flow_status: Literal["IN_PROGRESS", "SUCCESS", "FAILED", "EXPIRED", "CANCELED"]
182193
"""Current flow status."""
183194

184-
flow_step: Literal["DISCOVERING", "AWAITING_INPUT", "AWAITING_EXTERNAL_ACTION", "SUBMITTING", "COMPLETED"]
195+
flow_step: Literal[
196+
"DISCOVERING",
197+
"AWAITING_INPUT",
198+
"AWAITING_EXTERNAL_ACTION",
199+
"AWAITING_HUMAN_INTERVENTION",
200+
"SUBMITTING",
201+
"COMPLETED",
202+
]
185203
"""Current step in the flow."""
186204

187205
timestamp: datetime

‎src/kernel/types/auth/connection_update_params.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ class ConnectionUpdateParams(TypedDict, total=False):
7373
only triggered by a failed scheduled health check).
7474
"""
7575

76+
hitl: bool
77+
"""
78+
Whether a login blocked by a CAPTCHA the browser cannot solve pauses for a
79+
person instead of failing
80+
"""
81+
7682
login_url: str
7783
"""Login page URL. Set to empty string to clear."""
7884

‎src/kernel/types/auth/managed_auth.py‎

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,20 @@ class Choice(BaseModel):
133133
"""Human-readable choice label."""
134134

135135
type: Literal[
136-
"mfa_method", "sso_provider", "sign_in_method", "auth_method", "identifier_method", "account", "other"
136+
"mfa_method",
137+
"sso_provider",
138+
"sign_in_method",
139+
"auth_method",
140+
"identifier_method",
141+
"account",
142+
"human_intervention",
143+
"other",
137144
]
138-
"""Choice type."""
145+
"""Choice type.
146+
147+
A `human_intervention` choice confirms the person finished the challenge in the
148+
live view.
149+
"""
139150

140151
context: Optional[str] = None
141152
"""Context captured for a choice."""
@@ -308,6 +319,12 @@ class ManagedAuth(BaseModel):
308319
domain: str
309320
"""Target domain for authentication"""
310321

322+
hitl: bool
323+
"""
324+
Whether a login blocked by a CAPTCHA the browser cannot solve pauses at
325+
AWAITING_HUMAN_INTERVENTION so a person can complete it in the live view.
326+
"""
327+
311328
profile_name: str
312329
"""Name of the profile associated with this auth connection"""
313330

@@ -506,7 +523,14 @@ class ManagedAuth(BaseModel):
506523
"""Current flow status (null when no flow in progress)"""
507524

508525
flow_step: Optional[
509-
Literal["DISCOVERING", "AWAITING_INPUT", "AWAITING_EXTERNAL_ACTION", "SUBMITTING", "COMPLETED"]
526+
Literal[
527+
"DISCOVERING",
528+
"AWAITING_INPUT",
529+
"AWAITING_EXTERNAL_ACTION",
530+
"AWAITING_HUMAN_INTERVENTION",
531+
"SUBMITTING",
532+
"COMPLETED",
533+
]
510534
] = None
511535
"""Current step in the flow (null when no flow in progress)"""
512536

‎tests/api_resources/auth/test_connections.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ def test_method_create_with_all_params(self, client: Kernel) -> None:
113113
},
114114
health_check_interval=3600,
115115
health_checks=True,
116+
hitl=False,
116117
login_url="https://netflix.com/login",
117118
proxy={
118119
"id": "id",
@@ -281,6 +282,7 @@ def test_method_update_with_all_params(self, client: Kernel) -> None:
281282
},
282283
health_check_interval=3600,
283284
health_checks=True,
285+
hitl=False,
284286
login_url="https://netflix.com/login",
285287
proxy={
286288
"id": "id",
@@ -782,6 +784,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) ->
782784
},
783785
health_check_interval=3600,
784786
health_checks=True,
787+
hitl=False,
785788
login_url="https://netflix.com/login",
786789
proxy={
787790
"id": "id",
@@ -950,6 +953,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncKernel) ->
950953
},
951954
health_check_interval=3600,
952955
health_checks=True,
956+
hitl=False,
953957
login_url="https://netflix.com/login",
954958
proxy={
955959
"id": "id",

0 commit comments

Comments
 (0)