Derby.js-frameworkin derby-auth-laajennuksella saa helposti lisättyä Facebook-kirjautumisen sovellukseensa. Sen ongelmana on vain, että käyttäjä ohjataan kirjautumisen jälkeen aina palvelun etusivulle. Joskus on tarvetta ohjata käyttäjä erilliselle tervetuliaissivulle. Tämä onnistuukin muokkaamalla oletusasetuksia hieman.

Facebook-asetukset

Tavallisesti derby-auth konfiguroidaan käyttämään Facebook-kirjautumista tällaisilla asetuksilla:

options =
  domain: 'http://localhost:3000'

strategies =
  facebook:
    strategy: require('passport-facebook').Strategy
    conf:
      clientID: 'xxx'
      clientSecret: 'yyyyyy'
      callbackURL: options.domain + '/auth/facebook/custom/callback'

Viimeisellä rivillä näkyy lisäämäni callbackURL-asetus, joka määrittelee, minne Facebook ohjaa käyttäjän onnistuneen kirjautumisen jälkeen. Koska ohjaus tapahtuu Facebookin puolella, tämän URLin täytyy olla absoluuttinen eli sisältää myös domain-osoite.

Tämän jälkeen sama osoite on vielä lisättävä Derby-sovelluksen Express-reitteihin näin:

expressApp.get '/auth/facebook/custom/callback',
  passport.authenticate('facebook', failureRedirect: '/'), (req, res) ->
    res.redirect '/welcome' # success redirect

Ylläoleva koodi tarkoittaa, että autentikoinnin epäonnistuessa käyttäjä ohjataan etusivulle, ja sen onnistuessa taas /welcome -sivulle. Tästä eteenpäin /welcome voi sitten olla ihan tavallinen Derby-sivu tai mikä tahansa osoite omalla sivustolla.

Tarkkasilmäinen saattaa huomata vielä yhden ongelman. Derby-auth käyttää nimittäin omaa versiotaan passport-moduulista, jonka se konfiguroi automaattisesti. Ylläolevassa koodissa viitataan niinikään passport-moduuliin, ja viittauksen on osoitettava sen samaan instanssiin. Sen vuoksi require-kutsu täytyy tehdä näin:

passport = require 'derby-auth/node_modules/passport'

En ole varma, löytyykö tähän siistimpikin ratkaisu, mutta noin se ainakin toimii.

Alkuperäiselle sivulle palaaminen

Yllä kuvailtu ratkaisu perustuu siihen, että käyttäjä ohjataan Facebook-kirjautumisen jälkeen aina tietylle vakiosivulle. Usein olisi kuitenkin parempi palata takaisin sille sivulle, josta kirjautuminen aloitettiin. Sama pätee uloskirjautumiseen.

Käytin hetken pohtiakseni, mikä olisi tähän kaikkein derbymäisin ratkaisu. Valitettavasti kauniit ratkaisut eivät toimineet:

  • Alkuperäisen sivun tallentaminen tietomalliin (model.set '_refURL'): Tämä tieto ei välity selaimelta palvelimelle.
  • Alkuperäisen sivun tallentaminen sessioon (model.session.refURL): Tämä tieto ei välity palvelimelta selaimelle.

Jäljelle jää perinteinen tapa välittää alkuperäinen osoite query-parametrinä Derby-palvelimelle, tallentaa se sessioon ja ohjata käyttäjä sitten edelleen Facebook-kirjautumiseen. Se onnistuu tekemällä ensin tällainen sivupohja:

{#if _loggedIn}
  <li><a x-bind="click: clickLogout" href="#" class="">Sign out</a></li>
{else}
  <li><a x-bind="click: clickLogin" href="#" class="">Sign in</a></li>
{/if}

Sitten lisätään tapahtumankäsittelijät kyseisille painikkeille:

# Login click event
module.exports.clickLogin = (event, element, next) ->
  app.history.push '/auth/facebook/custom/login?ref=' +
    encodeURIComponent(window.location.href)

# Logout click event
module.exports.clickLogout = (event, element, next) ->
  app.history.push '/auth/facebook/custom/logout?ref=' +
    encodeURIComponent(window.location.href)

Ja lopuksi vielä palvelinpuolen reitit:

expressApp.get '/auth/facebook/custom/login', (req, res) ->
  req.session.refURL = req.query.ref
  res.redirect '/auth/facebook'

expressApp.get '/auth/facebook/custom/logout', (req, res) ->
  req.session.userId = undefined
  req.logout()
  res.redirect req.query.ref or '/'

expressApp.get '/auth/facebook/custom/callback',
  passport.authenticate('facebook', failureRedirect: '/'), (req, res) ->
    res.redirect req.session.refURL or '/' # success redirect

Ylläolevan koodin toimintaperiaate on otettu derby-authin lähdekoodista. Omasta mielestäni on hiukan ongelmallista, että uloskirjautumisen yhteydessä userId vain unohdetaan kokonaan. Silloin derby-auth nimittäin luo joka kerta uuden käyttäjän MongoDB:n users-collectioniin seuraavalla sivulatauksella. Tähän pitäisi luultavasti ehdottaa jotakin parannusta.

Published 12.3.2013