jira-auth
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseJira Authentication Skill
Jira身份验证技能
Purpose
用途
Authenticate with Jira Cloud REST API using API tokens or OAuth 2.0. Handle connection setup, credential validation, and rate limiting.
使用API令牌或OAuth 2.0通过Jira Cloud REST API进行身份验证。处理连接设置、凭据验证和速率限制。
When to Use
适用场景
- Setting up Jira API connections
- Validating Jira credentials
- Testing API connectivity
- Managing authentication headers
- 设置Jira API连接
- 验证Jira凭据
- 测试API连通性
- 管理身份验证标头
Prerequisites
前提条件
- Jira Cloud instance URL (e.g., )
mycompany.atlassian.net - API token (generate at Atlassian account settings)
- Email address associated with the Jira account
- Jira Cloud实例URL(例如:)
mycompany.atlassian.net - API令牌(在Atlassian账户设置中生成)
- 与Jira账户关联的邮箱地址
Environment Variables
环境变量
Create a file in the jira skill root directory ():
.env.claude/skills/jira/.envbash
undefined在Jira技能根目录()中创建文件:
.claude/skills/jira/.env.envbash
undefinedRequired
Required
JIRA_EMAIL=user@example.com
JIRA_API_TOKEN=ATATT3xFfGF0...
JIRA_BASE_URL=https://mycompany.atlassian.net
JIRA_EMAIL=user@example.com
JIRA_API_TOKEN=ATATT3xFfGF0...
JIRA_BASE_URL=https://mycompany.atlassian.net
Optional (defaults shown)
Optional (defaults shown)
JIRA_PROJECT_KEY=SCRUM
JIRA_BOARD_ID=1
Copy from `.env.example` template:
```bash
cp .env.example .envJIRA_PROJECT_KEY=SCRUM
JIRA_BOARD_ID=1
从`.env.example`模板复制:
```bash
cp .env.example .envEdit .env with your credentials
Edit .env with your credentials
Get your API token at: https://id.atlassian.com/manage-profile/security/api-tokens
获取API令牌的地址:https://id.atlassian.com/manage-profile/security/api-tokensTest Scripts
测试脚本
Test authentication using the cross-platform runner:
bash
undefined使用跨平台运行器测试身份验证:
bash
undefinedFrom .claude/skills/jira directory
From .claude/skills/jira directory
node scripts/run.js test # Auto-detect runtime
node scripts/run.js --python test # Force Python
node scripts/run.js --node test # Force Node.js
undefinednode scripts/run.js test # Auto-detect runtime
node scripts/run.js --python test # Force Python
node scripts/run.js --node test # Force Node.js
undefinedImplementation Pattern
实现模式
Node.js (ES Modules)
Node.js(ES Modules)
javascript
// Load from environment variables (set by run.js or manually)
const JIRA_EMAIL = process.env.JIRA_EMAIL;
const JIRA_API_TOKEN = process.env.JIRA_API_TOKEN;
const JIRA_BASE_URL = process.env.JIRA_BASE_URL;
const PROJECT_KEY = process.env.JIRA_PROJECT_KEY || 'SCRUM';
// Validate required env vars
if (!JIRA_EMAIL || !JIRA_API_TOKEN || !JIRA_BASE_URL) {
console.error('Error: Missing required environment variables.');
process.exit(1);
}
// Build auth header
const auth = Buffer.from(`${JIRA_EMAIL}:${JIRA_API_TOKEN}`).toString('base64');
const headers = {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
};javascript
// Load from environment variables (set by run.js or manually)
const JIRA_EMAIL = process.env.JIRA_EMAIL;
const JIRA_API_TOKEN = process.env.JIRA_API_TOKEN;
const JIRA_BASE_URL = process.env.JIRA_BASE_URL;
const PROJECT_KEY = process.env.JIRA_PROJECT_KEY || 'SCRUM';
// Validate required env vars
if (!JIRA_EMAIL || !JIRA_API_TOKEN || !JIRA_BASE_URL) {
console.error('Error: Missing required environment variables.');
process.exit(1);
}
// Build auth header
const auth = Buffer.from(`${JIRA_EMAIL}:${JIRA_API_TOKEN}`).toString('base64');
const headers = {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
};Python
Python
python
import base64
import os
import sys
from pathlib import Pathpython
import base64
import os
import sys
from pathlib import PathLoad .env file from parent directory (jira skill root)
Load .env file from parent directory (jira skill root)
def load_env():
env_path = Path(file).parent.parent / '.env'
if env_path.exists():
with open(env_path, 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#') and '=' in line:
key, value = line.split('=', 1)
os.environ.setdefault(key.strip(), value.strip())
load_env()
def load_env():
env_path = Path(file).parent.parent / '.env'
if env_path.exists():
with open(env_path, 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#') and '=' in line:
key, value = line.split('=', 1)
os.environ.setdefault(key.strip(), value.strip())
load_env()
Configuration from environment variables
Configuration from environment variables
JIRA_EMAIL = os.environ.get('JIRA_EMAIL')
JIRA_API_TOKEN = os.environ.get('JIRA_API_TOKEN')
JIRA_BASE_URL = os.environ.get('JIRA_BASE_URL')
PROJECT_KEY = os.environ.get('JIRA_PROJECT_KEY', 'SCRUM')
JIRA_EMAIL = os.environ.get('JIRA_EMAIL')
JIRA_API_TOKEN = os.environ.get('JIRA_API_TOKEN')
JIRA_BASE_URL = os.environ.get('JIRA_BASE_URL')
PROJECT_KEY = os.environ.get('JIRA_PROJECT_KEY', 'SCRUM')
Validate required env vars
Validate required env vars
if not all([JIRA_EMAIL, JIRA_API_TOKEN, JIRA_BASE_URL]):
print('Error: Missing required environment variables.', file=sys.stderr)
sys.exit(1)
if not all([JIRA_EMAIL, JIRA_API_TOKEN, JIRA_BASE_URL]):
print('Error: Missing required environment variables.', file=sys.stderr)
sys.exit(1)
Build auth header
Build auth header
auth_string = f'{JIRA_EMAIL}:{JIRA_API_TOKEN}'
auth_bytes = base64.b64encode(auth_string.encode('utf-8')).decode('utf-8')
HEADERS = {
'Authorization': f'Basic {auth_bytes}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
undefinedauth_string = f'{JIRA_EMAIL}:{JIRA_API_TOKEN}'
auth_bytes = base64.b64encode(auth_string.encode('utf-8')).decode('utf-8')
HEADERS = {
'Authorization': f'Basic {auth_bytes}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
undefinedTypeScript (Reference Pattern)
TypeScript(参考模式)
typescript
function buildJiraAuthHeader(email: string, apiToken: string): string {
const credentials = Buffer.from(`${email}:${apiToken}`).toString('base64');
return `Basic ${credentials}`;
}typescript
function buildJiraAuthHeader(email: string, apiToken: string): string {
const credentials = Buffer.from(`${email}:${apiToken}`).toString('base64');
return `Basic ${credentials}`;
}Step 2: Create Jira Client
步骤2:创建Jira客户端
typescript
interface JiraConfig {
baseUrl: string;
email: string;
apiToken: string;
}
class JiraClient {
private baseUrl: string;
private headers: Record<string, string>;
constructor(config: JiraConfig) {
this.baseUrl = config.baseUrl.replace(/\/$/, '');
this.headers = {
'Authorization': buildJiraAuthHeader(config.email, config.apiToken),
'Accept': 'application/json',
'Content-Type': 'application/json',
};
}
async request<T>(path: string, options: RequestInit = {}): Promise<T> {
const url = `${this.baseUrl}/rest/api/3${path}`;
const response = await fetch(url, {
...options,
headers: { ...this.headers, ...options.headers },
});
if (response.status === 429) {
const resetTime = response.headers.get('X-RateLimit-Reset');
throw new Error(`Rate limited. Reset at: ${resetTime}`);
}
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(`Jira API error: ${response.status} - ${JSON.stringify(error)}`);
}
return response.json();
}
}typescript
interface JiraConfig {
baseUrl: string;
email: string;
apiToken: string;
}
class JiraClient {
private baseUrl: string;
private headers: Record<string, string>;
constructor(config: JiraConfig) {
this.baseUrl = config.baseUrl.replace(/\/$/, '');
this.headers = {
'Authorization': buildJiraAuthHeader(config.email, config.apiToken),
'Accept': 'application/json',
'Content-Type': 'application/json',
};
}
async request<T>(path: string, options: RequestInit = {}): Promise<T> {
const url = `${this.baseUrl}/rest/api/3${path}`;
const response = await fetch(url, {
...options,
headers: { ...this.headers, ...options.headers },
});
if (response.status === 429) {
const resetTime = response.headers.get('X-RateLimit-Reset');
throw new Error(`Rate limited. Reset at: ${resetTime}`);
}
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(`Jira API error: ${response.status} - ${JSON.stringify(error)}`);
}
return response.json();
}
}Step 3: Validate Connection
步骤3:验证连接
typescript
async function validateJiraConnection(client: JiraClient): Promise<boolean> {
try {
const user = await client.request<{ accountId: string; displayName: string }>('/myself');
console.log(`Connected as: ${user.displayName} (${user.accountId})`);
return true;
} catch (error) {
console.error('Jira connection failed:', error);
return false;
}
}typescript
async function validateJiraConnection(client: JiraClient): Promise<boolean> {
try {
const user = await client.request<{ accountId: string; displayName: string }>('/myself');
console.log(`Connected as: ${user.displayName} (${user.accountId})`);
return true;
} catch (error) {
console.error('Jira connection failed:', error);
return false;
}
}curl Examples
curl示例
Test Authentication
测试身份验证
bash
curl -X GET "https://mycompany.atlassian.net/rest/api/3/myself" \
-H "Authorization: Basic $(echo -n 'email@example.com:API_TOKEN' | base64)" \
-H "Accept: application/json"bash
curl -X GET "https://mycompany.atlassian.net/rest/api/3/myself" \
-H "Authorization: Basic $(echo -n 'email@example.com:API_TOKEN' | base64)" \
-H "Accept: application/json"Expected Success Response
预期成功响应
json
{
"self": "https://mycompany.atlassian.net/rest/api/3/user?accountId=...",
"accountId": "5e10b8dbf0cab60d71f4a9cd",
"displayName": "John Doe",
"active": true,
"timeZone": "America/New_York"
}json
{
"self": "https://mycompany.atlassian.net/rest/api/3/user?accountId=...",
"accountId": "5e10b8dbf0cab60d71f4a9cd",
"displayName": "John Doe",
"active": true,
"timeZone": "America/New_York"
}Rate Limiting
速率限制
| Metric | Limit |
|---|---|
| Authenticated requests | 60/minute |
| Unauthenticated requests | 20/minute |
| 指标 | 限制 |
|---|---|
| 已认证请求 | 60次/分钟 |
| 未认证请求 | 20次/分钟 |
Rate Limit Headers
速率限制标头
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Reset: 1640000000X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Reset: 1640000000Handle Rate Limiting
处理速率限制
typescript
async function withRateLimitRetry<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error: any) {
if (error.message.includes('Rate limited') && i < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, 60000));
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}typescript
async function withRateLimitRetry<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error: any) {
if (error.message.includes('Rate limited') && i < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, 60000));
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}Common Mistakes
常见错误
- Using password instead of API token (deprecated)
- Forgetting to base64 encode credentials
- Missing the space after "Basic " in header
- Using wrong base URL format (must include )
https://
- 使用密码而非API令牌(已弃用)
- 忘记对凭据进行base64编码
- 标头中"Basic "后缺少空格
- 使用错误的基础URL格式(必须包含)
https://
References
参考资料
Version History
版本历史
- 2025-12-11: Added .env file setup and test script documentation
- 2025-12-10: Created
- 2025-12-11:添加.env文件设置和测试脚本文档
- 2025-12-10:创建文档