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
49 changes: 24 additions & 25 deletions auth/credentials.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -311,17 +311,19 @@ const auth = await kernel.auth.connections.create({

const login = await kernel.auth.connections.login(auth.id);

// Poll until password is needed
let state = await kernel.auth.connections.retrieve(auth.id);
while (state.flow_status === 'IN_PROGRESS') {
if (state.flow_step === 'AWAITING_INPUT' && state.discovered_fields?.length) {
// Stream state changes and submit the missing password
const authEvents = await kernel.auth.connections.follow(auth.id);
for await (const event of authEvents) {
if (
event.event === 'managed_auth_state' &&
event.flow_step === 'AWAITING_INPUT' &&
event.discovered_fields?.length
) {
// Only password field will be pending (email auto-filled from credential)
await kernel.auth.connections.submit(auth.id, {
fields: { password: 'user-provided-password' }
});
}
await new Promise(r => setTimeout(r, 2000));
state = await kernel.auth.connections.retrieve(auth.id);
}
// TOTP auto-submitted from credential → SUCCESS
```
Expand All @@ -342,17 +344,19 @@ auth = await kernel.auth.connections.create(

login = await kernel.auth.connections.login(auth.id)

# Poll until password is needed
state = await kernel.auth.connections.retrieve(auth.id)
while state.flow_status == "IN_PROGRESS":
if state.flow_step == "AWAITING_INPUT" and state.discovered_fields:
# Stream state changes and submit the missing password
auth_events = await kernel.auth.connections.follow(auth.id)
async for event in auth_events:
if (
event.event == "managed_auth_state"
and event.flow_step == "AWAITING_INPUT"
and event.discovered_fields
):
# Only password field will be pending (email auto-filled from credential)
await kernel.auth.connections.submit(
auth.id,
fields={"password": "user-provided-password"},
)
await asyncio.sleep(2)
state = await kernel.auth.connections.retrieve(auth.id)
# TOTP auto-submitted from credential → SUCCESS
```

Expand Down Expand Up @@ -390,13 +394,11 @@ if err != nil {
}
_ = login

// Poll until password is needed
state, err := client.Auth.Connections.Get(ctx, auth.ID)
if err != nil {
panic(err)
}
for state.FlowStatus == kernel.ManagedAuthFlowStatusInProgress {
if state.FlowStep == kernel.ManagedAuthFlowStepAwaitingInput && len(state.DiscoveredFields) > 0 {
// Stream state changes and submit the missing password
authEvents := client.Auth.Connections.FollowStreaming(ctx, auth.ID)
for authEvents.Next() {
event := authEvents.Current()
if event.Event == "managed_auth_state" && event.FlowStep == "AWAITING_INPUT" && len(event.DiscoveredFields) > 0 {
// Only password field will be pending (email auto-filled from credential)
_, err := client.Auth.Connections.Submit(ctx, auth.ID, kernel.AuthConnectionSubmitParams{
SubmitFieldsRequest: kernel.SubmitFieldsRequestParam{
Expand All @@ -407,12 +409,9 @@ for state.FlowStatus == kernel.ManagedAuthFlowStatusInProgress {
panic(err)
}
}

time.Sleep(2 * time.Second)
state, err = client.Auth.Connections.Get(ctx, auth.ID)
if err != nil {
panic(err)
}
}
if err := authEvents.Err(); err != nil {
panic(err)
}
// TOTP auto-submitted from credential → SUCCESS
```
Expand Down
104 changes: 54 additions & 50 deletions auth/hosted-ui.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -92,57 +92,60 @@ The user will:
2. Enter their credentials
3. Complete 2FA if needed

### 4. Poll for Completion
### 4. Stream until completion

On your backend, poll until authentication completes:
On your backend, follow the connection's SSE stream until authentication completes:

<CodeGroup>
```typescript TypeScript
let state = await kernel.auth.connections.retrieve(auth.id);
const events = await kernel.auth.connections.follow(auth.id);
let finalState;

while (state.flow_status === 'IN_PROGRESS') {
await new Promise(r => setTimeout(r, 2000));
state = await kernel.auth.connections.retrieve(auth.id);
for await (const event of events) {
if (event.event === 'managed_auth_state') {
finalState = event;
}
}

if (state.status === 'AUTHENTICATED') {
if (finalState?.flow_status === 'SUCCESS') {
console.log('Authentication successful!');
}
```

```python Python
state = await kernel.auth.connections.retrieve(auth.id)
events = await kernel.auth.connections.follow(auth.id)
final_state = None

while state.flow_status == "IN_PROGRESS":
await asyncio.sleep(2)
state = await kernel.auth.connections.retrieve(auth.id)
async for event in events:
if event.event == "managed_auth_state":
final_state = event

if state.status == "AUTHENTICATED":
if final_state and final_state.flow_status == "SUCCESS":
print("Authentication successful!")
```

```go Go
state, err := client.Auth.Connections.Get(ctx, auth.ID)
if err != nil {
panic(err)
}
events := client.Auth.Connections.FollowStreaming(ctx, auth.ID)
authenticated := false

for state.FlowStatus == kernel.ManagedAuthFlowStatusInProgress {
time.Sleep(2 * time.Second)
state, err = client.Auth.Connections.Get(ctx, auth.ID)
if err != nil {
panic(err)
for events.Next() {
event := events.Current()
if event.Event == "managed_auth_state" && event.FlowStatus == "SUCCESS" {
authenticated = true
}
}
if err := events.Err(); err != nil {
panic(err)
}

if state.Status == kernel.ManagedAuthStatusAuthenticated {
if authenticated {
fmt.Println("Authentication successful!")
}
```
</CodeGroup>

<Info>
Poll every 2 seconds. The session expires after 20 minutes if not completed, and the flow times out after 10 minutes of waiting for user input.
The SSE stream closes automatically when the flow succeeds, fails, expires, or is canceled. The session expires after 20 minutes if not completed, and the flow times out after 10 minutes of waiting for user input.
</Info>

### 5. Use the Profile
Expand Down Expand Up @@ -216,14 +219,16 @@ const login = await kernel.auth.connections.login(auth.id);
// Send user to hosted page
console.log('Login URL:', login.hosted_url);

// Poll for completion
let state = await kernel.auth.connections.retrieve(auth.id);
while (state.flow_status === 'IN_PROGRESS') {
await new Promise(r => setTimeout(r, 2000));
state = await kernel.auth.connections.retrieve(auth.id);
// Stream state changes until the flow completes
const events = await kernel.auth.connections.follow(auth.id);
let finalState;
for await (const event of events) {
if (event.event === 'managed_auth_state') {
finalState = event;
}
}

if (state.status === 'AUTHENTICATED') {
if (finalState?.flow_status === 'SUCCESS') {
const browser = await kernel.browsers.create({
profile: { name: 'doordash-user-123' },
stealth: true,
Expand All @@ -235,10 +240,9 @@ if (state.status === 'AUTHENTICATED') {
```

```python Python
from kernel import Kernel
import asyncio
from kernel import AsyncKernel

kernel = Kernel()
kernel = AsyncKernel()

# Create connection
auth = await kernel.auth.connections.create(
Expand All @@ -252,13 +256,14 @@ login = await kernel.auth.connections.login(auth.id)
# Send user to hosted page
print(f"Login URL: {login.hosted_url}")

# Poll for completion
state = await kernel.auth.connections.retrieve(auth.id)
while state.flow_status == "IN_PROGRESS":
await asyncio.sleep(2)
state = await kernel.auth.connections.retrieve(auth.id)
# Stream state changes until the flow completes
events = await kernel.auth.connections.follow(auth.id)
final_state = None
async for event in events:
if event.event == "managed_auth_state":
final_state = event

if state.status == "AUTHENTICATED":
if final_state and final_state.flow_status == "SUCCESS":
browser = await kernel.browsers.create(
profile={"name": "doordash-user-123"},
stealth=True,
Expand All @@ -274,7 +279,6 @@ package main
import (
"context"
"fmt"
"time"

"github.com/kernel/kernel-go-sdk"
"github.com/kernel/kernel-go-sdk/shared"
Expand Down Expand Up @@ -304,20 +308,20 @@ func main() {
// Send user to hosted page
fmt.Println("Login URL:", login.HostedURL)

// Poll for completion
state, err := client.Auth.Connections.Get(ctx, auth.ID)
if err != nil {
panic(err)
}
for state.FlowStatus == kernel.ManagedAuthFlowStatusInProgress {
time.Sleep(2 * time.Second)
state, err = client.Auth.Connections.Get(ctx, auth.ID)
if err != nil {
panic(err)
// Stream state changes until the flow completes
events := client.Auth.Connections.FollowStreaming(ctx, auth.ID)
authenticated := false
for events.Next() {
event := events.Current()
if event.Event == "managed_auth_state" && event.FlowStatus == "SUCCESS" {
authenticated = true
}
}
if err := events.Err(); err != nil {
panic(err)
}

if state.Status == kernel.ManagedAuthStatusAuthenticated {
if authenticated {
browser, err := client.Browsers.New(ctx, kernel.BrowserNewParams{
Profile: shared.BrowserProfileParam{
Name: kernel.String("doordash-user-123"),
Expand Down
52 changes: 29 additions & 23 deletions auth/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,17 @@ const login = await kernel.auth.connections.login(auth.id);
// Send user to login page
console.log('Login URL:', login.hosted_url);

// Poll until complete
let state = await kernel.auth.connections.retrieve(auth.id);
while (state.flow_status === 'IN_PROGRESS') {
await new Promise(r => setTimeout(r, 2000));
state = await kernel.auth.connections.retrieve(auth.id);
// Stream state changes until the flow completes
const events = await kernel.auth.connections.follow(auth.id);
let finalState;

for await (const event of events) {
if (event.event === 'managed_auth_state') {
finalState = event;
}
}

if (state.status === 'AUTHENTICATED') {
if (finalState?.flow_status === 'SUCCESS') {
console.log('Authenticated!');
}
```
Expand All @@ -70,13 +73,15 @@ login = await kernel.auth.connections.login(auth.id)
# Send user to login page
print(f"Login URL: {login.hosted_url}")

# Poll until complete
state = await kernel.auth.connections.retrieve(auth.id)
while state.flow_status == "IN_PROGRESS":
await asyncio.sleep(2)
state = await kernel.auth.connections.retrieve(auth.id)
# Stream state changes until the flow completes
events = await kernel.auth.connections.follow(auth.id)
final_state = None

if state.status == "AUTHENTICATED":
async for event in events:
if event.event == "managed_auth_state":
final_state = event

if final_state and final_state.flow_status == "SUCCESS":
print("Authenticated!")
```

Expand All @@ -89,20 +94,21 @@ if err != nil {
// Send user to login page
fmt.Println("Login URL:", login.HostedURL)

// Poll until complete
state, err := client.Auth.Connections.Get(ctx, auth.ID)
if err != nil {
panic(err)
}
for state.FlowStatus == kernel.ManagedAuthFlowStatusInProgress {
time.Sleep(2 * time.Second)
state, err = client.Auth.Connections.Get(ctx, auth.ID)
if err != nil {
panic(err)
// Stream state changes until the flow completes
events := client.Auth.Connections.FollowStreaming(ctx, auth.ID)
authenticated := false

for events.Next() {
event := events.Current()
if event.Event == "managed_auth_state" && event.FlowStatus == "SUCCESS" {
authenticated = true
}
}
if err := events.Err(); err != nil {
panic(err)
}

if state.Status == kernel.ManagedAuthStatusAuthenticated {
if authenticated {
fmt.Println("Authenticated!")
}
```
Expand Down
Loading
Loading