Merhaba,
Bu içeriğimizde, Angular ile geliştirdiğimiz uygulamalarda kullanıcıların sosyal profillerini kullanarak giriş yapmak istemeleri durumlarına istinaden Google hesabı üzerinden bir oturum açma çalışması sergiliyor olacağız. Tabi bu çalışmada, Angular kısmında @abacritt/angularx-social-login kütüphanesinden istifade edecek ve sosyal ağlardan gelen kayıt ve oturum bilgilerini tutabilmek ve yönetebilmek için backend’de Asp.NET Core Identity mekanizmasını kullanıyor olacağız.
Başlarken
Her şeyden önce hali hazırda temelleri atılmış Angular 14 ve Asp.NET Core Web API uygulamaları oluşturunuz ve özellikle Asp.NET Core uygulamasında Identity konfigürasyonlarını ve hatta JWT gibi authentication yapılanmalarını tamamlayınız.
Google Platformunda Credentials Oluşturma
İlk olarak oturum açma işlemini gerçekleştireceğimiz Google API platformu üzerinden bir kimlik bilgisi oluşturarak işe başlamamız gerekmektedir. Bunun için New Project – Google Cloud Console sayfası üzerinden uygulamanız için Google Cloud’da bir proje oluşturunuz.
Artık tekrardan ‘Credentials’ sekmesine gelip ‘Create Credentials’ butonu üzerinden ‘OAuth client ID’ sekmesine tıklarsanız artık bir ‘OAuth client ID’ tanımlama fırsatı elde edebileceksiniz. Evet, biliyorum. Google Cloud bu konuda oldukça zahmetli ve yorucu 🙂 Ama el mahkum, bu aşamaların bir şekilde geçilmesi gerekmektedir… Velhasıl, ‘OAuth client ID’ tanımlarken aşağıdaki görselde olduğu gibi adı ve yetkilendirilmiş javascript origins’leri bildirmemiz gerekmektedir.
Angularx-Social-Login Paketinin Yüklenmesi
Angular uygulamasında; Google login, Facebook login vs. gibi sosyal sitelerden yetkilendirme işlemleri söz konusu olduğu taktirde bizlere oldukça yardımı dokunacak olan angularx-social-login kütüphanesinden istifade edeceğimizi söylemiştik. Şimdi bu paketi npm i @abacritt/angularx-social-login talimatı eşliğinde Angular uygulamanıza yükleyiniz ve aşağıdaki konfigürasyonları gerçekleştiriniz(Not : İlgili paketi verilen talimat eşliğinde yükleyebilmek için Angular uygulamasının en düşük 14 versiyonunda olması gerekmektedir)
import { GoogleLoginProvider, SocialAuthServiceConfig, SocialLoginModule } from '@abacritt/angularx-social-login';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
SocialLoginModule
],
providers: [
{
provide: "SocialAuthServiceConfig",
useValue: {
autoLogin: false,
providers: [
{
id: GoogleLoginProvider.PROVIDER_ID,
provider: new GoogleLoginProvider("902986185803-4dl068flq4g27bpj299khhlq7es3g988.apps.googleusercontent.com")
}
],
onError: err => console.log(err)
} as SocialAuthServiceConfig
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
Dikkat ederseniz client id değeri burada provider olarak bildirilmektedir. Bu konfigürasyonlardan sonra ‘login.component.ts’ dosyasına geliniz ve aşağıdaki gibi ‘SocialAuthService’i çağırarak işlemleri gerçekleştiriniz.
import { SocialAuthService, SocialUser } from '@abacritt/angularx-social-login';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
constructor(private socialAuthService: SocialAuthService) {
this.socialAuthService.authState.subscribe((user: SocialUser) => {
console.log(user);
});
}
ngOnInit(): void {
}
}
Tabi, bir yandan da Google login butonunu çıkarabilmek için ise ‘login.component.html’ dosyasında aşağıdaki gibi ‘asl-google-signin-button’ direktifini çağırmanız gerekmektedir.
<asl-google-signin-button></asl-google-signin-button>
Tüm bu işlemlerden sonra ilgili butona tıkladığınızda aşağıdaki ekran görüntüsünde olduğu gibi Google açısından kullanıcı tarafından gerekli yetkilendirme sağlanması için bir onay penceresi çıkacaktır.
Angular’da Gerçekleştirilen Login’i Asp.NET Core Identity İle Backend’de de Gerçekleştirmek
Google’dan gelen idToken‘ı backend’de de doğrulamak için Google.Apis.Auth kütüphanesinden istifade ediyor olacağız. Haliyle ilgili kütüphaneyi verilen adres üzerinden projenize yükleyiniz ve aşağıdaki geliştirmeyi gerçekleştiriniz.
public class GoogleIdTokenValidationService : IGoogleIdTokenValidationService
{
readonly IConfiguration _configuration;
readonly UserManager<AppUser> _userManager;
readonly ITokenHandler _tokenHandler;
public GoogleIdTokenValidationService(
IConfiguration configuration,
UserManager<AppUser> userManager,
ITokenHandler tokenHandler)
{
_configuration = configuration;
_userManager = userManager;
_tokenHandler = tokenHandler;
}
public async Task<Token> ValidateIdTokenAsync(GoogleLoginVM model)
{
ValidationSettings? settings = new GoogleJsonWebSignature.ValidationSettings()
{
Audience = new List<string>()
{ _configuration["ExternalLogin:Google-Client-Id"] }
};
Payload payload = await GoogleJsonWebSignature.ValidateAsync(model.IdToken, settings);
UserLoginInfo userLoginInfo = new(model.Provider, payload.Subject, model.Provider);
AppUser user = await _userManager.FindByLoginAsync(userLoginInfo.LoginProvider, userLoginInfo.ProviderKey);
bool result = user != null;
if (user == null)
{
user = await _userManager.FindByEmailAsync(payload.Email);
if (user == null)
{
user = new() { Id = Guid.NewGuid().ToString(), Email = payload.Email, UserName = payload.Email, Provider = model.Provider };
IdentityResult createResult = await _userManager.CreateAsync(user);
result = createResult.Succeeded;
}
}
if (result)
await _userManager.AddLoginAsync(user, userLoginInfo);
else
throw new Exception("Invalid external authentication.");
Token token = _tokenHandler.CreateAccessToken(5);
return token;
}
}
Yukarıdaki kod bloğunu incelerseniz eğer; 18 ile 23. satır aralığında Google Client Id’yi(appsettings.json’dan geliyor) tutan bir ‘ValidationSettings’ nesnesi ayarlanmakta ve client’tan gelen ‘idToken’ bilgisi eşliğinde bu token’ın payload’ı ayrıştırılmaktadır. 25. satırda ise dış kaynaktan gelen kullanıcı bilgilerini ‘AspNetUserLogins’ tablosuna kaydetmemizi sağlayacak olan bir ‘UserLoginInfo’ nesnesi oluşturulmaktadır. 26. satırda FindByLoginAsync fonksiyonu ile ‘AspNetUserLogins’ tablosunda ‘UserLoginInfo’ nesnesindeki bilgilere karşılık bir kayıt olup olmadığı kontrol edilmekte ve kayıt varsa eğer giriş yaptırılmaktadır. Nihayetinde burada kullanıcı önceden aynı dış kaynaktan geldiyse uygulama tarafından tanınması ve direkt giriş yaptırılması gerekmektedir. 27 ile 37. satır aralığında ise dış kaynaktan gelen kullanıcının önceden gelmediğine dair ‘AspNetUserLogins’ tablosunda bir kaydın olmaması durumu göz önüne alınarak, önce bu kullanıcı email’inde bir kullanıcı olup olmadığı değerlendirilmekte, yoksa bu kullanıcının kaydı gerçekleştirilmektedir. 40. satırda ise AddLoginAsync fonksiyonu ile dış kaynaktan giriş yapan kullanıcının bilgileri eğer ‘AspNetUserLogins’ tablosunda yoksa işlenmektedir. 44 ile 45. satır aralığında ise artık Google’dan gelen kullanıcı doğrulandıysa yetkilendirmeyi sağlayacak manevra gerçekleştirilmektedir. Tabi biz burada JWT üretiyor ve gönderiyoruz ama sizler farklı bir yetkilendirme davranışı neticesinde client’a dönüş yapabilirsiniz.
Ayrıca 33. satıra bakarsanız gelen login talebinde eklenecek olan kullanıcının hangi provider eşliğinde eklendiğinin de kaydını tutmaktayız. Ne de olsa tüm kullanıcılar ‘AspNetUsers’ tablosunda tutulacaktırlar ve hangisinin nereden geldiğini bilebilmek için kullanıcı seviyesinde bu şekilde bir bilgi tutulması işimizi kolaylaştırabilir. Ha eğer ki sizler bu bilgiyi ‘AspNetUsers’ tablosunda tutmuyorsanız ‘AspNetUserLogins’ tablosunda hangi kullanıcının hangi provider ile geldiğini inner join eşliğinde rahatlıkla öğrenebilirsiniz.(Ki doğrusu bu ikincisidir, çünkü bu makale için görsel açıdan görebilelim diye provider bilgisini ‘AspNetUsers’ tablosunda tutuyorum ama bu durumda da her bir kullanıcı için veri tekrarı söz konusu olacak ve bu da normalizasyona aykırı bir durum ortaya koyacaktır!)
Bu geliştirmeden sonra misal olarak ‘UsersController’ isimli bir controller içerisinde ‘Login’ işlemi için aşağıdaki gibi istek karşılanabilir.
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
readonly IGoogleIdTokenValidationService _googleIdTokenValidationService;
public UsersController(IGoogleIdTokenValidationService googleIdTokenValidationService)
{
_googleIdTokenValidationService = googleIdTokenValidationService;
}
[HttpPost]
public async Task<IActionResult> Login(GoogleLoginVM model)
{
Token token = await _googleIdTokenValidationService.ValidateIdTokenAsync(model);
return Ok(token);
}
}
Backend’de ki bu geliştirmelerden sonra tek yapılması gereken Angular’da ‘SocialAuthService’in çağrıldığı yerde uygun endpoint’e ‘SocialUser’ türünde veriyi post isteği eşliğinde göndermektir.
import { SocialAuthService, SocialUser } from '@abacritt/angularx-social-login';
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http'
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
constructor(private socialAuthService: SocialAuthService, private httpClient: HttpClient) {
this.socialAuthService.authState.subscribe((user: SocialUser) => {
httpClient.post("https://localhost:7054/api/users/", user).subscribe(token => console.log(token))
});
}
ngOnInit(): void {
}
}
Test Edelim
Geliştirdiğimiz uygulamanın hem Angular hem de API kanatlarını ayağa kaldırıp test edersek eğer;
Nihai olarak,
Angular 14 ile Google hesabı üzerinden dış kaynak login işleminin backend’de Asp.NET Core Identity mekanizması eşliğinde nasıl yapılacağını incelemiş ve tatbik etmiş olduk. İçerik sürecinde geliştirdiğimiz uygulamaların bahsedilmeyen tüm detaylarını aşağıdaki github adreslerinden görebilir ve inceleyebilirsiniz.
İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…
AngularGoogleLoginExampleAPI : https://github.com/gncyyldz/AngularGoogleLoginExampleAPI
AngularGoogleLoginExampleClient: https://github.com/gncyyldz/AngularGoogleLoginExampleClient
