인증 및 보안

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019 | TFS 2018

이 문서는 파이프라인 작업 확장 또는 서비스 엔드포인트 확장이 아닌 웹 확장에만 관련됩니다. 이러한 작업의 경우 Azure Service Bus 작업에 게시를 사용할 수 있습니다.

Azure DevOps 확장 SDK를 사용하여 확장 개발에 대한 최신 설명서를 확인하세요.

확장에서 REST API 호출

대부분의 확장에는 현재 사용자를 대신하여 Azure DevOps REST API를 호출해야 합니다.

  • 제공된 JavaScript REST clients를 사용하는 경우 인증이 자동으로 처리됩니다. 이러한 클라이언트는 자동으로 핵심 SDK에서 액세스 토큰을 요청하고 요청의 권한 부여 헤더에 설정합니다.

  • 제공된 클라이언트를 사용하지 않는 경우 에서 Core SDK 토큰을 요청하고 요청의 권한 부여 헤더에 설정해야 합니다.

    VSS.require(["VSS/Authentication/Services"],
        function (VSS_Auth_Service) {
            VSS.getAccessToken().then(function(token){
                // Format the auth header
                var authHeader = VSS_Auth_Service.authTokenManager.getAuthorizationHeader(token);
    
                // Add token as an Authorization header to your request
            });
        });
    

서비스에 대한 요청 인증

일반적인 시나리오는 확장에서 백 엔드 서비스를 호출하는 것입니다. 이러한 호출이 Azure DevOps에서 실행되는 확장에서 오는지 확인하고 현재 사용자(및 기타 컨텍스트 정보)의 신뢰성을 확인하려면 확장에서 특수한 유형의 토큰을 사용할 수 있습니다. 이 토큰에는 호출하는 사용자에 대한 정보와 확장에서 요청이 제공되었는지 확인할 수 있는 서명이 포함되어 있습니다.

확장의 키 가져오기

확장의 고유 키(확장이 게시될 때 생성됨)를 사용하여 확장에서 수행한 요청의 신뢰성을 확인할 수 있습니다.

이 키를 얻으려면 게시된 확장을 마우스 오른쪽 단추로 클릭하고 인증서를 선택합니다.

key

경고

확장의 범위 변경으로 인해 인증서가 변경됩니다. scope 변경하는 경우 새 확장 키가 필요합니다.

서비스에 제공할 토큰 생성

  1. Core SDK getAppToken 메서드는 해결될 때 확장 인증서로 서명된 토큰을 포함하는 프라미스를 반환합니다.

    VSS.getAppToken().then(function(token){
        // Add token to your request
    });
    
  2. 이 토큰을 쿼리 매개 변수 또는 요청 헤더로 서비스에 전달합니다.

토큰 구문 분석 및 유효성 검사

다음은 토큰 구문 분석 샘플입니다. 먼저 확장에 대한 비밀을 다운로드하고 저장합니다. 게시자 페이지에서 가져올 수 있습니다. 이 비밀은 애플리케이션에서 사용할 수 있어야 합니다.

.NET Framework

이 샘플을 컴파일하려면 1 참조를 추가해야 합니다.

  1. NuGet 패키지 관리자를 열고 System.IdentityModel.Tokens.Jwt에 대한 참조를 추가합니다. 이 샘플은 이 패키지의 버전 5.2.2로 빌드되었습니다.
using System.Collections.Generic;
using System.ServiceModel.Security.Tokens;
using Microsoft.IdentityModel.Tokens;

namespace TokenSample
{
	class Program
	{
		static void Main(string[] args)
		{
			string secret = ""; // Load your extension's secret
			string issuedToken = ""; // Token you are validating
				
			var validationParameters = new TokenValidationParameters()
			{
				IssuerSigningKey = new SymmetricSecurityKey(System.Text.UTF8Encoding.UTF8.GetBytes(secret)),
				ValidateIssuer = false,
				RequireSignedTokens = true,
				RequireExpirationTime = true,
				ValidateLifetime = true,
				ValidateAudience = false,
				ValidateActor = false
			};

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

.NET Core - WebAPI

이 샘플을 컴파일하려면 1 참조를 추가해야 합니다.

  1. NuGet 패키지 관리자를 열고 System.IdentityModel.Tokens.Jwt에 대한 참조를 추가합니다. 이 샘플은 이 패키지의 버전 5.1.4로 빌드되었습니다.

Startup.cs

using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;

namespace TokenSample.Core.API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            string _secret = "ey9asfasdmax..<the secret key downloaded from the Azure DevOps Services publisher page>.9faf7eh";
	    
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                    .AddJwtBearer((o) =>
                    {
                        o.TokenValidationParameters = new TokenValidationParameters()
                        {
                            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secret)),
                            ValidateIssuer = false,
                            ValidateAudience = false,
                            ValidateActor = false,
                            RequireSignedTokens = true,
                            RequireExpirationTime = true,
                            ValidateLifetime = true
                        };    
                    });
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseMvc();
            app.UseStaticFiles();
        }
    }
}

API 컨트롤러:

[Route("api/[controller]"), 
 Authorize()]
public class SampleLogicController : Controller
{
   // ...
}