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

noyjavascriptoyunyoutube

11 ay önce 3 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 11 ay önce

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

    {{commentLike101Count}} beğenme 13 saat ö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 3 saat ö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
  • Düşündüklerin nedir ?

    Abdurrahman Eker

    (1010 Eylül 11111001100)

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