Loading...
Loading...
Secure browser SSO and OAuth2 authentication proxy that lets AI agents access authenticated APIs without exposing credentials
npx skill4agent add aradotso/devtools-skills sigcli-auth-proxySkill by ara.so — Devtools Skills collection.
~/.sig/credentials/sig proxysig requestsig runnpm install -g @sigcli/clisig init # creates ~/.sig/config.yaml# 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# 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# 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# 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~/.sig/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}'validateUrlvalidateRulereddit:
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}'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}'res.statusres.bodyres.headersapi-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}'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}'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}'x:
networkProxy: socks5://127.0.0.1:3333
# ... rest of config// 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()');// 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');// 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());// 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));
}# 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| Service | validateUrl |
|---|---|
| |
| X (Twitter) | |
| |
| YouTube | |
| V2EX | |
| Zhihu | |
sig loginvalidateUrlreddit:
validateUrl: https://www.reddit.com/prefs/friendsvalidateRulevalidateRule: 'res.body.status_code === 0'validateUrltokenUrlclientIdclientSecretgrant_type=client_credentialssig logout oauth-provider # clear old token
sig get oauth-provider # force re-authenticationfrom: localStoragejsonPathextract:
- from: localStorage
as: token
match: appConfig # localStorage key
jsonPath: user.auth.token # nested pathprovider-name:
domains:
- api.example.com
- auth.example.com # add all relevant domainssig proxy --helpError reading credentialsrm ~/.sig/credentials/provider-name.json
sig login https://provider.example.com~/.sig/credentials/~/.sig/logs/sig get --no-redactionsig status <provider>sig requestsig proxysig requestfunction ensureAuthenticated(provider: string): boolean {
try {
execSync(`sig status ${provider}`, { encoding: 'utf8' });
return true;
} catch {
console.error(`Not authenticated. Run: sig login https://${provider}.com`);
return false;
}
}
if (ensureAuthenticated('jira-example')) {
const data = execSync('sig request https://jira.example.com/rest/api/2/myself');
// process data
}