Passport.js ile Local ve Google üzerinden Authenticate Edilmesi - NOY#9

noyjavascriptoyunyoutube

6 yıl önce 8 yorum

Merhabalar, bu bölümde ise oyuncumuzun giriş yapması için gerekli olan özellikleri ekleyeceğiz. Öncelikle kullanıcımızın veritabanımızdan şifresi ile giriş yapabilmesini sağlayacağız. Sonra ise google üzerinden oturum açabilmesini sağlayacağız. 

Bu tür yapıları çeşitli platformlar üzerinde kullanmak için passport.js kullanacağız. Bu modül sadece google için değil Facebook, Twitter, OpenID gibi sistemler için de imkanlar sağlıyor. Ancak şu an için biz Google kullancağız. İleride diğer platformlar için destek ekleyebiliriz. 

Passport.js ile ilgili bu makaleyi okumanızı tavsiye ederim.

Passport.js kurulumu

npm i passport --save

kurulumu yaptıktan sonra server/uses.js dosyasında şu değişiklikleri yapalım:

const http = require('http');
const passport = require('passport');
...
    app.use(bodyParser.json());
    app.use(cookieParser());

    app.use(passport.initialize()); // burada tanımladık
    app.use(passport.session()); // oturum üzerinde değişiklik yapabilmesini istedik
...

Geçen hafta sanırsam bazı değişiklikleri anlamamışım örneğin users modelinin isExist methodunda değişiklik yapmışız. Bu değişiklik isExist modelinin eğer password ekliyse kullanıcıyı parolası ile aramasını sağlıyormuş. Biz de şöyle değişiklikler yapalım api/models/users.js modelindeki isExist methodunda:

isExist(email, password) {
    ...

    query = query
    .select([ // ve select üzerinde değişiklikler yapıldı
        ["id", "users.id"],
        ["name", "RTRIM(users.name)"],
        ["email", "RTRIM(users.email)"]
    ])
    .exec();

    return new Promise((resolve, reject) => {
        ...
                if (res.rows.length > 0)
                    return resolve(res.rows[0]); // eskiden true dönüyordu şimdi kayıt
                return resolve(false);
        ...
    })
}

Bir de userPassword modelinde hatırlarsanız crypto yapan bir satır vardı bunu diğer yerlerde de kullanmak için crypto adında bir methoda çevridik. userPassword modeli de şöyle değişti:

const database = require('../../database');
const sm = require('sqlmaster');
const crypto = require('crypto');

class UserPassword {
    crypto(str) {
        return crypto.pbkdf2Sync(str, 'satranc', 100000, 64, 'sha512').toString('hex');
    }

