Skip to content
Draft
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
1,520 changes: 1,017 additions & 503 deletions Microsoft.Identity.Web.sln

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Microsoft.Identity.Web.Analyzers;

/// <summary>
/// Analyzer that detects usage of obsolete AddDownstreamWebApi extension method.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AddDownstreamWebApiAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Rule = new(
id: DiagnosticIds.AddDownstreamWebApiObsolete,
title: "AddDownstreamWebApi is obsolete",
messageFormat: "AddDownstreamWebApi is obsolete. Use AddDownstreamApi instead. See https://aka.ms/id-web-downstream-api-v2 for migration details.",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "AddDownstreamWebApi has been replaced by AddDownstreamApi from Microsoft.Identity.Abstractions. Update your code to use the new API.");

/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();

context.RegisterSyntaxNodeAction(AnalyzeInvocation, SyntaxKind.InvocationExpression);
}

private static void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
{
var invocation = (InvocationExpressionSyntax)context.Node;

if (invocation.Expression is not MemberAccessExpressionSyntax memberAccess)
{
return;
}

var methodSymbol = context.SemanticModel.GetSymbolInfo(invocation, context.CancellationToken).Symbol as IMethodSymbol;

if (methodSymbol is null)
{
return;
}

if (IsAddDownstreamWebApiMethod(methodSymbol))
{
var diagnostic = Diagnostic.Create(Rule, memberAccess.Name.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}

private static bool IsAddDownstreamWebApiMethod(IMethodSymbol methodSymbol)
{
return methodSymbol.Name == "AddDownstreamWebApi" &&
methodSymbol.ContainingType?.Name == "DownstreamWebApiExtensions" &&
methodSymbol.ContainingNamespace?.ToDisplayString() == "Microsoft.Identity.Web";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
; Shipped analyzer releases
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
; Unshipped analyzer release
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md

### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
IDW4001 | Usage | Warning | TokenAcquirerTokenCredential is obsolete. Use MicrosoftIdentityTokenCredential instead.
IDW4002 | Usage | Warning | TokenAcquirerAppTokenCredential is obsolete. Use MicrosoftIdentityTokenCredential instead.
IDW4003 | Usage | Warning | AddDownstreamWebApi is obsolete. Use AddDownstreamApi instead.
IDW4004 | Usage | Warning | IDownstreamWebApi is obsolete. Use IDownstreamApi instead.
30 changes: 30 additions & 0 deletions src/Microsoft.Identity.Web.Analyzers/DiagnosticIds.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.Identity.Web.Analyzers;

/// <summary>
/// Diagnostic IDs for Microsoft.Identity.Web v4 migration analyzers.
/// </summary>
public static class DiagnosticIds
{
/// <summary>
/// IDW4001: TokenAcquirerTokenCredential is obsolete.
/// </summary>
public const string TokenAcquirerTokenCredentialObsolete = "IDW4001";

/// <summary>
/// IDW4002: TokenAcquirerAppTokenCredential is obsolete.
/// </summary>
public const string TokenAcquirerAppTokenCredentialObsolete = "IDW4002";

/// <summary>
/// IDW4003: AddDownstreamWebApi is obsolete.
/// </summary>
public const string AddDownstreamWebApiObsolete = "IDW4003";

/// <summary>
/// IDW4004: IDownstreamWebApi is obsolete.
/// </summary>
public const string IDownstreamWebApiObsolete = "IDW4004";
}
27 changes: 27 additions & 0 deletions src/Microsoft.Identity.Web.Analyzers/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project>
<!-- This file prevents inheriting properties from the parent Directory.Build.props -->
<PropertyGroup>
<!--This should be passed from the VSTS build-->
<MicrosoftIdentityWebVersion Condition="'$(MicrosoftIdentityWebVersion)' == ''">3.14.1</MicrosoftIdentityWebVersion>
<!--This will generate AssemblyVersion, AssemblyFileVersion and AssemblyInformationVersion-->
<Version>$(MicrosoftIdentityWebVersion)</Version>

<RepositoryType>git</RepositoryType>
<Authors>Microsoft</Authors>
<Company>Microsoft Corporation</Company>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/AzureAD/microsoft-identity-web</PackageProjectUrl>
<RepositoryUrl>https://github.com/AzureAD/microsoft-identity-web</RepositoryUrl>
<PackageReleaseNotes>The release notes are available at https://github.com/AzureAD/microsoft-identity-web/releases and the roadmap at https://github.com/AzureAD/microsoft-identity-web/wiki#roadmap </PackageReleaseNotes>
<PackageTags>Microsoft Identity Web;Roslyn;Analyzer;Code Fixer;Migration;v4</PackageTags>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
</PropertyGroup>

<ItemGroup>
<None Include="../../LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Microsoft.Identity.Web.Analyzers;

/// <summary>
/// Analyzer that detects usage of obsolete IDownstreamWebApi interface.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class IDownstreamWebApiAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Rule = new(
id: DiagnosticIds.IDownstreamWebApiObsolete,
title: "IDownstreamWebApi is obsolete",
messageFormat: "IDownstreamWebApi is obsolete. Use IDownstreamApi instead. See https://aka.ms/id-web-downstream-api-v2 for migration details.",
category: "Usage",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "IDownstreamWebApi has been replaced by IDownstreamApi from Microsoft.Identity.Abstractions. Update your code to use the new interface.");

/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();

context.RegisterSyntaxNodeAction(AnalyzeIdentifierName, SyntaxKind.IdentifierName);
}

private static void AnalyzeIdentifierName(SyntaxNodeAnalysisContext context)
{
var identifierName = (IdentifierNameSyntax)context.Node;

var symbolInfo = context.SemanticModel.GetSymbolInfo(identifierName, context.CancellationToken);

if (symbolInfo.Symbol is INamedTypeSymbol typeSymbol && IsIDownstreamWebApi(typeSymbol))
{
var diagnostic = Diagnostic.Create(Rule, identifierName.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}

private static bool IsIDownstreamWebApi(ITypeSymbol typeSymbol)
{
return typeSymbol.Name == "IDownstreamWebApi" &&
typeSymbol.ContainingNamespace?.ToDisplayString() == "Microsoft.Identity.Web";
}
}
190 changes: 190 additions & 0 deletions src/Microsoft.Identity.Web.Analyzers/IMPLEMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Microsoft.Identity.Web.Analyzers - Implementation Summary

## Overview

This document provides a summary of the Roslyn Analyzer package implementation for Microsoft.Identity.Web v4 migration support.

## What Was Implemented

### 1. Analyzer Project Structure

**Location**: `src/Microsoft.Identity.Web.Analyzers/`

- **Microsoft.Identity.Web.Analyzers.csproj**: Analyzer project configured for NuGet packaging
- Targets netstandard2.0 for broad compatibility
- Configured as a development dependency
- Properly packages analyzer DLL into `analyzers/dotnet/cs` directory
- Includes README.md in the package

- **Directory.Build.props**: Isolated build configuration
- Prevents inheritance of incompatible settings from parent
- Configures package metadata (version, license, tags, etc.)

### 2. Diagnostic Analyzers

Four diagnostic analyzers were implemented to detect obsolete v3.x APIs:

#### IDW4001: TokenAcquirerTokenCredential
- **File**: `TokenAcquirerTokenCredentialAnalyzer.cs`
- **Purpose**: Detects usage of `TokenAcquirerTokenCredential` class
- **Message**: "TokenAcquirerTokenCredential is obsolete. Use MicrosoftIdentityTokenCredential instead."
- **Detection**: Object creation and type references

#### IDW4002: TokenAcquirerAppTokenCredential
- **File**: `TokenAcquirerAppTokenCredentialAnalyzer.cs`
- **Purpose**: Detects usage of `TokenAcquirerAppTokenCredential` class
- **Message**: "TokenAcquirerAppTokenCredential is obsolete. Use MicrosoftIdentityTokenCredential instead."
- **Detection**: Object creation and type references

#### IDW4003: AddDownstreamWebApi
- **File**: `AddDownstreamWebApiAnalyzer.cs`
- **Purpose**: Detects calls to `AddDownstreamWebApi` extension method
- **Message**: "AddDownstreamWebApi is obsolete. Use AddDownstreamApi instead."
- **Detection**: Method invocations

#### IDW4004: IDownstreamWebApi
- **File**: `IDownstreamWebApiAnalyzer.cs`
- **Purpose**: Detects usage of `IDownstreamWebApi` interface
- **Message**: "IDownstreamWebApi is obsolete. Use IDownstreamApi instead."
- **Detection**: Interface type references

### 3. Supporting Files

- **DiagnosticIds.cs**: Centralized diagnostic ID constants
- **AnalyzerReleases.Shipped.md**: Tracks shipped analyzer releases (currently empty)
- **AnalyzerReleases.Unshipped.md**: Documents unreleased diagnostic rules
- **README.md**: Comprehensive documentation with examples and migration guidance

### 4. Test Project

**Location**: `tests/Microsoft.Identity.Web.Analyzers.Test/`

- **Microsoft.Identity.Web.Analyzers.Test.csproj**: xUnit test project
- Targets net8.0
- References Microsoft.CodeAnalysis.CSharp.Workspaces for testing

- **Test Files**:
- `AnalyzerTestBase.cs`: Base class with helper methods for analyzer testing
- `TokenAcquirerTokenCredentialAnalyzerTests.cs`: 3 tests
- `TokenAcquirerAppTokenCredentialAnalyzerTests.cs`: 2 tests
- `AddDownstreamWebApiAnalyzerTests.cs`: 2 tests
- `IDownstreamWebApiAnalyzerTests.cs`: 2 tests

**Total**: 9 tests, all passing

### 5. Solution Integration

- Added `Microsoft.Identity.Web.Analyzers.csproj` to `Microsoft.Identity.Web.sln`
- Added `Microsoft.Identity.Web.Analyzers.Test.csproj` to `Microsoft.Identity.Web.sln`

## Technical Decisions

### 1. No Assembly Signing
- Analyzer project is **not** strong-named signed
- Rationale: Analyzers are development-time only dependencies and don't require signing
- Test project also unsigned to avoid compatibility issues

### 2. Isolated Build Configuration
- Created separate `Directory.Build.props` in analyzer folder
- Prevents inheritance of incompatible settings (signing, multiple target frameworks, etc.)
- Maintains compatibility with Roslyn analyzer requirements

### 3. netstandard2.0 Target
- Chosen for maximum compatibility across .NET Framework, .NET Core, and .NET versions
- Required by Roslyn analyzer infrastructure

### 4. Test Approach
- Tests define types inline to avoid dependency on actual obsolete types
- Uses Roslyn's semantic model for accurate testing
- Covers both positive (detection) and negative (no false positives) cases

## Package Contents

The NuGet package (`Microsoft.Identity.Web.Analyzers.3.14.1.nupkg`) includes:

1. **Microsoft.Identity.Web.Analyzers.dll** - The analyzer assembly
- Packaged in `analyzers/dotnet/cs/` directory (standard location)

2. **README.md** - Documentation with:
- Overview of each diagnostic
- Migration examples for each scenario
- Installation instructions
- Links to additional resources

3. **LICENSE** - MIT license file

## Usage

Developers can install the analyzer via:

```bash
dotnet add package Microsoft.Identity.Web.Analyzers
```

Or in the project file:

```xml
<ItemGroup>
<PackageReference Include="Microsoft.Identity.Web.Analyzers" Version="3.14.1" PrivateAssets="all" />
</ItemGroup>
```

The analyzers will automatically run during builds and provide warnings for obsolete API usage.

## Future Enhancements (Not Implemented)

The following were identified as optional enhancements but not implemented in this initial version:

1. **Code Fixers**: Automated code fixes to replace obsolete APIs
- Would require more complex code transformations
- May need additional configuration options

2. **Additional Diagnostics**: More v4 breaking changes
- Legacy generic helpers (e.g., PostForUserAsync)
- Sync API replacements
- Configuration modernization

3. **Sample Project**: Demonstration project showing analyzer in action
- Would help users understand analyzer behavior
- Could be added to DevApps folder

## Testing Results

All 9 unit tests pass successfully:

```
Passed! - Failed: 0, Passed: 9, Skipped: 0, Total: 9
```

Tests cover:
- Detection of obsolete type usage
- Detection of obsolete method calls
- No false positives for unrelated code
- Multiple references to the same obsolete type

## Build Verification

- ✅ Analyzer project builds without warnings or errors
- ✅ Test project builds without warnings or errors
- ✅ All tests pass
- ✅ NuGet package creates successfully
- ✅ Package contains expected files

## Integration with CI/CD

The analyzer and test projects are:
- Added to the main solution (`Microsoft.Identity.Web.sln`)
- Will be built as part of the standard build process
- Tests will run as part of the test suite

## Documentation

Comprehensive documentation is provided in:
- `src/Microsoft.Identity.Web.Analyzers/README.md` - User-facing documentation
- This file - Implementation details and design decisions

## Conclusion

The Microsoft.Identity.Web.Analyzers package is fully functional and ready for use. It provides valuable migration assistance for developers moving from v3.x to v4.0.0 by detecting obsolete API usage at compile time and providing clear guidance on replacements.

The implementation follows Roslyn analyzer best practices and integrates seamlessly with the existing Microsoft.Identity.Web solution structure.
Loading
Loading