Skip to content
Draft
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
63 changes: 29 additions & 34 deletions agent-auth/auto-login.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,12 @@ const invocation = await kernel.agents.auth.invocations.create({
auth_agent_id: agent.id,
});

// Check if already authenticated
if (invocation.status === 'ALREADY_AUTHENTICATED') {
console.log('Already logged in! Profile ready.');
} else {
// Step 4: Poll for completion
console.log(`Invocation type: ${invocation.type}`); // "auto_login"

const result = await pollForCompletion(invocation.invocation_id);
// Step 4: Poll for completion
const result = await pollForCompletion(invocation.invocation_id);

if (result.success) {
console.log('Auto-login successful!');
}
// If already authenticated, status will immediately be SUCCESS
if (result.success) {
console.log('Auto-login successful!');
}

// Step 5: Use the profile
Expand Down Expand Up @@ -145,17 +139,12 @@ invocation = await kernel.agents.auth.invocations.create(
auth_agent_id=agent.id,
)

# Check if already authenticated
if invocation.status == "ALREADY_AUTHENTICATED":
print("Already logged in! Profile ready.")
else:
# Step 4: Poll for completion
print(f"Invocation type: {invocation.type}") # "auto_login"

result = await poll_for_completion(invocation.invocation_id)
# Step 4: Poll for completion
result = await poll_for_completion(invocation.invocation_id)

if result["success"]:
print("Auto-login successful!")
# If already authenticated, status will immediately be SUCCESS
if result["success"]:
print("Auto-login successful!")

# Step 5: Use the profile
browser = await kernel.browsers.create(
Expand Down Expand Up @@ -240,6 +229,7 @@ async def poll_for_completion(invocation_id: str):
| `awaiting_input` | Waiting for input (manual intervention needed) |
| `submitting` | Currently submitting credentials |
| `completed` | Login flow finished |
| `expired` | Invocation expired before completion |

## Credential Field Mapping

Expand Down Expand Up @@ -288,19 +278,21 @@ if (result.reason === 'MANUAL_INPUT_REQUIRED') {
// Get the 2FA code from user or TOTP generator
const otpCode = await getOTPCode();

// Call discover to see current fields
const discover = await kernel.agents.auth.invocations.discover(
invocation.invocation_id,
{}
// Retrieve to see current pending fields
const state = await kernel.agents.auth.invocations.retrieve(
invocation.invocation_id
);
console.log('Pending fields:', state.pending_fields);

// Submit the missing value
const submit = await kernel.agents.auth.invocations.submit(
// Submit the missing value (async - returns immediately)
await kernel.agents.auth.invocations.submit(
invocation.invocation_id,
{ field_values: { code: otpCode } }
);

if (submit.logged_in) {
// Poll for completion
const finalResult = await pollForCompletion(invocation.invocation_id);
if (finalResult.success) {
console.log('Login completed!');
}
}
Expand All @@ -313,18 +305,21 @@ if result["reason"] == "MANUAL_INPUT_REQUIRED":
# Get the 2FA code from user or TOTP generator
otp_code = await get_otp_code()

# Call discover to see current fields
discover = await kernel.agents.auth.invocations.discover(
invocation.invocation_id,
# Retrieve to see current pending fields
state = await kernel.agents.auth.invocations.retrieve(
invocation.invocation_id
)
print(f"Pending fields: {state.pending_fields}")

# Submit the missing value
submit = await kernel.agents.auth.invocations.submit(
# Submit the missing value (async - returns immediately)
await kernel.agents.auth.invocations.submit(
invocation.invocation_id,
field_values={"code": otp_code},
)

if submit.logged_in:
# Poll for completion
final_result = await poll_for_completion(invocation.invocation_id)
if final_result["success"]:
print("Login completed!")
```
</CodeGroup>
Expand Down
93 changes: 71 additions & 22 deletions agent-auth/credentials.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ Without stored credentials, every time a session expires, you need to redirect u
</Step>
<Step title="Automatic Re-auth">
```typescript
// When session expires, trigger re-auth
const reauth = await kernel.agents.auth.reauth(agent.id);
// When session expires, create a new invocation
// With linked credentials, it will auto-fill and submit
const invocation = await kernel.agents.auth.invocations.create({
auth_agent_id: agent.id,
});
// Poll for completion - credentials are used automatically
```
</Step>
</Steps>
Expand Down Expand Up @@ -129,10 +133,50 @@ const invocation = await kernel.agents.auth.invocations.create({
```

This approach:
- Creates the credential automatically when login succeeds
- Creates the credential automatically when login succeeds (or updates if it already exists)
- Links it to the Auth Agent
- Saves the form selectors for future re-auth

#### Updating Existing Credentials

If you use `save_credential_as` with the name of an existing credential, the submitted values are **merged** into that credential:

```typescript
// 1. Create credential with just the identifier
const credential = await kernel.credentials.create({
name: 'my-login',
domain: 'example.com',
values: { email: 'user@example.com' }, // No password yet
});

// 2. Create agent with the credential linked
const agent = await kernel.agents.auth.create({
domain: 'example.com',
profile_name: 'my-profile',
credential_name: 'my-login',
});

// 3. During login, user provides the password manually
// Using save_credential_as with the SAME name updates the credential
const invocation = await kernel.agents.auth.invocations.create({
auth_agent_id: agent.id,
save_credential_as: 'my-login', // Same name as linked credential
});

// After successful login:
// - The credential now has BOTH email AND password
// - Future re-auth can use both values automatically
```

This is useful when:
- You have partial credentials (e.g., identifier only) and want to capture the password during login
- Users change their password and you want to save the new one
- You want to progressively build up credential values across multiple logins

<Note>
TOTP codes are never saved to credentials—they're one-time codes generated from the TOTP secret.
</Note>

## Using Stored Credentials

Once credentials are linked and an initial login has completed (capturing form selectors), the Auth Agent can re-authenticate automatically.
Expand All @@ -154,23 +198,28 @@ An Auth Agent `can_reauth` when:

### Triggering Re-authentication

When sessions expire, trigger re-authentication:
When sessions expire, create a new invocation. With linked credentials, the system will automatically use them:

```typescript
const reauth = await kernel.agents.auth.reauth(agent.id);
// Check agent status first
const agent = await kernel.agents.auth.retrieve(agentId);

switch (reauth.status) {
case 'REAUTH_STARTED':
console.log('Re-auth started, invocation:', reauth.invocation_id);
if (agent.status === 'NEEDS_AUTH') {
if (agent.can_reauth) {
// Create invocation - credentials will be used automatically
const invocation = await kernel.agents.auth.invocations.create({
auth_agent_id: agent.id,
});

// Poll for completion
break;
case 'ALREADY_AUTHENTICATED':
console.log('Session is still valid');
break;
case 'CANNOT_REAUTH':
console.log('Cannot re-auth:', reauth.message);
// Missing credentials or selectors - need manual login
break;
const result = await pollForCompletion(invocation.invocation_id);
if (result.success) {
console.log('Re-auth successful!');
}
} else {
console.log('Cannot auto re-auth - manual login required');
// Redirect user to hosted_url for manual login
}
}
```

Expand Down Expand Up @@ -391,12 +440,12 @@ async function useAuthenticatedBrowser(agentId: string) {

if (agent.status === 'NEEDS_AUTH') {
if (agent.can_reauth) {
// Automated re-auth
const reauth = await kernel.agents.auth.reauth(agentId);
if (reauth.status === 'REAUTH_STARTED') {
// Wait for re-auth to complete
await pollForCompletion(reauth.invocation_id);
}
// Create invocation - credentials will be used automatically
const invocation = await kernel.agents.auth.invocations.create({
auth_agent_id: agentId,
});
// Wait for re-auth to complete
await pollForCompletion(invocation.invocation_id);
} else {
throw new Error('Cannot re-auth - manual login required');
}
Expand Down
35 changes: 22 additions & 13 deletions agent-auth/hosted-ui.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ Use the Hosted UI when:
});
```
</Step>
<Step title="Redirect User to Hosted UI">
<Step title="Check State and Redirect">
```typescript
if (invocation.status !== 'ALREADY_AUTHENTICATED') {
const state = await kernel.agents.auth.invocations.retrieve(invocation.invocation_id);
if (state.status !== 'SUCCESS') {
window.location.href = invocation.hosted_url;
}
```
Expand Down Expand Up @@ -71,7 +72,9 @@ const invocation = await kernel.agents.auth.invocations.create({
});

// Step 3: Check if already logged in
if (invocation.status === 'ALREADY_AUTHENTICATED') {
let state = await kernel.agents.auth.invocations.retrieve(invocation.invocation_id);

if (state.status === 'SUCCESS') {
console.log('Already authenticated! Profile is ready to use.');
// Skip to Step 6: Use the Profile
} else {
Expand Down Expand Up @@ -142,7 +145,9 @@ invocation = await kernel.agents.auth.invocations.create(
)

# Step 3: Check if already logged in
if invocation.status == "ALREADY_AUTHENTICATED":
state = await kernel.agents.auth.invocations.retrieve(invocation.invocation_id)

if state.status == "SUCCESS":
print("Already authenticated! Profile is ready to use.")
else:
print(f"Redirect user to: {invocation.hosted_url}")
Expand Down Expand Up @@ -222,11 +227,11 @@ const invocation = await kernel.agents.auth.invocations.create({
| `invocation_id` | Unique ID for this auth attempt |
| `hosted_url` | URL to redirect the user to |
| `expires_at` | When the invocation expires (5 minutes) |
| `status` | If `ALREADY_AUTHENTICATED`, profile is already logged in—no redirect needed |

<Warning>
If `status` is `ALREADY_AUTHENTICATED`, the profile is already logged in. Skip the redirect and go straight to using the profile.
</Warning>

<Note>
Poll the invocation immediately after creation. If `status` is already `SUCCESS`, the profile is already logged in—skip the redirect.
</Note>

### 3. Redirect the User

Expand All @@ -249,19 +254,23 @@ The hosted UI will:
On your backend, poll the invocation status until it completes:

```typescript
let status = await kernel.agents.auth.invocations.retrieve(invocation.invocation_id);
let state = await kernel.agents.auth.invocations.retrieve(invocation.invocation_id);

while (status.status === 'IN_PROGRESS') {
while (state.status === 'IN_PROGRESS') {
console.log(`Step: ${state.step}`); // discovering, awaiting_input, submitting, etc.
await new Promise(r => setTimeout(r, 2000)); // Poll every 2 seconds
status = await kernel.agents.auth.invocations.retrieve(invocation.invocation_id);
state = await kernel.agents.auth.invocations.retrieve(invocation.invocation_id);
}

switch (status.status) {
switch (state.status) {
case 'SUCCESS':
console.log('Authentication successful!');
break;
case 'FAILED':
console.log('Login failed:', state.error_message);
break;
case 'EXPIRED':
console.log('User did not complete login in time');
console.log('Invocation expired - user did not complete login in time');
break;
case 'CANCELED':
console.log('Authentication was canceled');
Expand Down
12 changes: 8 additions & 4 deletions agent-auth/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,20 @@ Agent Auth logs users into any website and saves their authenticated browser ses
auth_agent_id: agent.id,
});

// If already logged in, skip the flow
if (invocation.status === 'ALREADY_AUTHENTICATED') {
console.log('Session still valid!');
// Poll to check if already authenticated
const state = await kernel.agents.auth.invocations.retrieve(invocation.invocation_id);

if (state.status === 'SUCCESS') {
console.log('Already authenticated!');
} else {
// Redirect user to complete login
console.log('Login URL:', invocation.hosted_url);
}
```

Invocation statuses: `IN_PROGRESS` → `SUCCESS` | `EXPIRED` | `CANCELED`
**Invocation states:**
- `status`: `IN_PROGRESS` → `SUCCESS` | `EXPIRED` | `CANCELED` | `FAILED`
- `step`: `initialized` → `discovering` → `awaiting_input` ⇄ `submitting` → `completed`
</Step>
<Step title="User Completes Login">
{/* TODO: Add image showing the hosted UI login flow */}
Expand Down
Loading