    insert(id, password) {
        return new Promise((resolve, reject) => {
            if (!password) {
                reject(new Error('password is null'));
            }

            password = this.crypto(password); // burada çağrıldı
...

Şimdi passport'u kullanmak için controllers/auth.js dosyasını oluşturalım:

const passport = require('passport');

module.exports = (request, response, next) => {
    switch (request.query.type) {
        case 'user':
            // yazılacak
            break;
        case 'google':
            // yazılacak
            break;
        default:
            response.status(400);
            response.end();
            break;
    }
};

NOT: Her controller ve model'i oluşturduktan sonra kesinlikle controlles/index.js ve models/index.js dosyasında belirtmeyi unutmayın.

Bir de bunu /v1/auth?type=user şeklinde çağrılması için routes üzerinde belirtelim.

...

module.exports = {
    post: {
        ...
        '/v1/auth': auth,
    },
    get: {
        ...
        '/v1/auth': auth,
    },
...

Hem get'e hem de post'a ekledik çünkü google üzerinden oturum doğrulamak için POST kullanmak, yeni bir tarayıcı sekmesinde oturum açabilmeyi olanaksız kılıyor. window.open('/v1/auth?type=google') gibi düşünebilirsiniz.

Giriş yapmasını sağlayalım

Passport'u istenildiği gibi local veritabanından kontrol etmek için passport-local modülünü kullanacağız. Kurmak için:

npm install passport-local --save

Şimdi uses.js dosyasında kullanalım:

...
const passport = require('passport');
const LocalStrategy = require('passport-local');

// doğrulamayı bu model yaptığı için buraya aldık.
// MVC yapısını bozmuş olsada böyle olabileceğini düşündüm.
const {
    Users,
} = require('./api/models');

module.exports = (app) => {

    ...

    app.use(passport.initialize());
    app.use(passport.session());

    // Bunlar session kullandığımız için gerekli bkz: https://github.com/jaredhanson/passport#sessions
    passport.serializeUser(function(user, done) {
        done(null, user);
    });

    passport.deserializeUser(function(obj, done) {
        done(null, obj);
    });

    // burada bir Strategy kullanmasını sağlıyoruz
    passport.use(
        new LocalStrategy((username, password, done) => {
            // kullanıcıyı getiriyoruz
            Users.isExist(username, password).then(user => {
                // kullanıcı gelmişse done methodunu ona göre çağırıyoruz
                if (user !== false) return done(null, user);
                done(null, false);
            }, err => {
                done(err);
            })
        })
    );

    ...
}

Şimdi auth controller'ına gelen isteğin passport kullanmasını sağlayalım. Bunun için controllers/auth.js dosyasını şöyle düzenleyelim:

const passport = require('passport');

module.exports = (request, response, next) => {
    switch (request.query.type) {
        case 'user':
            passport.authenticate('local', (err, user) => {
                // uses'a yazdığımı done methodu burayı tetikliyor
                if (user !== false) {
                    request.session.auth = true;
                } else {
                    request.session.auth = false;
                }
                response.send({
                    auth: request.session.auth
                });
            })(request, response, next);
            break;
        case 'google':
            // Yapılacak
            break;
        default:
            response.status(400);
            response.end();
            break;
    }
};

Şimdi localhost:3000/v1/auth?type=user adresine username ve password değerleriyle istek attığımızda gelen cevabı görebiliriz.

Google üzerinden oturum açmak

Passport'u google üzerinden oturum açmasını sağlamak için passport-google-oauth2 modülünü kullanacağız (bkz: Google Auth 1.0'ı kullanımdan kaldırdı). Kurmak için:

npm i passport-google-oauth2 --save

Burada bizi bekleyen bir controller daha var çünkü google auth için yönlendirme üzerinden çalışıyor. Bundan önce biz uses.js üzerinde passport'a use edelim. Bunun için uses.js dosyasını düzenliyoruz:

...
const GoogleStrategy = require('passport-google-oauth20');
const config = require('./config');

const {
    Users,
} = require('./api/models');

module.exports = (app) => {

    ...

    passport.use(
        new GoogleStrategy({
            clientID: config.google.GOOGLE_CLIENT_ID,
            clientSecret: config.google.GOOGLE_CLIENT_SECRET,
            callbackURL: "/v1/auth/callback/google",  // burayı oluşturacağız
        }, (accessToken, refreshToken, profile, done) => {
            // burada o kişiye ait bilgiler geliyor ancak biz kaydetmiyoruz şu an için
            // yani burası yapılacak, test için verileri yazdırıyoruz
            console.log(JSON.stringify(profile));
            return done(null, accessToken);
        })
    );

    ...
}

Burada verdiğimiz config değerindeki veriler sizin uygulamanızda vermeniz gereken google değerleri. Bunları config.js dosyasına eklemelisiniz:

module.exports = {
    ...
    google: {
        GOOGLE_CLIENT_ID: "...",
        GOOGLE_CLIENT_SECRET: "...",
    }
}

Bu değerler size ait olmalı. Elde etmek için console.developers.google.com.

Şimdi, biraz önce belirttiğimiz callbackURL değerine yazdığımız router'ı oluşturalım. Bunun için api/controllers/callback.js diye bir dosya oluşturuyoruz:

module.exports = {
    success: (request, response) => {
        request.session.auth = true;
        response.redirect('/');
    },
    error: (request, response) => {
        request.session.auth = false;
        response.redirect('/');
    }
}

şimdi routes.js dosyasında değişiklikler yapalım:

const {
    ...
    callback,
} = require('./controllers');
...
const passport = require('passport');

module.exports = {
    ...
    get: {
        ...
        '/v1/auth/callback/error': callback.error,
        '/v1/auth/callback/google': [
            passport.authenticate('google', { failureRedirect: '/v1/auth/callback/error' }),
            callback.success,
        ]
    },

Dikkat ederseniz direkt bir method vermek yerine bir dizi verdik. express bize böyle bir imkan sağlıyor. Burada passport.authenticate('google') middleware işlevini görüyor. Bir de dikkat etmemiz gereken bir nokta da ana dizine (/) redirect yapmamız. Bu router'ı bulamadığı içi 404 verecektir. Biz ana dizin için bir boş sayfa açalım. Bunu da home controller'ı oluşturalım ve orada yapalım. controllers/home.js şöyle olsun

module.exports = (request, response) => {
    response.end('');
}

 routes.js üzerinde de belirtelim ve yukarı home controller'ını dahil etmeyi unutmayalım: 

...
    home,
} = require('./controllers');
...
module.exports = {
    ...
    get: {
        '/': home,
        ...
    },
    ...
}

şimdi http://localhost:3000/v1/auth?type=google adresine girdiğimizde ve giriş yaptığımızda ana dizine (/) yönlendirileceğiz. 

Bu gün anlatacaklarım bu kadardır. Diğer bölümde görüşmek üzere kendinize iyi bakın :)

Yorumlar ({{totalCommentCount}})

  • s-Jey

    {{commentLike74Count}} beğenme 6 yıl önce

    Güzel bi Makale olmuş helal olsun :) Böyle devamm :))
    Beğen Beğendin
  • Kaan

    {{commentLike101Count}} beğenme 5 yıl önce

    Ne güzel devam ediyorduk, neden kaldı ki böyle. Devam etmeyi düşünüyor musunuz. Teşekkürler.
    Beğen Beğendin
  • Abdurrahman Eker

    {{commentLike102Count}} beğenme 5 yıl önce

    Merhabalar, zamanında tam da 11 ay önce böyle sizin gibi yorum yazanlar olmadığı için seriye devam etmeyi bırakmıştım :) Şu an devam etmeyi düşünmüyorum. Devam etmem için belli bir sayıda kişinin takip etmesi lazım :/
    Beğen Beğendin
  • zübeyir

    {{commentLike107Count}} beğenme 5 yıl önce

    Hocam nodejs dururken neden java ee ye ihtiyac duyuyorlar ?
    Beğen Beğendin
  • Abdurrahman Eker

    {{commentLike108Count}} beğenme 5 yıl önce

    Merhaba Node.js veya Java EE kullanmak insanların ihtiyaçlarına ve süreçlerine bağlıdır. Performans açısından Node.js iyi, geliştirmesi de o kadar kolay ki projelerimi artık Node.js tabanlı yazıyorum. Bu konuda aydınlatıcı olması için bu cevabı okuyabilirsiniz: https://qr.ae/TW1CRr
    Beğen Beğendin
  • Kaan

    {{commentLike148Count}} beğenme 4 yıl önce

    Hocam selam, seri çok güzel gidiyor idi. Buraya kadar geldim devamını göremedim. Gerçekten harika idi. Lütfen devamını atar mısınız. Teşekkür ederim.
    Beğen Beğendin
  • Şafakvakti

    {{commentLike166Count}} beğenme 3 yıl önce

    Bu kadar yapıyı kurduktan sonra client tarafıyla projeyi taçlandırmamanız buruk bıraktı, Seriyi yorumlayacak olursam teorik bilgilerden nefret eden ben e göre oldukça akıcı şekilde şöyleyse böyle olur yazmanız kendime güvenimi arttırdı . İlginç olan şudur ki Her bir kütüphanenin bu seriden çok tutorialı vardır oysa 2 satırda görevini açıkladınız bu da projeyi gözümde daha değerli kıldı , emeğinize sağlık teşekkürler söyleyrceklerim bu kadar klavyeyi bırakamıyorum aloooooo
    Beğen Beğendin
  • Abdurrahman Eker

    {{commentLike167Count}} beğenme 3 yıl önce

    Ah be :) İyi yerden dem vurdun. Ben bu projede neler yapıcaktım neler! socket, stream, canlı yayında oynanan satranç turnuvalar, puantaj neler neler. Ancak kendi topluluğumu belirli bir seviyeye çıkarabilirsem o zaman yaparım gibi geliyor. Ama uğraşmak için değecek bir şey lazım. Güzel yorumun için teşekkür ederim :)
    Beğen Beğendin
  • Düşündüklerin nedir ?

    Abdurrahman Eker

    (1010 Eylül 11111001100)

  • Full Stack Developer Turkey/Sivas
  • İnternette Avare Kodcu
  • coffee
  • github
  • instagram
  • linkedin
  • youtube
  • Yeni içeriklerden haberdar olmak ister misin ?