Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 264 additions & 0 deletions docs/azure-identity-authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
# Azure Identity Authentication for Azure DevOps MCP Server

This document outlines the implementation approach for adding Azure Identity authentication support to the Azure DevOps MCP Server.

## Overview

The Azure DevOps MCP Server currently supports Personal Access Token (PAT) authentication. This enhancement will add support for Azure Identity authentication methods, specifically DefaultAzureCredential and AzureCliCredential, to provide more flexible authentication options for different environments.

## Azure Identity SDK

The `@azure/identity` package provides various credential types for authenticating with Azure services. For our implementation, we will focus on the following credential types:

### DefaultAzureCredential

`DefaultAzureCredential` provides a simplified authentication experience by trying multiple credential types in sequence:

1. Environment variables (EnvironmentCredential)
2. Managed Identity (ManagedIdentityCredential)
3. Azure CLI (AzureCliCredential)
4. Visual Studio Code (VisualStudioCodeCredential)
5. Azure PowerShell (AzurePowerShellCredential)
6. Interactive Browser (InteractiveBrowserCredential) - optional, disabled by default

This makes it ideal for applications that need to work in different environments (local development, Azure-hosted) without code changes.

### AzureCliCredential

`AzureCliCredential` authenticates using the Azure CLI's logged-in account. It requires the Azure CLI to be installed and the user to be logged in (`az login`). This is particularly useful for local development scenarios where developers are already using the Azure CLI.

## Implementation Approach

### 1. Authentication Abstraction Layer

Create an abstraction layer for authentication that supports both PAT and Azure Identity methods:

```typescript
// src/api/auth.ts
export interface AuthProvider {
getConnection(): Promise<WebApi>;
isAuthenticated(): Promise<boolean>;
}

export class PatAuthProvider implements AuthProvider {
// Existing PAT authentication implementation
}

export class AzureIdentityAuthProvider implements AuthProvider {
// New Azure Identity authentication implementation
}
```

### 2. Authentication Factory

Implement a factory pattern to create the appropriate authentication provider based on configuration:

```typescript
// src/api/auth.ts
export enum AuthMethod {
PAT = 'pat',
AZURE_IDENTITY = 'azure-identity'
}

export function createAuthProvider(config: AzureDevOpsConfig): AuthProvider {
switch (config.authMethod) {
case AuthMethod.AZURE_IDENTITY:
return new AzureIdentityAuthProvider(config);
case AuthMethod.PAT:
default:
return new PatAuthProvider(config);
}
}
```

### 3. Azure Identity Authentication Provider

Implement the Azure Identity authentication provider:

```typescript
// src/api/auth.ts
export class AzureIdentityAuthProvider implements AuthProvider {
private config: AzureDevOpsConfig;
private connectionPromise: Promise<WebApi> | null = null;

constructor(config: AzureDevOpsConfig) {
this.config = config;
}

async getConnection(): Promise<WebApi> {
if (!this.connectionPromise) {
this.connectionPromise = this.createConnection();
}
return this.connectionPromise;
}

private async createConnection(): Promise<WebApi> {
try {
// Azure DevOps resource ID for token scope
const azureDevOpsResourceId = '499b84ac-1321-427f-aa17-267ca6975798';

// Create credential based on configuration
const credential = this.createCredential();

// Get token for Azure DevOps
const token = await credential.getToken(`${azureDevOpsResourceId}/.default`);

if (!token) {
throw new AzureDevOpsAuthenticationError('Failed to acquire token from Azure Identity');
}

// Create auth handler with token
const authHandler = new BearerCredentialHandler(token.token);

// Create WebApi client
const connection = new WebApi(this.config.organizationUrl, authHandler);

// Test the connection
await connection.getLocationsApi();

return connection;
} catch (error) {
throw new AzureDevOpsAuthenticationError(
`Failed to authenticate with Azure Identity: ${error instanceof Error ? error.message : String(error)}`
);
}
}

private createCredential(): TokenCredential {
if (this.config.azureIdentityOptions?.useAzureCliCredential) {
return new AzureCliCredential();
}

// Default to DefaultAzureCredential
return new DefaultAzureCredential();
}

async isAuthenticated(): Promise<boolean> {
try {
await this.getConnection();
return true;
} catch {
return false;
}
}
}
```

