Son zamanlarda twitter’ı çok sık kullanmaya başladım. Twitter, developer’lara kendi uygulamalarını geliştirmeleri için bazı API metodları sunuyor. Bu metodları kullanan basit bir uygulama yapmak istedim.
Twitter API ile neler yapılabilir diye düşünürken, aklıma basit bir uygulama geldi. Bu bir web uygulaması olacak, kullanıcı twitter hesabıyla uygulamaya giriş yapacak ve uygulama kullanıcının yerine, o kullanıcıymış gibi twitter API metodlarını çağıracak. Uygulama, kullanıcıya takip ettiği hesapların dünya haritası üzerinde nerelerde yer aldığını bir “heat map” ile gösterecek.
Öncelikle biraz güvenlikten bahsedelim. Authentication dediğimiz, bir nevi kimlik ispatı gibi düşünülebilir. Mesela polis sizi durdurup kimlik istediğinde, aslında yaptığınız şey authentication. Bir de authorization var, yetki kontrolü diye çevirebiliriz. Authentication olmadan, authorization’dan bahsetmek mümkün değil. Bir kullanıcı, önce kendi kimliğini ispat edecek (authentication), daha sonra gerektiğinde o kimliğe tanımlanmış yetki kontrolü (authorization) yapılabilir.
Twitter, API metodları için iki farklı authentication türüne sahip:
Twitter API metodlarına erişebilmek için, developer.twitter.com üzerinden kendimize bir uygulama (twitter application) oluşturuyoruz.
Uygulama oluşturunca, twitter bize consumer key ve consumer secret adında iki anahtar verecek. OAuth 1.0a akışını, bu anahtarlarla başlatacağız. Bunlar olmadan, Twitter API metodlarını çağırmamız mümkün değil.
Not: Birazdan token’lar havalarda uçuşacak, hangi token ne için kullanılıyor daha kolay takip edebilmek için token’lara “TOKEN_A” “TOKEN_B” gibi isimler vereceğim. Bunu yaparken, bize görünmeyen arka planda kullanılan token’ları isimsiz bırakıyorum.
OAuth bir kütüphane veya uygulama değil. OAuth bir protokol ve Twitter API metodlarının kullanımını OAuth protokolü ile emniyete almış.
OAuth 1.0a’yı kabaca tarif etmek gerekirse, uygulamanızın başka bir kullanıcı adına o kullanıcının üyesi olduğu servisi çağırmanızı sağlıyor. Burada uygulamamız twheat-map, servis dediğimiz şey ise Twitter. Mekanizma şöyle işliyor:
OAuth 1.0a akışı, sunucu tarafında gerçekleştirilmesi gerek bir iş. İstemci (client) tarafında gerçekleştirilmemeli çünkü bize özel anahtarlarımız kullanılacak ve bu anahtarların istemciler tarafından görülmemesi lazım, aksi takdirde isteyen bunları kullanarak bizim uygulamaymış gibi davranabilir.
Authentication kısmı karışık gelmiş olabilir, merak etmeyin çünkü OAuth işlemlerini Auth0‘a devredeceğiz. Eğer bunu yapmasaydık, sunucu tarafında kendi oauth implementasyonumuzu yapmak zorunda kalacaktık, gerek yok, yapılmışı var. Hem de Auth0 ile gayet şık bir login ekranımız bile hazır gelecek. Auth0 çok güzel ve kullanması inanılmaz kolay bir identity servisi sunuyor. Twitter entegrasyonu zaten mevcut, bu demek oluyor ki oauth ile ilgili hiç kod yazmadan “Twitter ile login” özelliğine sahip olacak uygulamamız.
Uygulamamız, bir sunucu (backend) ve bir istemci (client) olmak üzere iki parçadan oluşacak. Düşününce neden bir sunucuya ihtiyacımız var diyebilirsiniz, sebebi şu: Auth0 ile twitter üzerinden kullanıcının kimlik doğrulamasını yapabiliyoruz. Bu doğrulama sonucunda twitter Auth0’a bir access token (TOKEN_A) veriyor. Fakat Auth0, twitter’ın kullanıcıya verdiği access token’ı (TOKEN_A) bize doğrudan vermiyor. Bu token’a (TOKEN_A) erişebilmek için, Auth0’ın yönetim API metodunu çağırmamız gerekiyor. Bu metodu çağırabilmek için de Auth0’dan bir başka token (TOKEN_B) almamız gerekiyor, bu herkese açık olan bir API değil. Dolayısıyla, bu metodu sadece güvenli bir yerden yani sunucudan çağırmalıyız. Çünkü bu metodu çağırırken, Auth0’dan aldığımız “client id” ve “client secret” değerlerini kullanacağız. Eğer istemciden çağırsaydık, herkes bu çağrıyı nasıl yaptığımızı görüp, sanki biz çağırıyormuşuz gibi Auth0 metodlarını çağırıp uygulamamızın güvenliğini delebilirdi.
Bir diğer sebep de, henüz yapmamış da olsam, sunucu uygulamasında bir cache mekanizması yapmak istemem. Aynı kullanıcı için her seferinde gidip de arkadaş listesini çekmeye gerek yok, arkadaş listesini haftada bir güncellesem yeterli diye düşünüyorum. Bunu yapmanın tek yolu, sunucu tarafında kullanıcının arkadaş listesini bir veritabanına yazmak. Bu sayede Twitter API metodunu da sık sık çağırmamış olacağız, neticede onun da bir kotası ve ücreti var.
İstemciyi ReactJS ile yazıp, netlify üzerinden host etmeyi düşünüyorum. Benzer bir örnek için ilgili yazıma bakabilirsiniz.
İstemci, API authentication için JWT kullanacak, bunun için Auth0’ın geniş istemci kütüphanesinde kullanıma hazır kodlar mevcut. İstediğiniz platformu seçip, ilgili kod parçacığına erişebiliyorsunuz.
ReactJS de hazır kütüphaneleriyle destekledikleri platformlardan biri.
İstemci tarafında login akışı şöyle olacak:
Kullanıcımızın kimliğini doğruladığımıza göre, şimdi kendi API metodumuzu çağırıp kullanıcının arkadaşlarının hangi koordinatlarda yer aldığını bulabiliriz. API metodumuz bize şöyle bir liste dönecek:
Haritayı gösterebilmek için ilk başta Google Maps API metodlarını kullanmayı düşünmüştüm, fakat o kadar detaylı bir haritaya gerek olmadığından zoom özelliği olmayan statik bir dünya haritası ile işi kotarmaya karar verdim. Bunun için react-simple-maps diye çok kullanışlı ve şık haritalar çizen bir react komponent kütüphanesi buldum:
Sunucu tarafını nodejs ile yapmaya karar verdim. Önceleri Python ile yazılım geliştirmeyi tercih ediyordum fakat son zamanlarda javascript’e kaydım. Nedenine gelince, javascript kullanımı iyice yaygınlaştı ve örnek kod, SDK, kütüphane vb. yardımcı araç gereç bulmak çok daha kolay. Stackoverflow 2019 yazılımcı anketine göre, javascript %67.8 ile en popüler programlama dili. Yine aynı ankette, nodejs kullanımı %49.9 ile .Net’i bile geride bırakmış durumda.
Sunucuda bir API metodumuz olacak, bu metod parametre olarak twitter ile giriş yapan kullanıcı adına Auth0’ın ürettiği bir JWT token’ı (TOKEN_C) alacak ve güvenliğini bununla sağlayacak. Sadece twitter’la login olan kullanıcılar metodumuza erişebilecek. Metodumuz sırasıyla şunları yapacak:
Twitter’ın API metodundan gelen cevap şu şekilde:
Twitter’dan gelen cevapta, her kullanıcının bir de location bilgisi var. Twitter’daki lokasyon, Free-text bir alan, yani isteyen istediği şeyi yazabiliyor buraya. Tabi insanlar genelde kullanım amacına uygun olarak, bulundukları şehri, ülkeyi vs. yazıyorlar. Lokasyon bilgisini haritada gösterebilmek için koordinata çevirmemiz gerekiyor. Bu noktada basite kaçtım ve içinde yaklaşık 13.000 adet lokasyon/koordinat bilgisi olan ücretsiz bir data set buldum. Tüm her yeri kapsamasa da, pek çok ülkeyi ve şehri kapsıyor, işimizi fazlasıyla görecektir.
Sunucuyu herhangi bir fiziksel/sanal makina yönetmeden, tamamen serverless olarak AWS API Gateway ve Lambda ile gerçekleyeceğim. Serverless framework çok kullanışlı ve AWS üzerinde sunucusuz (serverless) uygulamalar geliştirme sürecini çok hızlandıran güzide bir framework. Eğer hala bir AWS hesabınız yoksa ve nasıl açılacağını merak ediyorsanız, ilgili yazımı okuyarak yaklaşık beş dakika içinde AWS hesabınızı açabilirsiniz.
AWS üzerinde host edilecek olan sunucu uygulamamız basit bir uygulama olup, iki adet Lambda metodundan oluşacak. Bunlardan biri, API Gateway ile sunacağımız asıl metodumuzu kullanmak isteyen istemcilerin authentication yani kimlik doğrulamasını yapacak olan metod. Auth0, API Gateway custom lambda authorizer’ları nasıl yazılır anlatan güzel bir doküman yazmış. Bir de tabi AWS’nin kendi dokümanı var.
İstemci, API Gateway üzerinde host edilecek olan /heatmap
metodumuzu çağırırken, HTTP header’ında aşağıda gördüğünüz Authorization: Bearer parametresiyle JWT token’ı (TOKEN_C) gönderecek.
API Gateway, header’daki bu token’ı (TOKEN_C) alıp authorization lambda’mızı çağıracak. Kimlik doğrulamasını yapacak olan lambda metodumuzun tek yaptığı şey gelen JWT token’ı (TOKEN_C) doğrulayıp, cevap olarak bir AWS IAM Policy dokümanı dönecek. API Gateway de bu dokümana bakarak istemciden gelen isteği ya kabul edecek ya da reddedecek. Kabul ettiği durumda ise asıl metodumuz olan ve Twitter API metodunu çağırıp bize koordinatları dönecek olan metodumuzu çağırıp sonucu istemciye dönecek. Anlayacağınız, API Gateway aslında bir proxy görevi görüyor, yani asıl işi yapan o değil. Elçilik görevi görüyor. API Gateway’i kullanarak lambda metodlarınızı RESTful bir API metoduna dönüştürebilirsiniz.
Kendi oauth sunucumuzu yapmamak için, Auth0’ın sunduğu SAAS identity hizmetini kullanıyoruz. Bunun için Auth0 üzerinde ücretsiz hesap oluşturdum. Bir “Single Page Web Application” ve bir de “Machine to Machine Application” oluşturdum. Bir adet API oluşturdum. Aslında buradaki API mantıksal bir şey, yani bir metod falan değil. Bunu kullanarak API Gateway için JWT token (TOKEN_C) üreteceğiz.
Auth0 üzerinde iki adet API’ımız var, biri kendi oluşturduğumuz ve API Gateway için JWT üretmek için kullandığımız, diğeri de Auth0’ın kendi oluşturduğu bir management API.
“Machine to Machine Application” oluşturmamızın sebebi, kendi sunucumuzun (backend) Auth0’ı çağırıp kullanıcının detaylarını çekebilmesi. Bunun için de ilgili uygulamaya Auth0 management API metodlarını çağırabilmesi için bazı yetkiler vermemiz gerekiyor:
Tabi bu uygulamamızın bir client id ve client secret’i olacak, bu ikiliyle birlikte backend uygulamamız Auth0 API metodlarını çağırabilecek:
Auth0 üzerinden machine-to-machine backend app’a yetki olarak “read:users” ve “read:user_idp_tokens” verdim. Bu sayede, artık backend aşağıda gördüğünüz management API metodunu çağırarak kullanıcıya Twitter tarafından verilen access token’a (TOKEN_A) erişebilecek.
Son olarak, henüz Auth0’ı Twitter’a bağlamadık. Auth0’ın bize Twitter entegrasyonu verebilmesi için, Twitter’dan aldığımız consumer api key ve consumer api secret anahtarlarını Auth0’a tanıtmamız lazım. Bunun için Auth0 yönetim konsolundan “Connections->Social” sayfasını açıp, Twitter ikonuna tıklayıp entegrasyonu başlatıyoruz. Twitter’dan aldığımız anahtarları, aşağıdaki gibi Auth0’a ekliyoruz:
Uygulamaya twheat-map.selcukcihan.com adresinden erişebilirsiniz. İstemci projesi github.com/selcukcihan/twheat-map üzerinde, sunucu projesi ise github.com/selcukcihan/twheat-map-backend üzerinde.
Uygulama için düşündüğüm bazı iyileştirmeler var:
Serinin ikinci yazısında, uygulamayı basit bir geliştirmeyle hızlandırıyorum.