通过


对 Web 扩展进行身份验证和保护

Azure DevOps Services |Azure DevOps Server |Azure DevOps Server 2022

本文仅介绍 Web 扩展 的身份验证。 它不适用于管道任务扩展或服务终结点扩展。

小窍门

有关最新的扩展开发指南,包括主题设置以及从 VSS.SDK 进行迁移,请参阅 Azure DevOps 扩展 SDK 开发人员门户

从扩展调用 REST API

大多数扩展都代表当前用户调用 Azure DevOps REST API。

  • 使用 SDK REST 客户端:会自动处理身份验证。 客户端从 SDK 请求访问令牌并设置 Authorization 标头。

  • 使用自定义 HTTP 请求:从 SDK 请求令牌并自行设置标头:

    import * as SDK from "azure-devops-extension-sdk";
    
    SDK.init();
    
    SDK.ready().then(async () => {
        const token = await SDK.getAccessToken();
        const authHeader = `Bearer ${token}`;
    
        // Use authHeader in your fetch/XMLHttpRequest calls
    });
    

对您的服务请求进行身份验证

当扩展调用所控制的后端服务时,需要验证请求来自 Azure DevOps 中运行的扩展。 SDK 提供 getAppToken(),返回使用您扩展程序的证书签名的 JWT。 服务验证此令牌以对请求进行身份验证。

获取扩展程序的密钥

发布时会生成扩展的唯一密钥。 使用它来验证扩展令牌的真实性。

  1. 转到 扩展管理门户
  2. 右键单击 已发布的扩展 并选择 “证书”。

钥匙

警告

范围更改会导致证书更改。 修改范围后获取新密钥。

为您的服务生成令牌

getAppToken() 获取用您的扩展证书签名的 JWT,然后将该 JWT 传递给您的服务:

import * as SDK from "azure-devops-extension-sdk";

SDK.init();

SDK.ready().then(async () => {
    const token = await SDK.getAppToken();
    
    // Pass this token to your backend as a header or query parameter
    const response = await fetch("https://your-service.example.com/api/data", {
        headers: {
            "Authorization": `Bearer ${token}`
        }
    });
});

验证令牌

后端服务使用扩展的密钥验证 JWT。 以下示例演示如何实现验证。

重要

切勿在源代码中对扩展机密进行硬编码。 从环境变量、Azure Key Vault 或其他安全配置存储加载它。

.NET (控制台应用程序)

安装 NuGet 包:

dotnet add package System.IdentityModel.Tokens.Jwt

注释

使用版本 7.x 或更高版本。 版本 6.x 及更早版本已弃用。 有关详细信息,请参阅 IdentityModel 版本生命周期

using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;

string secret = Environment.GetEnvironmentVariable("EXTENSION_SECRET")
    ?? throw new InvalidOperationException("EXTENSION_SECRET not configured");
string issuedToken = ""; // Token from the extension request

var validationParameters = new TokenValidationParameters()
{
    IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(secret)),
    ValidateIssuer = false,
    ValidateAudience = false,
    ValidateActor = false,
    RequireSignedTokens = true,
    RequireExpirationTime = true,
    ValidateLifetime = true
};

var tokenHandler = new JwtSecurityTokenHandler();
var principal = tokenHandler.ValidateToken(issuedToken, validationParameters, out SecurityToken token);

ASP.NET 核心 Web API

安装 NuGet 包:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Program.cs

using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

string secret = builder.Configuration["ExtensionSecret"]
    ?? throw new InvalidOperationException("ExtensionSecret not configured");

builder.Services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters()
        {
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)),
            ValidateIssuer = false,
            ValidateAudience = false,
            ValidateActor = false,
            RequireSignedTokens = true,
            RequireExpirationTime = true,
            ValidateLifetime = true
        };
    });

var app = builder.Build();

app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();

app.Run();

API 控制器:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[Route("api/[controller]")]
[Authorize]
public class SampleLogicController : ControllerBase
{
   // Requests without a valid token return 401 Unauthorized
}