### 4. Configuration Updates

Update the configuration interface to support specifying the authentication method:

```typescript
// src/types/config.ts
export interface AzureDevOpsConfig {
// Existing properties
organizationUrl: string;
personalAccessToken?: string;
defaultProject?: string;
apiVersion?: string;

// New properties
authMethod?: AuthMethod;
azureIdentityOptions?: {
useAzureCliCredential?: boolean;
// Other Azure Identity options as needed
};
}
```

### 5. Environment Variable Updates

Update the environment variable handling in `index.ts`:

```typescript
// src/index.ts
const config: AzureDevOpsConfig = {
organizationUrl: process.env.AZURE_DEVOPS_ORG_URL || '',
personalAccessToken: process.env.AZURE_DEVOPS_PAT,
defaultProject: process.env.AZURE_DEVOPS_DEFAULT_PROJECT,
apiVersion: process.env.AZURE_DEVOPS_API_VERSION,
authMethod: (process.env.AZURE_DEVOPS_AUTH_METHOD as AuthMethod) || AuthMethod.PAT,
azureIdentityOptions: {
useAzureCliCredential: process.env.AZURE_DEVOPS_USE_CLI_CREDENTIAL === 'true'
}
};
```

### 6. Client Updates

Update the `AzureDevOpsClient` class to use the authentication provider:

```typescript
// src/api/client.ts
export class AzureDevOpsClient {
private authProvider: AuthProvider;

constructor(config: AzureDevOpsConfig) {
this.authProvider = createAuthProvider(config);
}

private async getClient(): Promise<WebApi> {
return this.authProvider.getConnection();
}

// Rest of the class remains the same
}
```

## Error Handling

Implement proper error handling for Azure Identity authentication failures:

```typescript
// src/common/errors.ts
export class AzureIdentityAuthenticationError extends AzureDevOpsAuthenticationError {
constructor(message: string) {
super(`Azure Identity Authentication Error: ${message}`);
}
}
```

## Configuration Examples

### PAT Authentication

```env
AZURE_DEVOPS_ORG_URL=https://dev.azure.com/your-org
AZURE_DEVOPS_PAT=your-pat
AZURE_DEVOPS_AUTH_METHOD=pat
```

### DefaultAzureCredential Authentication

```env
AZURE_DEVOPS_ORG_URL=https://dev.azure.com/your-org
AZURE_DEVOPS_AUTH_METHOD=azure-identity
# Optional environment variables for specific credential types
AZURE_TENANT_ID=your-tenant-id
AZURE_CLIENT_ID=your-client-id
AZURE_CLIENT_SECRET=your-client-secret
```

### AzureCliCredential Authentication

```env
AZURE_DEVOPS_ORG_URL=https://dev.azure.com/your-org
AZURE_DEVOPS_AUTH_METHOD=azure-identity
AZURE_DEVOPS_USE_CLI_CREDENTIAL=true
```

## Testing

Implement tests for the new authentication methods:

1. Unit tests for the authentication providers
2. Integration tests for Azure Identity authentication
3. Tests for fallback behavior

## Documentation Updates

Update the README.md and other documentation to include information about the new authentication methods.

## Conclusion

