Giriş
Hepinize Merhabalar👋
Bu yazımızda discord, google, facebook ve diğer sosyal platformdaki hesapların şifrelerini istemeden, bize sunulan oauth servisleri ile kullanıcı bilgilerini çekeceğiz.
Ben bu yazıda sadece google'i anlatmaya çalışacağım.
Çok fazla detaya inmeyeceğim, dilerseniz kaynakça kısmından derine inebilirsiniz.
Direkt olarak projeyi github üzerinden incelemek için buraya, test etmek içinde buraya
tıklayabilirsiniz.
Kullanacağımız Kütüphane Ve Servisler
Database seçimi:
- mongodb
Not: Eğer mongodb uzak sunucu bağlantısını nasıl alacağınızı bilmiyorsanız, buraya tıklayarak ilgili yazıya ulaşabilirsiniz.
Google Oauth Servislerinin Aktif Edilmesi
- Google Console Cloud'a giriş yapıyoruz.(Buraya tıklayarak hızlıca gidebilirsiniz.)
- Proje seçimini yapıyoruz.
- Sol menüden
Credentials
'a ardından,Create Credentials
'a hemen ardından iseOAuth client ID
butonlarına tıklıyoruz.
- Gerekli ayarları yaptıktan sonra,
Authorized redirect URIs
kısmına yetkilendirmek istediğimiz linkleri yazıyoruz. Burası en önemli kısımlardan birisi olduğu için dikkat ediniz :)
- Ve :tada: API key ve secret keylerimiz hazır. Bunlar bize ileride lazım olacağı için sağa sola bir kenara kaydedelim.
- Ayarlamaları yapmaya devam edelim, soldaki menüden
OAuth consent screen
bölümüne geçelim.
- Burayı da geçtikten sonra, önümüze gelen paneldeki gerekli olan inputları dolduralım.
Scopes
kısmına geldiğimizde, kullanıcının hangi verilerini çekmek istediğimiz bilgisini isteyecek. Banaemail
ve bazıkişisel
bilgileri lazım olduğu için görseldeki gibi işaretliyorum.
- Hepsini tamamladıktan sonra, uygulamayı yayına alalım ve google cloud ile işimizi tamamen bitirelim!
Backendin kurulması
Yazıda da belirttiğim gibi bu projede nestjs
kullanacağım.
Yeni bir proje açıp ardından hemen konsola geçiyorum.
Ardından nest-cli
kurulu değilse onu kuruyorum.
npm install -g @nestjs/cli
CLI'i kullanarak yeni bir nest uygulaması yaratıyorum.
nest new .
Şimdi de passport-google-oauth20
kütüphanesini yükleyelim.
npm install --save @nestjs/passport passport passport-google-oauth20
npm install -D @types/passport-google-oauth20
Objeleri DTO'a çevirmeye işe yarayan plainToClass
fonksiyonunu kullanmak için class-transformer
kütüphanesini kuralım.
npm install class-transformer --save
Vee projemiz hazır..
Hemen ardından projeyi ayağa kaldırıyorum.
npm run start:dev
Bu ekranı görüyorsanız sıkıntı yok demektir, devam edebilirsiniz :)
Typeorm İle Mongodb'nin Entegre Edilmesi
Projeye typeorm ve mongodb paketlerini yüklüyorum.
npm install @nestjs/typeorm typeorm [email protected] --save
** [email protected] > üzeri sürümlerinde gerçekleşen büyük değişikliklerden dolayı, "Right-hand side of 'instanceof' is not an object" gibi oluşabilecek hataları önlemek için [email protected] sürümüne düşürüldü. **
//app.module.ts
...
import { TypeOrmModule } from '@nestjs/typeorm';
...
//app.module.ts
...
imports: [
TypeOrmModule.forRoot({
type: 'mongodb',
url: 'mongodb+srv://username:[email protected]/databasename?retryWrites=true&w=majority',
authSource: 'admin',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
ssl: true,
useUnifiedTopology: true,
useNewUrlParser: true,
synchronize: true,
logging: true,
}),
],
...
Kullanıcı Modülünün Oluşturulması
Terminalden kök dizine geliyoruz ve hemen ardından aşağıdaki kodu çalıştırıyoruz.
nest g resource user
//REST API [x]
//CRUD [x]
Yeni eklenen dosyalar;
Entity ve DTO'nun düzenlenmesi
Adı, soyadı ve email adresinden oluşan bir kullanıcı tablosu hazırlayacağım.
//user.entity.ts
import { Column, Entity, ObjectID, ObjectIdColumn } from 'typeorm';
@Entity('User')
export class User {
@ObjectIdColumn()
id: ObjectID;
@Column()
name: string;
@Column()
surname: string;
@Column({ unique: true })
email: string;
}
//create-user.dto
import { Exclude, Expose } from 'class-transformer';
@Exclude()
export class CreateUserDto {
@Expose()
public name: string;
@Expose()
public surname: string;
@Expose()
public email: string;
}
//user.module.ts
...
imports: [TypeOrmModule.forFeature([User])],
...
//user.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDto } from './dto/create-user.dto';
import { User } from './entities/user.entity';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User) private userRepository: Repository<User>,
) {}
async create(createUserDto: CreateUserDto) {
return await this.userRepository.create(createUserDto);
}
async findOne(data: object) {
return await this.userRepository.findOne(data);
}
}
//user/guards/google-auth-guard.ts
import { HttpException, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class GoogleAuthGuard extends AuthGuard('google') {
constructor() {
super();
}
handleRequest(err: any, user: any, info: any, context: any, status: any) {
if (err || !user) {
throw new HttpException(err.message, err.status);
}
return user;
}
}
//user/strategies/google.strategy.ts
import { Strategy, VerifyCallback } from 'passport-google-oauth20';
import { Injectable } from '@nestjs/common';
import { UserService } from '../user.service';
import { PassportStrategy } from '@nestjs/passport';
@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor(private userService: UserService) {
super({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_SECRET,
callbackURL: `http://localhost:3000/auth/google`,
scope: ['email', 'profile'],
});
}
async validate(
accessToken: string,
refreshToken: string,
profile: any,
done: VerifyCallback,
): Promise<any> {
const { name, emails } = profile;
const email = emails[0].value;
const newUser = plainToClass(CreateUserDto, {
email,
name: name.givenName,
surname: name.familyName,
});
const foundedUser = await this.userService.findOne({ email });
if (foundedUser) return done(null, foundedUser);
else {
const savedUser = await this.userService.create(newUser);
return done(null, savedUser);
}
}
}
clientId
: Yukarıda google'dan aldığımız clientId,
clientSecret
: Yukarıda google'dan aldığımız clientSecret,
callbackURL
: Authorized redirect URI kısmına tanımladığımız URL,
scoped
: Kullanıcının çekmek istediğimiz verileri..
Son oluşturduğumuz google.strategy.ts
dosyasını, user.module.ts
'e providers olarak dahil edelim.
//user.module.ts
...
providers: [UserService, GoogleStrategy],
...
Ve tüm işlemler tamamlandı!
Google Oauth Servisinin Kullanılması
Şimdi de bu servisi nasıl kullanabiliriz gelin ona bakalım.
localhost:3000/auth/google
adresine istek attığımızda, login değilsek direkt olarak google auth panel gelecektir ve otomatik olarak callbackUrl
'de tanımladığımız linke yönlenecektir. Tüm bu yönlendirme işlemlerini kullandığımız passport-google-oauth20
kütüphanesini bizim yerimize yapıyor.
callbackUrl
'e gelirken code
parametresiyle dönecektir. Tabi bunları kendi içinde yaptığı için uzaktan bakınca servisin nasıl çalıştığını anlamak bir hayli zorlaşıyor.
Gelin bu zorluğu biraz aşmaya çalışalım.
localhost:3000/auth/google
adresine istek attığımızda bizi hesap seçmemiz için başka bir linke yönlendiriyor. Bu yönlendiğimiz sayfayı manuel olarak kendimiz yazacak olsaydık;
https://accounts.google.com/o/oauth2/v2/auth?scope=https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email&response_type=code&redirect_uri=http://localhost:3000/auth/google&client_id=clientId
clientId
: Yukarıda google'dan aldığımız clientId.
Yukarıdaki linke query olarak geçirdiğimiz redirect_uri
'nin backend ve google cloud'da Authorized redirect URI
kısmıyla eşleşiyor olduğuna dikkat edelim.
Atılan bu istekte, önce hesap seçilecek ardında bizi redirect_uri
'e code
parametresini ekleyerek otomatik olarak yönlendirecektir ve direkt olarak code'i çözüp bilgileri database'e kaydedip, kaydettiği verileri tekrar bize dönecektir.
Son
Okuduğunuz için çok teşekkürler🚀
Kucak dolusu sevgilerle!
Meraklısı İçin Kaynakça


