jira-auth

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Jira 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
.env
file in the jira skill root directory (
.claude/skills/jira/.env
):
bash
undefined
在Jira技能根目录(
.claude/skills/jira/.env
)中创建
.env
文件:
bash
undefined

Required

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 .env
JIRA_PROJECT_KEY=SCRUM JIRA_BOARD_ID=1

从`.env.example`模板复制:
```bash
cp .env.example .env

Edit .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-tokens

Test Scripts

测试脚本

Test authentication using the cross-platform runner:
bash
undefined
使用跨平台运行器测试身份验证:
bash
undefined

From .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
undefined
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
undefined

Implementation 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 Path
python
import base64
import os
import sys
from pathlib import Path

Load .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' }
undefined
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' }
undefined

TypeScript (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

速率限制

MetricLimit
Authenticated requests60/minute
Unauthenticated requests20/minute
指标限制
已认证请求60次/分钟
未认证请求20次/分钟

Rate Limit Headers

速率限制标头

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Reset: 1640000000
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Reset: 1640000000

Handle 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:创建文档