- name
- sigcli-auth-proxy
- description
- Secure browser SSO and OAuth2 authentication proxy that lets AI agents access authenticated APIs without exposing credentials
- triggers
- ["authenticate with browser SSO for my agent","set up secure API access for AI tools","proxy authenticated requests through sigcli","configure OAuth2 client credentials flow","extract and encrypt session cookies","validate authentication for enterprise SSO","inject credentials into agent requests","run commands with authenticated context"]
# sigcli Auth Proxy Skill
> Skill by [ara.so](https://ara.so) — Devtools Skills collection.
## Overview
sigcli is an authentication CLI and proxy that handles browser-based SSO, OAuth2 flows, and credential injection for AI agents. It extracts credentials (cookies, localStorage, OAuth tokens), encrypts them locally with AES-256-GCM, and injects them into HTTP requests — so agents can access authenticated APIs without ever seeing secrets.
**Key capabilities:**
- Browser SSO authentication for any website/SSO provider
- OAuth2 Client Credentials flow with automatic token refresh
- Encrypted credential storage (`~/.sig/credentials/`)
- Transparent HTTP proxy for agents (`sig proxy`)
- Direct authenticated requests (`sig request`)
- Command execution with injected credentials (`sig run`)
- Multi-provider support in single commands
## Installation
```bash
npm install -g @sigcli/cli
```
Initialize configuration:
```bash
sig init # creates ~/.sig/config.yaml
```
## Core Commands
### Authentication
```bash
# Browser-based SSO (auto-provision)
sig login https://jira.example.com
# OAuth2 Client Credentials
sig login https://api.example.com \
--strategy oauth2 \
--token-url https://api.example.com/oauth/token \
--client-id $CLIENT_ID \
--client-secret $CLIENT_SECRET
# Check authentication status
sig status # all providers
sig status jira-example # specific provider
# View credentials (redacted by default)
sig get jira-example # shows redacted credentials
sig get jira-example --no-redaction # shows raw tokens
# Logout (clears credentials, keeps config)
sig logout jira-example
```
### Making Authenticated Requests
```bash
# Direct HTTP request
sig request https://jira.example.com/rest/api/2/myself
# POST with JSON body
sig request https://jira.example.com/rest/api/2/search \
--method POST \
--body '{"jql":"assignee=currentUser()"}'
# Multiple providers in one request
sig request https://api.example.com/data \
--provider jira-example,github-enterprise
```
### Running Commands with Auth
```bash
# Execute command with credentials injected
sig run jira-example -- curl https://jira.example.com/rest/api/2/myself
# Multi-provider execution
sig run github-enterprise,jira-example -- node script.js
# Environment variables are automatically injected based on apply[] rules
```
### HTTP Proxy Mode
```bash
# Start MITM proxy (injects credentials transparently)
sig proxy --port 8080
# In another terminal or agent config:
export HTTP_PROXY=http://localhost:8080
export HTTPS_PROXY=http://localhost:8080
curl https://jira.example.com/rest/api/2/myself
# Credentials auto-injected by sig proxy
```
## Configuration
Configuration lives in `~/.sig/config.yaml`. Providers are auto-provisioned for SSO sites, or manually configured for public sites and OAuth2.
### Auto-Provisioned SSO (Zero Config)
```yaml
# ~/.sig/config.yaml (generated by sig login)
jira-example:
domains:
- jira.example.com
entryUrl: https://jira.example.com/
strategy: browser
extract:
- from: cookies
as: session
match: '*'
apply:
- in: header
name: Cookie
value: '${session}'
```
### Public Sites with Validation
Public sites need `validateUrl` and/or `validateRule` to distinguish auth cookies from tracking cookies:
```yaml
reddit:
domains:
- www.reddit.com
- reddit.com
entryUrl: https://www.reddit.com/
validateUrl: https://www.reddit.com/prefs/friends
strategy: browser
extract:
- from: cookies
as: cookie
match: '*'
apply:
- in: header
name: Cookie
value: '${cookie}'
```
### Validation Rule (JavaScript Expression)
For APIs that return 200 even when unauthenticated:
```yaml
douyin:
domains:
- www.douyin.com
entryUrl: https://www.douyin.com
validateUrl: https://www.douyin.com/aweme/v1/web/notice/count/
validateRule: 'res.body.status_code === 0'
strategy: browser
extract:
- from: cookies
as: cookie
match: '*'
apply:
- in: header
name: Cookie
value: '${cookie}'
```
**validateRule context:**
- `res.status` - HTTP status code
- `res.body` - parsed JSON or raw string
- `res.headers` - response headers
### OAuth2 Client Credentials
```yaml
api-example:
domains:
- api.example.com
strategy: oauth2
tokenUrl: https://api.example.com/oauth/token
clientId: ${CLIENT_ID}
clientSecret: ${CLIENT_SECRET}
scopes:
- read:data
- write:data
extract:
- from: oauth2
as: access_token
apply:
- in: header
name: Authorization
value: 'Bearer ${access_token}'
```
### localStorage Extraction
For apps that store tokens in localStorage:
```yaml
app-slack:
domains:
- your-org.enterprise.slack.com
entryUrl: https://app.slack.com/client/T12345
strategy: browser
extract:
- from: cookies
as: session
match: '*'
- from: localStorage
as: xoxc-token
match: localConfig_v2
jsonPath: teams.T12345.token
apply:
- in: header
name: Cookie
value: '${session}'
- in: header
name: Authorization
value: 'Bearer ${xoxc-token}'
```
### Multi-Domain Providers
For sites that use multiple domains (e.g., twitter.com → x.com migration):
```yaml
x:
domains:
- x.com
- twitter.com
entryUrl: https://x.com/
validateUrl: https://x.com/i/api/2/notifications/all.json?count=1
strategy: browser
extract:
- from: cookies
as: cookie
match: '*'
- from: cookies
as: ct0
match: 'ct0'
apply:
- in: header
name: Cookie
value: '${cookie}'
- in: header
name: x-csrf-token
value: '${ct0}'
```
### Network Proxy (for VPN/SOCKS)
If the browser needs to go through a proxy:
```yaml
x:
networkProxy: socks5://127.0.0.1:3333
# ... rest of config
```
## Real-World Usage Patterns
### Pattern 1: Agent Accessing Jira
```typescript
// agent-jira.ts
import { execSync } from 'child_process';
function getJiraIssue(issueKey: string): object {
const url = `https://jira.example.com/rest/api/2/issue/${issueKey}`;
const result = execSync(`sig request ${url}`, { encoding: 'utf8' });
return JSON.parse(result);
}
function searchJiraIssues(jql: string): object {
const url = 'https://jira.example.com/rest/api/2/search';
const body = JSON.stringify({ jql });
const result = execSync(
`sig request ${url} --method POST --body '${body}'`,
{ encoding: 'utf8' }
);
return JSON.parse(result);
}
// Usage
const issue = getJiraIssue('PROJ-123');
const myIssues = searchJiraIssues('assignee=currentUser()');
```
### Pattern 2: OAuth2 API with Auto-Refresh
```typescript
// oauth-api-client.ts
import { execSync } from 'child_process';
class AuthenticatedAPIClient {
constructor(private provider: string) {}
private exec(cmd: string): string {
return execSync(cmd, { encoding: 'utf8' });
}
async makeRequest(endpoint: string, method = 'GET', body?: object): Promise<any> {
let cmd = `sig request https://api.example.com${endpoint} --method ${method}`;
if (body) {
cmd += ` --body '${JSON.stringify(body)}'`;
}
try {
const result = this.exec(cmd);
return JSON.parse(result);
} catch (error) {
// sig automatically refreshes OAuth2 tokens on 401
throw error;
}
}
checkStatus(): void {
const status = this.exec(`sig status ${this.provider}`);
console.log(status);
}
}
// Usage
const client = new AuthenticatedAPIClient('oauth-mock');
const data = await client.makeRequest('/api/data');
```
### Pattern 3: Proxy Mode for HTTP Client
```typescript
// proxy-mode-agent.ts
import axios from 'axios';
import { spawn } from 'child_process';
// Start sig proxy
const proxy = spawn('sig', ['proxy', '--port', '8080'], {
stdio: 'inherit'
});
// Configure axios to use proxy
const client = axios.create({
proxy: {
host: 'localhost',
port: 8080,
},
});
// All requests auto-authenticated
async function fetchData() {
const response = await client.get('https://jira.example.com/rest/api/2/myself');
return response.data;
}
// Cleanup
process.on('exit', () => proxy.kill());
```
### Pattern 4: Multi-Provider Request
```typescript
// multi-provider-sync.ts
import { execSync } from 'child_process';
function syncDataAcrossSystems(): void {
// Single request with credentials from multiple providers
const result = execSync(
`sig request https://api.example.com/sync \
--provider jira-example,github-enterprise \
--method POST \
--body '{"sync": true}'`,
{ encoding: 'utf8' }
);
console.log('Sync result:', JSON.parse(result));
}
```
### Pattern 5: Running External Tools
```bash
# Use sig run to inject credentials into any command
# cURL
sig run jira-example -- curl https://jira.example.com/rest/api/2/myself
# Python script
sig run github-enterprise -- python sync_repos.py
# Node.js script with multiple providers
sig run jira-example,slack-enterprise -- node agent.js
```
## Common Validation URLs
| Service | validateUrl |
|-------------|-------------|
| Reddit | `https://www.reddit.com/prefs/friends` |
| X (Twitter) | `https://x.com/i/api/2/notifications/all.json?count=1` |
| LinkedIn | `https://www.linkedin.com/voyager/api/me` |
| YouTube | `https://www.youtube.com/account` |
| V2EX | `https://www.v2ex.com/notifications` |
| Zhihu | `https://www.zhihu.com/api/v4/me` |
## Troubleshooting
### Provider auto-provision fails
**Symptom:** `sig login` completes but provider not created.
**Solution:** Add `validateUrl` for public sites:
```yaml
reddit:
validateUrl: https://www.reddit.com/prefs/friends
```
### Credentials extracted but validation fails
**Symptom:** Browser login succeeds but sig reports "not authenticated".
**Solution 1:** Check if API returns 200 with error in body. Add `validateRule`:
```yaml
validateRule: 'res.body.status_code === 0'
```
**Solution 2:** Ensure `validateUrl` is a protected endpoint (returns 401/403 when logged out).
### OAuth2 token not refreshing
**Symptom:** Token expires and requests fail.
**Solution:** Verify `tokenUrl`, `clientId`, `clientSecret` in config. Check that the OAuth2 server supports `grant_type=client_credentials`.
```bash
sig logout oauth-provider # clear old token
sig get oauth-provider # force re-authentication
```
### localStorage extraction returns null
**Symptom:** `from: localStorage` extracts nothing.
**Solution:** Check `jsonPath` syntax. Open browser DevTools → Application → Local Storage and verify the key structure:
```yaml
extract:
- from: localStorage
as: token
match: appConfig # localStorage key
jsonPath: user.auth.token # nested path
```
### Proxy mode not injecting credentials
**Symptom:** HTTP_PROXY set but requests are unauthenticated.
**Solution:** Ensure domains match. The proxy only injects credentials for domains listed in provider config:
```yaml
provider-name:
domains:
- api.example.com
- auth.example.com # add all relevant domains
Auf GitHub ansehen