Swagger UI'da Authorization Bilgilerinin Girilmesi

Swagger UI'da Authorization Bilgilerinin Girilmesi

ASP.NET Core'da oluşturuşmuş bir Web API'ın dökümantasyonu için Swagger ve Swagger UI kullandığınızda varsayılan olarak güvenlik ile ilgili HTTP header'larını arayüz üzerinden girememektesiniz.

Örnek vermek gerekirse geliştirmiş olduğunuz API güvenliğini sağlayabilmek için Client-Id ve Client-Secret isminde iki tane HTTP header'ından gelen bilgileri kontrol ettiğini varsayalım.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.Swagger;

namespace AspNetCore.SwaggerUI.Authorization
{
	public class Startup
	{
		public Startup(IConfiguration configuration)
		{
			Configuration = configuration;
		}

		public IConfiguration Configuration { get; }

		// This method gets called by the runtime. Use this method to add services to the container.
		public void ConfigureServices(IServiceCollection services)
		{
			services.AddMvc();

			services.AddSwaggerGen(c =>
			{
				c.SwaggerDoc("v1", new Info { Title = "Sample Web API", Version = "v1" });
			});
		}

		// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
		public void Configure(IApplicationBuilder app, IHostingEnvironment env)
		{
			if (env.IsDevelopment())
			{
				app.UseDeveloperExceptionPage();
			}

			app.UseSwagger();
			app.UseSwaggerUI(c =>
			{
				c.SwaggerEndpoint("/swagger/v1/swagger.json", "Sample Web API v1");
				c.RoutePrefix = "help";
			});

			app.Use(async (context, next) =>
			{
				const string ClientIdKey = "Client-Id";
				const string ClientSecretKey = "Client-Secret";

				if (!context.Request.Headers.ContainsKey(ClientIdKey) || !context.Request.Headers.ContainsKey(ClientSecretKey))
				{
					context.Response.StatusCode = 401;
					return;
				}

				const string clientId = "0D09B83A-D7D4-46D9-A2DD-D45FD3CFAFBA";
				const string clientSecret = "FB9A0A9C-C910-483A-B1D7-23234592D735";
				var headerClientId = context.Request.Headers[ClientIdKey];
				var headerSecretId = context.Request.Headers[ClientSecretKey];

				if (!clientId.Equals(headerClientId) && !clientSecret.Equals(headerSecretId))
				{
					context.Response.StatusCode = 401;
					return;
				}

				await next.Invoke();
			});

			app.UseMvc();
		}
	}
}

44 ve 67. satırlar arasında API'ı kullanan kullanıcıdan gelen HTTP header'larının içerisindeki değerleri kontrol edecek bir middleware yazdık. Kullanıcı eğer Client-Id veya Client-Secret bilgilerini göndermezse veya istediğimiz değerleri göndermezse HTTP 401 Unauthorized durum kodunu göndermekteyiz.

API'ımızı Swagger üzerinden test ettiğimizde de sunucudan bize HTTP 401 durum kodunun geldiğini görmekteyiz.

swagger-ui-1

SwaggerUI'ı kullanan kullanıcıdan gerekli güvenlik bilgilerini kullanabilmek için Swashbuckle'ın SecurityDefinition tipinden yararlanmamız gerekmektedir.

Gerekli tanımlamaları yapabilmek için Startup.cs dosyamızı aşağıdaki şekilde düzenleyelim.

using System.Collections.Concurrent;
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.Swagger;

namespace AspNetCore.SwaggerUI.Authorization
{
	public class Startup
	{
		public Startup(IConfiguration configuration)
		{
			Configuration = configuration;
		}

		public IConfiguration Configuration { get; }

		// This method gets called by the runtime. Use this method to add services to the container.
		public void ConfigureServices(IServiceCollection services)
		{
			services.AddMvc();

			services.AddSwaggerGen(c =>
			{
				c.SwaggerDoc("v1", new Info { Title = "Sample Web API", Version = "v1" });
			});
		}

		// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
		public void Configure(IApplicationBuilder app, IHostingEnvironment env)
		{
			if (env.IsDevelopment())
			{
				app.UseDeveloperExceptionPage();
			}

			app.UseSwagger(c =>
			{
				c.PreSerializeFilters.Add((document, httpRequest) =>
				{
					document.SecurityDefinitions = new ConcurrentDictionary<string, SecurityScheme>();

					document.SecurityDefinitions.Add("Client-Id", new ApiKeyScheme
					{
						Name = "Client-Id",
						Description = "Client Id",
						Type = "apiKey",
						In = "header"
					});
					document.SecurityDefinitions.Add("Client-Secret", new ApiKeyScheme
					{
						Name = "Client-Secret",
						Description = "Client Secret",
						Type = "apiKey",
						In = "header"
					});

					document.Security = new List<IDictionary<string, IEnumerable<string>>>
					{
						new Dictionary<string, IEnumerable<string>>
						{
							{"Client-Id", new List<string>()},
							{"Client-Secret", new List<string>()}
						}
					};
				});
			});

			app.UseSwaggerUI(c =>
			{
				c.SwaggerEndpoint("/swagger/v1/swagger.json", "Sample Web API v1");
				c.RoutePrefix = "help";
			});

			app.Use(async (context, next) =>
			{
				const string ClientIdKey = "Client-Id";
				const string ClientSecretKey = "Client-Secret";

				if (!context.Request.Headers.ContainsKey(ClientIdKey) || !context.Request.Headers.ContainsKey(ClientSecretKey))
				{
					context.Response.StatusCode = 401;
					return;
				}

				const string clientId = "0D09B83A-D7D4-46D9-A2DD-D45FD3CFAFBA";
				const string clientSecret = "FB9A0A9C-C910-483A-B1D7-23234592D735";
				var headerClientId = context.Request.Headers[ClientIdKey];
				var headerSecretId = context.Request.Headers[ClientSecretKey];

				if (!clientId.Equals(headerClientId) && !clientSecret.Equals(headerSecretId))
				{
					context.Response.StatusCode = 401;
					return;
				}

				await next.Invoke();
			});

			app.UseMvc();
		}
	}
}

41 ve 68. satırlar arasında HTTP header tanımlamalarını gerçekleştirdik. İlk önce Client-Id ve Client-Secret isminde Security Definition tanımlarını yaparak ilgili Dictionary'e ekledik. Sonrasındaysa 60. satırdaki tanımlamayla hangi Security Definiton'ların ekranda görünmesi gerektiğini belirledik.

İlgili değişiklikleri yaptıktan sonra API projemizi tekrar çalıştırıyoruz ve http://localhost:5000/help adresine giderek sayfanın sağ tarafında kalan Authorize düğmesine basıyoruz.

swagger-ui-2

Ekrana Client-Id ve Client-Secret bilgilerini girebileceğimiz bir pencere gelecektir. İlgili değerleri alana girdikten sonra ekrandaki Authorize düğmelerine basarak bilgilerin Swagger UI üzerinde saklanmasını sağlayalım.

swagger-ui-3

Eğer girmiş olduğumuz bilgileri değiştirmek istiyorsak Logout düğmesine basmamız yeterlidir. Bilgilerimizin doğruluğundan emin olduktan sonra Close düğmesine basarak pencereyi kapatabiliriz.

swagger-ui-4

Şimdi API'ımızın herhangi bir endpoint'ini Try it out düğmesine basarak test edelim. Girdiğimiz bilgiler doğru ise HTTP 200 durum kodunu ve endpoint'ten gelen cevabı görebileceğiz.

swagger-ui-5

Uygulamaya ait örnek kodlara https://github.com/mennan/aspnetcore-swaggerui-authorization adresinden ulaşabilirsiniz.