Web Api Bearer Token Authentication

Bu makalemde front-end ve back-end arasında web api bearer token  authentication kullanımından bahsetmek istiyorum. İlk olarak nedir bu token diyerek başlayıp avantajlarından bahsettikten sonra örnek bir ASP .NET Web Api Üzerinden uygulama ile devam edeceğim.

 

Nedir Bu Token ve Avantajları ?

Kısaca özetlemek gerekirse token’ı anahtar olarak düşünebiliriz. Yani biz back-end serverımıza kullanıcı adımızı ve şifremizi doğrulamak için gönderdiğimizde bize bu doğrulamaya karşılık bir token ( Anahtar ) veriyor. Sonrasında istek gerçekleştirmek istediğimizde bizden kullanıcı adı şifre yerine bu anahtarı bekliyor ve gerekli güvenlik kontrollerini sağlıyor. Burada böyle bir yapının kullanılmasının bize sağladığı avantajlardan başlıcaları;

  • Mobil uygulamalar ile entegrasyonu
  • Farklı uygulamalar ile doğrulamanın paylaşılabilmesi
  • Durumsuz olması  ( Stateless ) ve sonucunda daha kolay ölçeklenebilir olması
  • Front-end ile back-end arasında gevşek (loosely coupled) bir yapı sağlaması

Uygulama İle Devam Edelim…

Örnek uygulamamızı oluştururken iki tane projeye ihtiyacımız olacak. Bir tanesi boş web-api projemiz ikincisi ise boş bir web projesi olacak.

Web Api Bearer Token  Authentication Projesi

Boş web-api projemizi “OrnekWebApi” şeklinde adlandırıp başlayalım.Projemize kullanacağımız paket eklentilerini yükleyelim. Bunlar için;

Install-Package Microsoft.AspNet.WebApi.Owin

Install-Package Microsoft.Owin.Security.OAuth

Install-Package Microsoft.Owin.Cors

Install-Package Microsoft.Owin.Host.SystemWeb

Paket yüklemelerini gerçekleştirdikten sonra aşağıda şekilde görüldüğü gibi bir klasör yapısı oluşturup içerine “Saglayici” ve “Startup” klasımızı ekleyelim.

projeyapi

Startup klasımız içine aşağıdaki kodları yapıştıralım.

using System;
using System.Web.Http;
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Owin;


[assembly: OwinStartup(typeof(OrnekWebApi.Authentication.Startup))]
namespace OrnekWebApi.Authentication
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration config = new HttpConfiguration();

            ConfigureOAuth(app);            
            WebApiConfig.Register(config);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            app.UseWebApi(config);
        }

        private void ConfigureOAuth(IAppBuilder app)
        {
            OAuthAuthorizationServerOptions oAuthAuthorizationServerOptions = new OAuthAuthorizationServerOptions()
            {
                TokenEndpointPath = new Microsoft.Owin.PathString("/token"), // token adresi
                AccessTokenExpireTimeSpan = TimeSpan.FromHours(10),//10 Saat geçerli
                AllowInsecureHttp = true,
                Provider = new Saglayici() // Burada hata alırsanızz saglayıcı klasınızız doğru ayarladığınızdan emin olun.
            };

            
            app.UseOAuthAuthorizationServer(oAuthAuthorizationServerOptions); // Ayarladığımız config dosyasının server'a kullanması için gönderiyoruz.
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());// Bearer Authentication'ı kullanacağımızı belirttik.
        }
    }
}

 

Burada yukarıda tanımladığımız assembly ile serverımız ilk çalıştığında çalıştırılacak klasımızı tanımladık.  Configuration kısmında yer alan parametre ise bu çağrılma sırasında host tarafından sağlanmakta. İkinci olarak HttpConfiguration ayarlarımızı yaptıktan sonra App_Start’da yer alan Register metodumuzu bu configrasyon ile çağıracağız.Bu işlemin ardından projemizde yer alan Global.asax.cs dosyamızın bulunmasına gerek kalmamaktadır. Çünkü startup işlemini artık buradan sağlıyoruz. OAuth server ayarlarında tanımladığımız özelliklerden bahsedecek olursak

TokenEndpointPath ile geçerli token alacağımız adresi belirtiyoruz.

AccessTokenExpireTimeSpan ise sağlanan token’ın geçerlilik süresini belirtir.

AllowInsecureHttp ise token requestlerin HTTPS olmayan protokollere açılmasını sağlar.

Şimdi ikinci klasımız olan sağlayıcı klasımız için kodları paylaşalım.

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Owin.Security.OAuth;
namespace OrnekWebApi.Authentication
{
    public class Saglayici : OAuthAuthorizationServerProvider
    {
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
//Burada client validation kullanmadık. İstersek custom client tipleri ile client tipine görede validation sağlayabiliriz.
            context.Validated();
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {

            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); // Farklı domainlerden istek sorunu yaşamamak için

            //Burada kendi authentication yöntemimizi belirleyebiliriz.Veritabanı bağlantısı vs...
            if (context.UserName == "Sinan" && context.Password == "Bir")
            {
                var identity = new ClaimsIdentity(context.Options.AuthenticationType);

                identity.AddClaim(new Claim("name", context.UserName));
                identity.AddClaim(new Claim("yetki", "Admin"));

                context.Validated(identity);
            }
            else
            {
                context.SetError("Geçersiz istek", "Hatalı kullanıcı bilgisi");
            }

            

        }
    }
}

 