This implementation approach provides a flexible and extensible way to add Azure Identity authentication support to the Azure DevOps MCP Server. It allows users to choose the authentication method that best suits their environment and needs, while maintaining backward compatibility with the existing PAT authentication method.
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"author": "",
"license": "MIT",
"dependencies": {
"@azure/identity": "^4.0.0",
"@azure/identity": "^4.8.0",
"@modelcontextprotocol/sdk": "^1.6.0",
"axios": "^1.6.0",
"azure-devops-node-api": "^12.0.0",
Expand Down
44 changes: 44 additions & 0 deletions project-management/task-management/doing.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,46 @@
## Current Tasks In Progress

- [ ] **Task A.1**: Research and document Azure Identity implementation options
- **Role**: Technical Architect
- **Phase**: Research
- **Description**: Research DefaultAzureCredential and related Azure Identity SDKs, determine ideal authentication flow using Azure CLI credentials, and document findings and implementation approach.
- **Notes**:
- **Azure Identity SDK Overview**:
- The `@azure/identity` package provides various credential types for authenticating with Azure services
- Key credential types include `DefaultAzureCredential`, `AzureCliCredential`, `ChainedTokenCredential`, and others
- These credentials can be used with Azure DevOps by obtaining a bearer token and using it with the `BearerCredentialHandler`

- **DefaultAzureCredential**:
- Provides a simplified authentication experience by trying multiple credential types in sequence
- Attempts to authenticate using environment variables, managed identity, Azure CLI, Visual Studio Code, and other methods
- Ideal for applications that need to work in different environments (local development, Azure-hosted) without code changes
- For Azure DevOps, it requires the resource ID `499b84ac-1321-427f-aa17-267ca6975798` to obtain the correct token scope

- **AzureCliCredential**:
- Authenticates using the Azure CLI's logged-in account
- Requires the Azure CLI to be installed and the user to be logged in (`az login`)
- Good for local development scenarios where developers are already using the Azure CLI
- Can be used as a fallback mechanism in a `ChainedTokenCredential`

- **Implementation Approach**:
- Create an abstraction layer for authentication that supports both PAT and Azure Identity methods
- Implement a factory pattern to create the appropriate credential based on configuration
- Use `DefaultAzureCredential` as the primary Azure Identity method with `AzureCliCredential` as a fallback
- Update the configuration to support specifying the authentication method (PAT, AAD, DefaultAzureCredential)
- Implement proper error handling and logging for authentication failures

- **Integration with Azure DevOps Node API**:
- The Azure DevOps Node API supports bearer token authentication via the `BearerCredentialHandler` class
- Tokens obtained from Azure Identity can be used with this handler to authenticate API requests
- Example: `const authHandler = new BearerCredentialHandler(token.token); const connection = new WebApi(orgUrl, authHandler);`

- **Configuration Requirements**:
- For PAT: `AZURE_DEVOPS_PAT` and `AZURE_DEVOPS_ORG_URL`
- For DefaultAzureCredential: `AZURE_DEVOPS_ORG_URL` and potentially `AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET` depending on the environment
- New configuration option: `AZURE_DEVOPS_AUTH_METHOD` to specify which authentication method to use

- **Sub-tasks**:
- [x] Research DefaultAzureCredential and related Azure Identity SDKs
- [x] Determine ideal authentication flow using Azure CLI credentials
- [x] Document findings and implementation approach

33 changes: 33 additions & 0 deletions project-management/task-management/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,39 @@
- Documentation is still handled as a separate step for clarity.
- Tasks are prefixed with `[ ]` for tracking completion.

### Authentication Enhancements

- [ ] **Task A.2**: Create authentication abstraction layer
- **Role**: Full-Stack Developer
- Design interface to abstract authentication methods (PAT, AAD, DefaultAzureCredential)
- Implement factory pattern for credential creation
- Add unit tests
- [ ] **Task A.3**: Implement DefaultAzureCredential authentication
- **Role**: Full-Stack Developer
- Add @azure/identity package
- Implement bearer token authentication handler
- Create DefaultAzureCredential authentication flow
- Add unit tests
- [ ] **Task A.4**: Implement AzureCliCredential fallback
- **Role**: Full-Stack Developer
- Add specific support for AzureCliCredential
- Implement proper error handling and fallback mechanisms
- Add unit tests
- [ ] **Task A.5**: Update environment configuration
- **Role**: Full-Stack Developer
- Update .env file format to support new authentication methods
- Implement configuration validation
- Add unit tests
- [ ] **Task A.6**: Create integration tests for Azure Identity
- **Role**: Full-Stack Developer
- Add integration tests using Azure CLI credentials
- Add integration tests using DefaultAzureCredential
- Add tests for credential fallback behavior
- [ ] **Task A.7**: Update authentication documentation
- **Role**: Technical Writer
- Document new authentication methods
- Add examples for all supported auth methods
- Update troubleshooting guide
---

- [ ] **Task 1.4**: Implement `list_projects` using WebApi with tests
Expand Down
Loading