mirror of
https://github.com/Skyvern-AI/skyvern.git
synced 2026-04-28 11:40:32 +00:00
fix(sky-8861): block-output download refresh fallback when artifact_ids are missing (#5675)
This commit is contained in:
parent
c2f0581390
commit
2250788de3
73 changed files with 610 additions and 954 deletions
|
|
@ -1,516 +0,0 @@
|
|||
---
|
||||
title: Handle 2FA
|
||||
subtitle: Configure two-factor authentication for automated logins
|
||||
description: Configure two-factor authentication for Skyvern automations using TOTP secrets, pushed verification codes, pull-based endpoints, or magic links. Supports Bitwarden TOTP integration.
|
||||
slug: developers/credentials/handle-2fa
|
||||
keywords:
|
||||
- TOTP
|
||||
- 2FA
|
||||
- totp_identifier
|
||||
- totp_url
|
||||
- authenticator
|
||||
- email code
|
||||
- SMS code
|
||||
- magic link
|
||||
- send_totp_code
|
||||
- verification
|
||||
---
|
||||
|
||||
Many websites require two-factor authentication (2FA) during login. Skyvern supports multiple 2FA methods to handle these flows automatically.
|
||||
|
||||
---
|
||||
|
||||
## Choose your method
|
||||
|
||||
Select the method that matches how you receive verification codes:
|
||||
|
||||
| Scenario | Recommended Method |
|
||||
|----------|-------------------|
|
||||
| You have the TOTP secret key | [Store TOTP secret](#method-1-store-totp-secret): Skyvern generates codes |
|
||||
| Codes sent to email/SMS you can forward | [Push codes to Skyvern](#method-2-push-codes-to-skyvern) |
|
||||
| Codes sent to a system you control | [Skyvern pulls from your endpoint](#method-3-skyvern-pulls-from-your-endpoint) |
|
||||
| Passwordless/magic link auth | [Magic links](#method-4-magic-links) |
|
||||
| Using Bitwarden with TOTP | [Password manager integration](#password-manager-totp) |
|
||||
|
||||
---
|
||||
|
||||
## Method 1: Store TOTP secret
|
||||
|
||||
**Best for:** Sites where you have access to the TOTP secret (the setup QR code or manual key).
|
||||
|
||||
When you enable 2FA on a website, you're typically shown a QR code or a secret key. Store this secret with your credential, and Skyvern generates valid codes automatically.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import os
|
||||
from skyvern import Skyvern
|
||||
|
||||
client = Skyvern(api_key=os.getenv("SKYVERN_API_KEY"))
|
||||
|
||||
credential = await client.create_credential(
|
||||
name="Acme Portal Login",
|
||||
credential_type="password",
|
||||
credential={
|
||||
"username": "user@example.com",
|
||||
"password": "secure_password",
|
||||
"totp": "JBSWY3DPEHPK3PXP", # Base32 secret from QR code
|
||||
"totp_type": "authenticator",
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
```typescript TypeScript
|
||||
import { SkyvernClient } from "@skyvern/client";
|
||||
|
||||
const client = new SkyvernClient({
|
||||
apiKey: process.env.SKYVERN_API_KEY,
|
||||
});
|
||||
|
||||
const credential = await client.createCredential({
|
||||
name: "Acme Portal Login",
|
||||
credential_type: "password",
|
||||
credential: {
|
||||
username: "user@example.com",
|
||||
password: "secure_password",
|
||||
totp: "JBSWY3DPEHPK3PXP",
|
||||
totp_type: "authenticator",
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X POST "https://api.skyvern.com/v1/credentials" \
|
||||
-H "x-api-key: $SKYVERN_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Acme Portal Login",
|
||||
"credential_type": "password",
|
||||
"credential": {
|
||||
"username": "user@example.com",
|
||||
"password": "secure_password",
|
||||
"totp": "JBSWY3DPEHPK3PXP",
|
||||
"totp_type": "authenticator"
|
||||
}
|
||||
}'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### TOTP types
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `authenticator` | Standard TOTP codes (Google Authenticator, Authy, etc.) |
|
||||
| `email` | Codes sent via email that you'll push to Skyvern |
|
||||
| `text` | Codes sent via SMS that you'll push to Skyvern |
|
||||
|
||||
<Tip>
|
||||
Skyvern generates TOTP codes using standard 30-second windows. If the current code has less than 20 seconds remaining, Skyvern automatically uses the next code to ensure successful entry.
|
||||
</Tip>
|
||||
|
||||
### Finding your TOTP secret
|
||||
|
||||
Most sites show the secret when you set up 2FA:
|
||||
|
||||
1. Go to your account's security settings
|
||||
2. Click "Set up authenticator app"
|
||||
3. Look for "Can't scan the QR code?" or "Manual entry"
|
||||
4. Copy the secret key (usually Base32 format like `JBSWY3DPEHPK3PXP`)
|
||||
|
||||
<Note>
|
||||
If you've already set up 2FA and don't have the secret, you may need to disable and re-enable 2FA to see the secret again. Contact [support@skyvern.com](mailto:support@skyvern.com) for help.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Method 2: Push codes to Skyvern
|
||||
|
||||
**Best for:** Codes sent to email or SMS that you can programmatically forward.
|
||||
|
||||
When your automation encounters a 2FA prompt, Skyvern polls for codes that you push via API. Your email or SMS forwarding system sends codes to Skyvern as they arrive.
|
||||
|
||||
### Step 1: Set up code forwarding
|
||||
|
||||
Configure your email or SMS to forward verification codes to Skyvern. Common approaches:
|
||||
|
||||
- **Email:** Gmail filter + Zapier webhook
|
||||
- **SMS:** Twilio webhook to your server
|
||||
- **Email:** AWS SES + Lambda function
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="How to set up Email Forwarding with Zapier?">
|
||||
|
||||
This setup requires a Zapier Pro plan account.
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a Zapier Zap">
|
||||
{/* TODO: Replace with actual video */}
|
||||
<Frame>
|
||||
<video
|
||||
autoPlay
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
className="rounded-xl"
|
||||
src="/images/zapier-forwarding-setup.mp4"
|
||||
></video>
|
||||
</Frame>
|
||||
|
||||
1. Go to [zapier.com](https://zapier.com/app/home) and create a new Zap
|
||||
2. In the newly created Zap draft, click the "Trigger" button
|
||||
3. Click **Email by Zapier**
|
||||
4. In the Email "Setup", pick **New Inbound Email** in the Trigger event selection. Click **Continue**
|
||||
5. In Email "Configure", create an email address which will be used to forward emails for TOTP codes. Click **Continue**
|
||||
6. Click the "Action" button and add **Webhooks by Zapier**
|
||||
7. In the Setup, choose **POST** under the Action event selection. Click **Continue**
|
||||
8. In the "Configure", set up these fields to make a POST request to Skyvern's TOTP API:
|
||||
- **URL:** `https://api.skyvern.com/v1/credentials/totp`
|
||||
- **Payload Type:** `json`
|
||||
- **Data:**
|
||||
- `totp_identifier`: choose **Raw To Email** after clicking the "+" sign
|
||||
- `content`: choose **Body Plain** after clicking the "+" sign
|
||||
- `source`: `email`
|
||||
- **Headers:**
|
||||
- `x-api-key`: your Skyvern API key
|
||||
9. Click **Continue**
|
||||
</Step>
|
||||
<Step title="Add forwarding email and create a filter in Gmail">
|
||||
Go to Gmail Settings → Forwarding and POP/IMAP → click **Add a forwarding address** → enter the Zapier email address you created. Complete any verification steps.
|
||||
|
||||
Then go to **Filters and Blocked Addresses**. Click **Create a new filter** and set up a filtering rule for your TOTP (2FA/MFA) emails. Click **Create filter**, check **Forward it to**, pick the Zapier email address, and update the filter.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/totp/create_email_forwarding.png" alt="Gmail forwarding filter" width="400"/>
|
||||
</Frame>
|
||||
</Step>
|
||||
<Step title="Test it end to end">
|
||||
Forward any previous TOTP email to the Zapier email address you created.
|
||||
|
||||
In Zapier, under the "Test" of the Webhooks action, send a request to test it out. If successful, you should see a "A request was sent to Webhooks by Zapier" message.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="How to set up SMS Forwarding with Twilio?">
|
||||
|
||||
Use a virtual phone number service like [Twilio](https://www.twilio.com/en-us/docs/usage/tutorials/how-to-use-your-free-trial-account) or [Plivo](https://www.plivo.com/) to receive SMS codes and forward them to Skyvern.
|
||||
|
||||
<Steps>
|
||||
<Step title="Set up a Twilio phone number">
|
||||
Create a Twilio account and provision a phone number. This number will receive the SMS verification codes.
|
||||
</Step>
|
||||
<Step title="Create a Twilio Function to forward codes">
|
||||
In your Twilio console, create a new Function that forwards incoming SMS to Skyvern's TOTP API:
|
||||
|
||||
```javascript
|
||||
// Twilio Function
|
||||
exports.handler = async function(context, event, callback) {
|
||||
const axios = require('axios');
|
||||
|
||||
await axios.post('https://api.skyvern.com/v1/credentials/totp', {
|
||||
totp_identifier: event.To, // Your Twilio phone number
|
||||
content: event.Body,
|
||||
source: 'phone'
|
||||
}, {
|
||||
headers: {
|
||||
'x-api-key': context.SKYVERN_API_KEY,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
return callback(null, new Twilio.Response());
|
||||
};
|
||||
```
|
||||
|
||||
Add your `SKYVERN_API_KEY` as an environment variable in the Twilio Function configuration.
|
||||
</Step>
|
||||
<Step title="Configure the webhook">
|
||||
In your Twilio phone number settings, set the **A Message Comes In** webhook to point to your Twilio Function URL.
|
||||
</Step>
|
||||
<Step title="Test it">
|
||||
Send a test SMS to your Twilio number and verify the code appears in Skyvern:
|
||||
|
||||
```bash
|
||||
curl -X GET "https://api.skyvern.com/v1/credentials/totp?totp_identifier=YOUR_TWILIO_NUMBER" \
|
||||
-H "x-api-key: $SKYVERN_API_KEY"
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Step 2: Start the run with a TOTP identifier
|
||||
|
||||
When running your login, include a `totp_identifier` that matches what you'll use when pushing codes:
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
result = await client.login(
|
||||
credential_type="skyvern",
|
||||
url="https://portal.example.com/login",
|
||||
credential_id="cred_xyz789",
|
||||
totp_identifier="user@example.com", # Must match when pushing code
|
||||
)
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X POST "https://api.skyvern.com/v1/run/tasks/login" \
|
||||
-H "x-api-key: $SKYVERN_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"credential_type": "skyvern",
|
||||
"url": "https://portal.example.com/login",
|
||||
"credential_id": "cred_xyz789",
|
||||
"totp_identifier": "user@example.com"
|
||||
}'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Step 3: Push the code when received
|
||||
|
||||
When the verification code arrives, push it to Skyvern:
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
await client.send_totp_code(
|
||||
totp_identifier="user@example.com",
|
||||
content="Your verification code is 123456", # Full email/SMS body
|
||||
source="email",
|
||||
)
|
||||
```
|
||||
|
||||
```typescript TypeScript
|
||||
await client.sendTotpCode({
|
||||
totp_identifier: "user@example.com",
|
||||
content: "Your verification code is 123456",
|
||||
source: "email",
|
||||
});
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X POST "https://api.skyvern.com/v1/credentials/totp" \
|
||||
-H "x-api-key: $SKYVERN_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"totp_identifier": "user@example.com",
|
||||
"content": "Your verification code is 123456",
|
||||
"source": "email"
|
||||
}'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Note>
|
||||
If `content` is longer than 10 characters, Skyvern's AI extracts the verification code automatically. You can send the full email or SMS body without parsing it yourself.
|
||||
</Note>
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Method 3: Skyvern pulls from your endpoint
|
||||
|
||||
**Best for:** Environments where you control the system receiving 2FA codes and can expose them via an HTTP endpoint.
|
||||
|
||||
Instead of pushing codes to Skyvern, you implement an endpoint that Skyvern polls until a code is available.
|
||||
|
||||
### Step 1: Implement the endpoint
|
||||
|
||||
Your endpoint must accept POST requests and return the verification code:
|
||||
|
||||
**Request from Skyvern:**
|
||||
```http
|
||||
POST https://your-server.com/totp-webhook
|
||||
x-skyvern-signature: <hmac-sha256-signature>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"task_id": "tsk_123456"
|
||||
}
|
||||
```
|
||||
|
||||
**Expected response:**
|
||||
```json
|
||||
{
|
||||
"task_id": "tsk_123456",
|
||||
"verification_code": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Verify request signatures
|
||||
|
||||
Skyvern signs all requests using HMAC-SHA256 with your API key. Verify the signature to ensure requests are authentic:
|
||||
|
||||
```python
|
||||
import hmac
|
||||
import hashlib
|
||||
|
||||
def verify_skyvern_request(request, api_key: str) -> bool:
|
||||
signature = request.headers.get("x-skyvern-signature")
|
||||
payload = request.body # bytes
|
||||
|
||||
expected = hmac.new(
|
||||
api_key.encode("utf-8"),
|
||||
msg=payload,
|
||||
digestmod=hashlib.sha256
|
||||
).hexdigest()
|
||||
|
||||
return hmac.compare_digest(signature, expected)
|
||||
```
|
||||
|
||||
### Step 3: Configure the login
|
||||
|
||||
Pass `totp_url` when running your login:
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
result = await client.login(
|
||||
credential_type="skyvern",
|
||||
url="https://portal.example.com/login",
|
||||
credential_id="cred_xyz789",
|
||||
totp_url="https://your-server.com/totp-webhook",
|
||||
)
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X POST "https://api.skyvern.com/v1/run/tasks/login" \
|
||||
-H "x-api-key: $SKYVERN_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"credential_type": "skyvern",
|
||||
"url": "https://portal.example.com/login",
|
||||
"credential_id": "cred_xyz789",
|
||||
"totp_url": "https://your-server.com/totp-webhook"
|
||||
}'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Skyvern polls your endpoint every 10 seconds until a code is returned or the timeout is reached.
|
||||
|
||||
---
|
||||
|
||||
## Method 4: Magic links
|
||||
|
||||
**Best for:** Passwordless authentication systems that send one-time login links.
|
||||
|
||||
When a site uses magic links instead of passwords, push the link to Skyvern:
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
await client.send_totp_code(
|
||||
totp_identifier="user@example.com",
|
||||
content="https://portal.example.com/login?token=abc123xyz",
|
||||
source="email",
|
||||
)
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X POST "https://api.skyvern.com/v1/credentials/totp" \
|
||||
-H "x-api-key: $SKYVERN_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"totp_identifier": "user@example.com",
|
||||
"content": "https://portal.example.com/login?token=abc123xyz",
|
||||
"source": "email"
|
||||
}'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Note>
|
||||
Skyvern automatically detects URLs and classifies them as magic links. The automation will navigate to the link to complete authentication.
|
||||
</Note>
|
||||
|
||||
For magic link flows, structure your workflow in two parts:
|
||||
|
||||
1. **Trigger the magic link**: Navigate to login and enter email
|
||||
2. **Complete login**: Use the magic link as the starting URL for the next step
|
||||
|
||||
---
|
||||
|
||||
## Password manager TOTP
|
||||
|
||||
If you store TOTP secrets in Bitwarden or 1Password, Skyvern can retrieve both the password and TOTP code in a single request.
|
||||
|
||||
### Bitwarden
|
||||
|
||||
Store your TOTP secret in Bitwarden's authenticator field:
|
||||
|
||||
1. Open the vault item in Bitwarden
|
||||
2. Click "Authenticator Key (TOTP)"
|
||||
3. Enter your TOTP secret or scan the QR code
|
||||
4. Skyvern retrieves both password and TOTP when using this credential
|
||||
|
||||
**Supported TOTP formats:**
|
||||
- Raw secret: `JBSWY3DPEHPK3PXP`
|
||||
- otpauth URI: `otpauth://totp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example`
|
||||
|
||||
### 1Password
|
||||
|
||||
Store TOTP in 1Password's one-time password field:
|
||||
|
||||
1. Edit the login item in 1Password
|
||||
2. Add a "One-Time Password" field
|
||||
3. Enter the TOTP secret or scan the QR code
|
||||
4. Reference the item in your Skyvern workflow
|
||||
|
||||
---
|
||||
|
||||
## Multi-field TOTP entry
|
||||
|
||||
Some sites split TOTP codes across multiple input fields (one digit per box). Skyvern handles this automatically:
|
||||
|
||||
1. Detects multi-field TOTP layouts
|
||||
2. Enters the same code across all fields
|
||||
3. Caches the code to prevent requesting a new one mid-entry
|
||||
|
||||
<Note>
|
||||
No special configuration needed. Skyvern's AI recognizes multi-field TOTP inputs and handles them correctly.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Timeouts and polling
|
||||
|
||||
Skyvern waits for 2FA codes based on configurable timeouts:
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| Polling interval | 10 seconds | How often Skyvern checks for new codes |
|
||||
| Polling timeout | 15 minutes | Maximum wait time before failing |
|
||||
|
||||
If you're pushing codes, ensure your forwarding pipeline delivers codes within the timeout window.
|
||||
|
||||
---
|
||||
|
||||
## List recent codes
|
||||
|
||||
Debug your 2FA integration by listing recent codes received:
|
||||
|
||||
```bash
|
||||
curl -X GET "https://api.skyvern.com/v1/credentials/totp?totp_identifier=user@example.com&limit=10" \
|
||||
-H "x-api-key: $SKYVERN_API_KEY"
|
||||
```
|
||||
|
||||
**Query parameters:**
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `totp_identifier` | Filter by identifier (email, phone, etc.) |
|
||||
| `workflow_run_id` | Filter by specific workflow run |
|
||||
| `otp_type` | Filter by type: `totp` or `magic_link` |
|
||||
| `limit` | Number of records (default 50, max 200) |
|
||||
|
||||
<Note>
|
||||
Skyvern only returns codes from the last 10 minutes (configurable via `TOTP_LIFESPAN_MINUTES`).
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Store Credentials" icon="key" href="/developers/credentials/store-credentials">
|
||||
Set up your credential vault integration
|
||||
</Card>
|
||||
<Card title="Troubleshooting" icon="wrench" href="/developers/credentials/troubleshooting-login">
|
||||
Debug 2FA and login failures
|
||||
</Card>
|
||||
</CardGroup>
|
||||
Loading…
Add table
Add a link
Reference in a new issue