Şimdi son olarak Authentication’ı test edebileceğimiz bir controller oluşturup projenin web-api kısmını tamamlamak.”Fatura” isminde boş bir web-api 2 controller oluşturalım.

using System.Collections.Generic;
using System.Web.Http;


namespace OrnekWebApi.Controllers
{
    public class FaturaController : ApiController
    {
        [HttpGet]
        [Authorize]
        public IEnumerable<string> Get()
        {
            var firmaList = new List<string>()
            {
                "Firma 1",
                "Firma 2",
                "Firma 3",
            };
            return firmaList;
        }   
    }
}

Burada [Authorize] attribute’i ile get metodumuzun yetkilendirme gerektireceğini belirttik. Buraya kadarki bölümde web-api projemizi tamamladık. Şimdi projemizi test etmek için boş bir web projesi açıp yapılandırmalara devam edelim.

 

Web Projemiz

Boş bir web-mvc projesi açalım. Projemize birtek jquery yüklememiz yeterli.  Ardından aşağıda vereceğim java script kodunu ekleyin.

$(document).ready(function() {
    $("#Login").click(function() {
        var userName = $("#UserName").val();
        var password = $("#Password").val();
        
        $.ajax({
            url: "http://localhost:11050/token",
            type: "POST",
            crossDomain: true,
            data: {
                "username": userName,
                "password": password,
                "grant_type": "password"
            },
            dataType: "json",
            success: function (result) {
                alert("Logged in successfully");
                localStorage.setItem('Token', JSON.stringify(result));
            },
            error: function (xhr, status, error) {
                alert(status+" " +error);
            }
        });
        
    });
    $("#Logout").click(function () {
        if (localStorage.getItem('Token')==null) {
            alert("You already logged out");
            return;
        }
        alert("Logged out successfully");
        localStorage.removeItem('Token');
    });
    $("#Get").click(function () {
        var token = $.parseJSON(localStorage.getItem('Token'));
        if (token == null) {
            alert("Unauthorized Request");
            return;
        }
        var accessToken = token.access_token;
        
       
        $.ajax({
            url: "http://localhost:11050/api/Fatura",
            type: "Get",
            crossDomain: true,
            dataType: "json",
            headers: {
                "accept": "application/json",
                "content-type": "application/json",
                "authorization": "Bearer "+ accessToken
                },
            success: function (result) {
                alert(JSON.stringify(result));
            },
            error: function (xhr, status, error) {
                alert(status + " " + error);
            }
        });
    });
});

 

Burada serverdan dönen token’ı local storage kaydettim. Siz farklı bir yapıda kullanabilirsiniz. Index sayfası için kodları paylaşıyorum.

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width"/>
    <script src="~/Scripts/jquery-2.1.4.min.js"></script>
    <script src="~/App/App.js"></script>
    <title>Index</title>
</head>
<body>
<input id="UserName" type="text" value="Sinan"/>
<input id="Password" type="password" value="Bir"/>
<input id="Login" type="button" value="Login"/>
<input id="Logout" type="button" value="Logout" />
<input id="Get" type="button" value="Get Data" />

</body>
</html>

 

 

Giriş yaptığınızda sonuç aşağıdaki gibi olacaktır.

Web Api Bearer Token Authentication

Veri isteği yaptığımızdaki sonuç

getdata

Proje dosyaları github’da mevcuttur.

 

3 Comments

  1. Çok Teşekkürler çok güzel bir makale ve anlatım.

  2. merhabalar şöyle bir sorum olacak benim bir web api projem var api crud işlemlerini yaptım daha sonra web api projemi jwt ile yetkilendirdim. Fakat asıl problemim şu ki ben web projemde httpclient kullanarak api tarafında jwt ile koruduğum api controllerıma bir httpclient ile get isteği atıp verilerimi asp net core mvc uygulamama çekmeye çalışırken token gönderemediğimden hata veriyor ben httpclient ile çekmeye çalışırken token bilgisini nasıl gönderebilirim ?

    • Merhabalar,

      MVC tarafinda httpclient ile istek atarken headers olarak “Authorize: Bearer ” şeklinde eklediğinizde, api tarafı schema olarak Jwt Bearer ile uyumlu olarak kurulduysa çalışması gerekir. OpenId/OAuth2 ile entegre ederseniz mvc tarafında session based authentication ile ilerlemek daha kolay olabilir.
      Yoksa HttpClient oluştururken factory uzerinden tokenları çeken ve refresh eden sınıfları sizin yazmanız gerekecektir.
      using IHost host = Host.CreateDefaultBuilder(args)
      .ConfigureServices((context, services) =>
      {
      string? httpClientName = context.Configuration[“TodoHttpClientName”];
      services.AddHttpClient(
      httpClientName ?? “”,
      client =>
      {
      // Set the base address of the named client.
      client.BaseAddress = new Uri(“https://jsonplaceholder.typicode.com/”);

      // Add a user-agent default request header.
      client.DefaultRequestHeaders.UserAgent.ParseAdd(“dotnet-docs”);
      });
      services.AddTransient();
      })

Leave a Reply

Your email address will not be published. Required fields are marked *