웹 확장 인증 및 보안

Azure DevOps 서비스 | Azure DevOps Server | Azure DevOps Server 2022

이 문서에서는 웹 확장에 대한 인증만 다룹니다 . 파이프라인 작업 확장 또는 서비스 엔드포인트 확장에는 적용되지 않습니다.

팁 (조언)

최신 확장 개발 지침, 테마 설정 및 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는 확장의 인증서로 서명된 JWT를 반환하는 기능을 제공합니다 getAppToken(). 서비스에서 이 토큰의 유효성을 검사하여 요청을 인증합니다.

확장의 키 가져오기

확장의 고유 키는 게시할 때 생성됩니다. 이를 사용하여 확장에서 토큰의 신뢰성을 확인합니다.

  1. 확장 관리 포털로 이동합니다.
  2. 게시된 확장을 마우스 오른쪽 단추로 클릭하고 인증서를 선택합니다.

key

경고

범위 변경으로 인해 인증서가 변경됩니다. 범위를 수정한 후 새 키를 가져옵니다.

서비스에 대한 토큰 생성

getAppToken()을(를) 사용하여 확장 기능의 인증서로 서명된 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 Core 웹 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
}