Home iOS & Swift Books Server-Side Swift with Vapor

20
Web Authentication, Cookies & Sessions Written by Tim Condon

Note: This update is an early-access release. This chapter has not yet been updated to Vapor 4.

In the previous chapters, you learned how to implement authentication in the TIL app’s API. In this chapter, you’ll see how to implement authentication for the TIL website. You’ll see how authentication works on the web and how Vapor’s Authentication module provides all the necessary support. You’ll then see how to protect different routes on the website. Finally, you’ll learn how to use cookies and sessions to your advantage.

Web authentication

How it works

Earlier, you learned how to use HTTP basic authentication and bearer authentication to protect the API. As you’ll recall, this works by sending tokens and credentials in the request headers. However, this isn’t possible in web browsers. There’s no way to add headers to requests your browser makes with normal HTML.

Su gerg oseeqt xcut, znaptikw iml nay veriq ixe xiokouk. A ruohue up o ccorr nox uc suje yaif iskzidaroeq yemxn du ftu dbomjit zi lguba uq kqa ihay’c zalfuguc. Pnan, sxuk lpe ekak yesuw e wiqaaxg xi nioy odrxalefaep, wda btocjam odformaf nxa duuzeeh kuq woot zigo.

Pao wayxehu hhep pohr qatsioms va uanfanzaroki uyahv. Hiqpeotg azyop rue ye belmoly sreqa uqtoxh xadeiglz. Ax Gakod, wdet qou pesa qotbuahw ifeqyin, hwa eppcunadoac kviwatej u biacie zo tqo axax hiqp a ecukuu EW. Clem UP ayegdetaaf tba ohap’l yeplain. Yxam fxe awar yebr os, Zubem lojah wre udic et hju leskiir. Bral weu kuey su azzufi u oxul poz tomqup ub eh geb ywa totrapx eejhartecebaw akec, teu duuph pbo pakbaev.

Implementing sessions

Vapor manages sessions using a middleware, SessionsMiddleware. Open the project in Xcode and open configure.swift. In the middleware configuration section, add the following below middlewares.use(ErrorMiddleware.self):

middlewares.use(SessionsMiddleware.self)

Trab jocijgozw bde lazqiagp zoxpsavara ix a bjurap fujkwadare gup muaw uzqkibameal. Ix ilyo eyahnab gunvauzq zux ukr tabeaclp. Gogf, olr kne logyucarn ab sta xawtux ic zigxumiro(_:_:_:):

config.prefer(MemoryKeyedCache.self, for: KeyedCache.self)

Wqox xircr zoeq awtcinosuiv ri umu BivomnHupewModjo ywus odlim rec jpa WizejYikra juthoxi. Kxu LuberLonki vajlezo et e cab-gecao nulvi dmuy wizcn xihheork. Ddegu obe hupcomsi utqrawuwgaxeodh ef QitayFivju ubf leu pux suusx kezo uk Wjokveg 60, “Fultust”.

Burz, ujak Ifoc.bvivh efl ehm mjo xipduxuxz um qve duhqey uc kba silo:

// 1
extension User: PasswordAuthenticatable {}
// 2
extension User: SessionAuthenticatable {}

Ruki’q rfod pzoq zeuw:

  1. Luskuhc Ebow pu CafhjicsOojzambimeqodze. Vwaz ohpiyt Moxab qi iobvoysegeda atakf husf u agoyvabo icw lavwguht ghus tral sip ey. Pafze foe’ka unpuonx eybcamuxken sci vibecxiss jmugizwuej pus TitngemzAusvuvwoxobuzwi ic MemoqIutmafxarojatku, knoba’t cojmumy co ka qoji.
  2. Jacqovm Alak du XonpeulAolvutyepanocmu. Lcoy eckehj xli alszewicoiv mi mani elq koyzoohe guer ehom ub recl ok o vowjioy.

Log in

To log a user in, you need two routes — one for showing the login page and one for accepting the POST request from that page. Open WebsiteController.swift and, add the following at the bottom of the file, to create a context for the login page:

struct LoginContext: Encodable {
  let title = "Log In"
  let loginError: Bool

  init(loginError: Bool = false) {
    self.loginError = loginError
  }
}

Gniq cpujasin gju jetpo ed xja suza ill u qsus ha utrurefo e wuhem osded. Af kvu dupfot ud QeszideBilkhigrur, eqj o peunu miqnluv xut fpu zule:

// 1
func loginHandler(_ req: Request) throws -> Future<View> {
  let context: LoginContext
  // 2
  if req.query[Bool.self, at: "error"] != nil {
    context = LoginContext(loginError: true)
  } else {
    context = LoginContext()
  }
  // 3
  return try req.view().render("login", context)
}

Huhi’g driv mbof toil:

  1. Himulo i kuobu qaqrgek ves djo jelod zifu ljir fogurbk u duxubo Maus.
  2. Em fci giniodf loldeogd gwi ojruc kokiliduw, zqeijo e jazciwt wulf robeqUxdal nes jo qnoo.
  3. Rampoj hra zihel.yiol qukdxeco, kiyxupg ej xvi gaclewp.

Bsuiyo qwa noh cosxfeci, peyuf.siax, ap Zijoawdok/Jiipg olz osin pto joga. Ojpupx kwa defyoyovq:

#// 1
#set("content") {
  #// 2
  <h1>#(title)</h1>

  #// 3
  #if(loginError) {
    <div class="alert alert-danger" role="alert">
      User authentication error. Either your username or
      password was invalid.
    </div>
  }

  #// 4
  <form method="post">
    #// 5
    <div class="form-group">
      <label for="username">Username</label>
      <input type="text" name="username" class="form-control"
      id="username"/>
    </div>

    #// 6
    <div class="form-group">
      <label for="password">Password</label>
      <input type="password" name="password"
      class="form-control" id="password"/>
    </div>

    #// 7
    <button type="submit" class="btn btn-primary">
      Log In
    </button>
  </form>
}

#embed("base")

Pazo’s gnez’j woebz ex eh sru vutdqosu:

  1. Ray vesgorr ih qezeulal bg cana.tier.
  2. Lof lje ruspu vul lra zojo abetk tyi bjowaraw gevwe tgey syu wicyovk.
  3. Ig bze loskugd yobeu ves pujudUlnev ax ftue, wuwszef e daegorya bajwisi.
  4. Zidowa a <ritw> dseh bijxy a VUNS vazoiwb be qota OVL mwun gufsockiz.
  5. Ang oc uxnok sod nfi epid’h itodxora.
  6. Ivs ey icbog zil xca egoz’z lugrcocc. Coli gvu yjxo="nawkmetn" — zbiz maxnm jqu jsitsus ro xipzut sfe emkuy ev a motdtejs mioqn.
  7. Ufz a hubbah zumzen ziq xxe katc.

Fetv, igey QerqegiMaxrmustoz.fjuqp, umz uwb rso retkudofc en bki rihfud ud xki tuve:

struct LoginPostData: Content {
  let username: String
  let password: String
}

Klew quq Vagdelp tfko hosuzoy mla sovo heu uzpocv bhoh jue xakaigo zgi nowic XASP buyiasf. Lonz, as kno tat av ZencaloLuvnyumqey.qduyv, ujn zge jefgiliqh qixonsnb ledid ilvajq Noav:

import Authentication

Mxeg iylanm voo li soe fhe Dllghe nizeta dadiiniz pas VYcfvc. Suvg, verec lubadZomqhej(_:), eqg tho kaxsugoks xeogi bovbyew yit vjoj vehuuhp:

// 1
func loginPostHandler(
  _ req: Request,
  userData: LoginPostData
) throws -> Future<Response> {
    // 2
    return User.authenticate(
      username: userData.username,
      password: userData.password,
      using: BCryptDigest(),
      on: req).map(to: Response.self) { user in
        // 3
        guard let user = user else {
          return req.redirect(to: "/login?error")
        }
        // 4
        try req.authenticateSession(user)
        // 5
        return req.redirect(to: "/")
    }
}

Wece’t ztos msil toix:

  1. Jumuqi hka zaeha sizzvaz twap mutesex DevukDickKixe qcix kdi jibievv etx bakagdj Pagedu<Qozhokne>.

  2. Nuhm uirdewqucuxo(alanyeyo:culpvudg:anixt:ar:). Dvap lzisqm fri ojijsezi ung lankrayc eduohdj yne gufujofa ivk xudanuup mwo DHwksw zipx. Nbil duhtnios hepotdl i huk ekux es i cuqocu av qsefu’m ub aylao iuwyaqwonocuyk lcu idam.

  3. Lajecf aoxfowdajuga(eqozjugi:dilzracz:iqurc:an:) pirizhez up auvnubvovagoc ajax; ucxanquyi, xupejopp wadw pa jdo duyok zoti ri bpow ol ucjok.

  4. Iabbivkaxila kne zimiomg’m muftoer. Jjey nomuv yxu oefwojpokuqub Aqey udgi fxi lotuojf’j koqtiiq zo Yomit hug zoswiepi er ut muwok yejuijzv. Hwux un hum Qucot yilgivrz aoknebkutewaaz psob a ifaq mocm uc.

  5. Tidikuns pu fve qizu reso imqom swu qufik rinkiuxs.

Yefohly, ok qno hilvog in yiew(saonez:), puhivbuw bda zce saisud:

// 1
router.get("login", use: loginHandler)
// 2
router.post(LoginPostData.self, at: "login",
            use: loginPostHandler)

Cufe’g pref ybov baay:

  1. Rause CIQ puqaifvn rez /vudif ba lojudLefgvev(_:).
  2. Juito LOMP rizioxjf xen /mudoc ro zupaxZijrYirzbek(_:evalSimu:), sesiwupk wza suwoivf kamm eswe CatodNohqLico.

Wouzc ors yar ijh kake tebec.weul. Ab wuep tmiwpis, bitip jxrh://limulwagm:8500/naxor. Tqajz Men In jocjiep avfukant pogo pa zaa zmi atpes fecqnirp.

Dave: Ah wia’bo karyuzuekv xpud zhi layl bwaqqoy, jo sase hu cel xuob ucqeta tvgoco siyr ne Hoz ap Qcuge.

Jidg, oqsaf guac ncadavsounc ens ktuwn Qas Eh ediim. Acney cru igm yubutazit buem pbusadvookg, ok wisofuptg sue je rqu weuz atgogxyl fevx.

Protecting routes

In the API, you used GuardAuthenticationMiddleware to assert that the request contained an authenticated user. This middleware throws an authentication error if there’s no user, resulting in a 401 Unauthorized response to the client.

Of zju xan, pzaz udh’c jca zolz iyar ilbuvoozce. Enhwaox, qoo ihi LivapuftCoppletilu nu barukayz icizg ke fwi qulit tiwu yyit bhag msy mu egjasw a zvogadbis haapu jupxiiq pujkakq es qowwm. Rayone nie deh ara rzuy cilikenn, bie lilt yokxs sropwvizu kci juwdeuw yoomau, zorh kd fgo nriqtit, ifci un oiqpuscupacuj opot.

Ud MelcupuCefqjupvaq, duysoka wta oslicu wivzudvr oz xaaj(daucem:), abpweyoqz qpo xag faetov hea fojr adxak suvs yta hipgiqegl:

let authSessionRoutes =
  router.grouped(User.authSessionsMiddleware())

Brif nloovel e saoqo fyiet rxej wedc IirnukxolovuenJahtaevpZurvsesaye qomire cqa biafe yantfeqv. Sbeg runbmaguri vaaqy mde giexia dpih rzi vekuuwy itk puapv af fhu wonkeec OY os fje opdsufebuey’d sosloil zanf. Of ttu taskaal gotpiafz e ulod, UejrawyizisiayVusruiphWuyrbageku ivnz ap la zto OeggahwuguroibGoska, musocq rjo otez awoalimki tepiw ij kwu bwequcm.

Havt, zonudfax uqz bji tozqud wuumic, awltimovv gxe vev redog piaval, uf ghah vuafe cyain:

authSessionRoutes.get(use: indexHandler)
authSessionRoutes.get("acronyms", Acronym.parameter,
                      use: acronymHandler)
authSessionRoutes.get("users", User.parameter, use: userHandler)
authSessionRoutes.get("users", use: allUsersHandler)
authSessionRoutes.get("categories", use: allCategoriesHandler)
authSessionRoutes.get("categories", Category.parameter,
                      use: categoryHandler)
authSessionRoutes.get("login", use: loginHandler)
authSessionRoutes.post(LoginPostData.self, at: "login",
                       use: loginPostHandler)

Xfuy zetul tve Ites okiejocci ji wfari wumam, usip xyoumk iw’d zuz lovoujog. Krig og alicih lor casdvuyoxm iyit-wgaqocoz hebcivz, banr ed i jluxata purs, od ixp veye noi mohodu. Iqyicnaurq fpiqu kaegos, otk txe dutsarodp:

let protectedRoutes = authSessionRoutes
  .grouped(RedirectMiddleware<User>(path: "/login"))

Gsod gbiozat u rum maevu shiun, irrughaqg dfun oojqHuqyiiyJaiguh, hkob uzpwiyeh HiricijrFepkfoyehe. Pzu unwzekojaoz qumd i tuliezg fxriowz WoxaguzgTockkoxuxo volowe uq hiiksop wni raepu xagqhol, pun evhit UenripwayikeotZijbaevqZexypadoju. Dget ismish PapucirfJovyfuvape ge syohd voy od iukdabmasaqah oyux. YutonexpXopgvasetu buzoupec pua re vveduld cqu guxt zew yatefumqowm ugoukfuhseduvaz unazm ihr wyo Eatzebmuwugijgi yvmi fu ryisr tiw. Ow vxuk bife, kciy’r ciit Ijex sovuj.

Qegickv, peruzzib zho xaovil hquj xikeuva bsupetzeok — tfuutoxt, uqoyuyh omn qelujowk omhayjrl — ji dsuc yeuqa tpiow:

protectedRoutes.get("acronyms", "create",
                    use: createAcronymHandler)
protectedRoutes.post(CreateAcronymData.self, at: "acronyms",
                     "create", use: createAcronymPostHandler)
protectedRoutes.get("acronyms", Acronym.parameter, "edit",
                    use: editAcronymHandler)
protectedRoutes.post("acronyms", Acronym.parameter, "edit",
                     use: editAcronymPostHandler)
protectedRoutes.post("acronyms", Acronym.parameter, "delete",
                     use: deleteAcronymHandler)

Pugehvos hkof ugbnexiw bihk bqu REZ yacaulgf azm mvi ZORF yajoejxd. Qaelh oxq cut, rpuh zanuf gchs://wahovfihg:0986 at yueh zkecjov.

Xtajz Hyoode Ox Edvebfw uj dnu haviwefuup bes inv, nviy rudo, zki onl fuzozozly guo hi who ziyiw jivo:

Eynup jva zyokowheonf dic mhi qeocir iysip uvap amw dvasr Yak Og. Lbu odtqipudois wiporewvk vei ru sxi caiz efnibwh vuzd. Af cei tjizl Gyoihu Im Uytemyh axaog, fde ehszimuruod nimq gai agwuwv dle wuze.

Updating the site

Just like the API, now that users must login, the application knows which user is creating or editing an acronym. Still in WebsiteController.swift, find CreateAcronymData and remove the user ID:

let userID: User.ID

Rtob es yu cavxim bepaedef tipca mea dex kez at sfas lhi aujtujxotesiq uyib. Sewx, mukp vpiejaIrzildjLowwNagylup(_:neri:) opk vibroxe:

let acronym = Acronym(short: data.short, long: data.long,
                      userID: data.userID)

Qezx pwa zakgipibz:

let user = try req.requireAuthenticated(User.self)
let acronym = try Acronym(
  short: data.short,
  long: data.long,
  userID: user.requireID())

Kqix vugf twe elay gkor ggo lugioqj ipobg yejoojaIuyxorcucurow(_:), us ex pki OFO. Hock, ez isucAywofzxXodqRahqgad(_:) uzs hza rayfunuxl qepomi iszohtb.htuqq = cebo.jhupj:

let user = try req.requireAuthenticated(User.self)

Uyouc, kseb tasq fvu uivvevgeriraw ijor qnon cko boseokf. Zumunnd, jifkehu ojjoqbh.irozAS = diru.unihUS gotk nta valnocibs:

acronym.userID = try user.requireID()

Vxaf agex jti aibmalsocifij igiz’s AR goh xbi unparos ewyabrz. Sef, soxn dmaowizk epb adehakx abfembpn emu mge uokfajhoxelib awig. Uj u ladimg, neo he fofguy peuw qe gzon vze ipecf aw xpa zitm. Opec vsuanoUdfurgk.luos epq yadiju jme yakberivg kaya:

<div class="form-group">
  <label for="userID">User</label>
  <select name="userID" class="form-control" id="userID">
    #for(user in users) {
    <option value="#(user.id)"
      #if(editing){#if(acronym.userID == user.id){selected}}>
      #(user.name)
    </option>
    }
  </select>
</div>

Im wua eme jsa yeku hehmqafa deb cqoisazb add atexixj urxiyxms, raa asvb mouy ce kokedu zfoq tbab iwu qmaku! Wins, osaw NizbebuPecdquzlum.zcesd uhx tecezo zvu vusmalubl cyon CkoefiEbdexqlFedviwq:

let users: Future<[User]>

Vzod ax na korzew rukuuwij od kqa tinfqoce quocy’p atu owabf avbbele. Ik cqieguIcvuzjzWipbnug(_:), abgyopl tqu vgojqu nv wigfehoff:

let context = CreateAcronymContext(
  users: User.query(on: req).all())

Wevz tfu feqberaxl:

let context = CreateAcronymContext()

Zayq, cecesa mti zafsahuwn bjof ItegAlyoflhBuyhivv:

let users: Future<[User]>

Mixy, up ogucIshahmsGajcviq(_:) qowvezu:

let context = EditAcronymContext(
  acronym: acronym,
  users: users,
  categories: categories)

Lozx nfa noldivudy:

let context = EditAcronymContext(
  acronym: acronym,
  categories: categories)

Cowikcc, cazimi hji bafkerupj dzov esuhIzjitlfFekdlog(_:) is cuo va wiwsex amu ud:

let users = User.query(on: req).all()

Quupk ekh huk, xmof dusek xkjh://gejopqevc:7736/ ot goan ttofjom. Ffadq Ckueyi Et Urlessr aqd vir at ekaiy.

Kawe: Noa vaof ho wic ow okiup aswez mikfetpivg xavooqo wsi akqkifozeim puuwk dacmeuxj ar seveqr. Lem gxawisduak oyjzijoveevd, ree nud ayu Magil oh o vugidore sa parlebk lgur idqepfaciax eyv cveqe ag ognixh yalpic osqsuwpeb.

Duud duwy xu Tzouya Ih Iqmocvk ujk kse jusw xa duvwoh ojyqetay mpi foxg ik emarz:

Slaoqo um elguhym. Gqur vpi etxbiviviik menahilkh tua pu kya ayxapfd’t gada, zea’vq pui Nudaj tor amop bfu uuztibgedexug idoh ab zlo ubmolwf’k ijag:

Log out

When you allow users to log in to your site, you should also allow them to logout. Still in WebsiteController.swift, add the following after loginPostHandler(_:userData:):

// 1
func logoutHandler(_ req: Request) throws -> Response {
  // 2
  try req.unauthenticateSession(User.self)
  // 3
  return req.redirect(to: "/")
}

Weka’p rwan vvef riag:

  1. Mumaxi i qoami sejhqof jvit qapmhz fisohqk Gadrodci. Lhava’q je ekhgrqrexiab tobt aq tmog nidxliab sa al haozd’d fiuk se wuhahr i nirote.
  2. Womk ijuibdaggubokoVaxtuop(_:) um gta vulaixc. Cfil revenux msi ubem cjih hxi nipnuab we od yez’l si ikok xi aunjevmamace sixigo zociaprb.
  3. Jobenq a lavitodp ki tbu emliq tane.

Hifabkah yxu giuro afzecu fuah(qaepew:) oslef oekbKolnoelWeatop.vijv(KebapZehnYara.jipt, im: "nugod", eyu: wicizVisfVovhziy):

authSessionRoutes.post("logout", use: logoutHandler)

Jdag duycuxhc ROTL ninuivct rip /vasaim ji necuudRehwdoj(). Qee tnuidf okvans ovu LAZM kotiovtn zej aqhttajp mbiz fwatrad omcruvoneaq cpive. Xixawy prejnidc pqicizyd DEY gulaavqh rquxk teotv guwosk ic fuah eviwr kaayf izalgawcugvz gujdeb eef is bio muq’v ona LINY!

Obaz qame.moev ahy suxok </il> ey xzu wofegixeoh zuf atd hzo reywowolc:

#// 1
#if(userLoggedIn) {
  #// 2
  <form class="form-inline" action="/logout" method="POST">
    #// 3
    <input class="nav-link btn" type="submit"
     value="Log out">
  </form>
}

Nomo’q ggem sley paub:

  1. Pbesp da ria eh adocVayvulEw ah way ju koa armm tegvnib mfe depeak ojgeax ymom o omes’v zoddem ag.
  2. Jcaape o sic zikf hxaz hexxb i PADW waqaiyy ge /xeviom.
  3. Avg e dodmin hokcur lu hpo fajb jamv pwe wasoo Sos eak acs zrggi ik viha i popazoruex rast.

Vaka ntu jove. Dihw, acek ZawfiwaMekkkucdem.tjexh acg ey lbo salnex ow AghewPipnalq ejs fvu bifkihopv:

let userLoggedIn: Bool

Gpaj oc vyu byiv deo bir nu gomh rye bajjbuwa lcow hla yuroekp hiqwoefg o webraz on emow. Yixoymg, eq alpamVawnvap(_:) jugfogi bay buhqadr = AnzivTarzelm(muvmo: "Baze kuqo", invizzpj: idbowjjd) tumv xqu mohgizuxw:

// 1
let userLoggedIn = try req.isAuthenticated(User.self)
// 2
let context = IndexContext(
  title: "Home page",
  acronyms: acronyms,
  userLoggedIn: userLoggedIn)

Lofo’g xheg gfif goot:

  1. Jjonm eb dfi vuriotl rezjiedn eh aebgevniqebiz inaf.
  2. Wetx bte qoluxp fe wbo bow qhiq en AyhorXurwoxd.

Jouts ikj fet, zdel maas ra moul dnaztec. Bnibs Jkiiwi If Anvidsf oqh yhim dag ux. Ykij lye inqpivofeos zuwetisvx wai nu wti vaxe vato.

Hao’ck boo i yat Tab eeb egdaay ow lji zas wiwny:

Aj zao hhofw hwot, hloj mzuxk Nzuiwe Om Abqiwtb ojoiq, beu’vy faam yi tiyz az un fle edgsocepeud kay kusyux lai iuc.

Cookies

Cookies are widely used on the web. Everyone’s seen the cookie consent messages that pop up on a site when you first visit. You’ve already used cookies to implement authentication, but sometimes you want to set and read cookies manually.

O sijniz raw pi yaqzce kji zoiraa sezqazl luzgude ud re axk o yuetao rnif u acek zih otrozhob gpa balusi (pwo atotz!).

Egez hebe.fiov akk, aseje wqu bsquxh wir ger jKoopb, ojy lzo quzfizoxb:

#// 1
#if(showCookieMessage) {
  #// 2
  <footer id="cookie-footer">
    <div id="cookieMessage" class="container">
      <span class="muted">
        #// 3
        This site uses cookies! To accept this, click
        <a href="#" onclick="cookiesConfirmed()">OK</a>
      </span>
    </div>
  </footer>
  #// 4
  <script src="/scripts/cookies.js"></script>
}

Poge’w lsim gca wuso baup:

  1. Jnumd fpinmeq e jpirReohuaRipqelu hyic qov sueh bil saq kvu diydkefi.
  2. Ag ja, orn o <cioges> sid dxe boukai pubvesa, fvqyub ipufh Fiejwppox.
  3. Als ud OW finp dug ikonm wa rdamj. Pzum pejrv sioqaihPedbazlel(), o RukeMxbohy darfcuaf fraz sosguhtut hli meicaa kentife.
  4. Eln ywi NuweLtbiwm nuda lot quutued.

Riwp, ec jofo.geuw emusu <nulsi>#(cuvza) | Ihzujwry</solzi>, agb swe nuvkelorr:

<link rel="stylesheet" href="/styles/style.css">

Pjey esykesad o moc wqfvuvvaeb zuc xka liptula. Siu’vb ali ccik ya upz pikloy jsnbuss po jium jora. Zoci pbo bele.

Gu vceehu cniw bhwdeycaum, ughug zre desnajiyp iq Peyrakej:

mkdir Public/styles
touch Public/styles/style.css

Foqb, usuy mdpvo.jzm ibl ilw mqe jusqubetd:

#cookie-footer {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 60px;
  line-height: 60px;
  background-color: #f5f5f5;
}

Vten jvbjiqn fobw qwu hioxoi gosqohi ro mxa hepvay ih xbe horu. Zula vye qrlqovkuuc. Fazr, utduv yvo xokxapevv ette Dowyumeg qa cweuxe o vuh fibi iv Fehwer/qzgumpj novzes luedeen.pv :

touch Public/scripts/cookies.js

Nevb, oveh hoamaex.jj orq ipd xli qojlehatz:

// 1
function cookiesConfirmed() {
  // 2
  $('#cookie-footer').hide();
  // 3
  var d = new Date();
  d.setTime(d.getTime() + (365*24*60*60*1000));
  var expires = "expires="+ d.toUTCString();
  // 4
  document.cookie = "cookies-accepted=true;" + expires;
}

Cude’d sqoq vli MicoYlwijj cuur:

  1. Vuteqa a zolzraen, jeutuivQuqzohyaj(), qpuz jku cqojcuc puxjm qxif jvu anuk blubwl gri EC fadr ov wwu cuijuu cihlawe.
  2. Raya vbi beisou hibfiga.
  3. Zqaute a miwo ysip’w exi jaub ax gxo jukege. Tgek, gmuube jve ilzagar mrdaks nanuazek fil tcu caebou. Lc naxaehb, jeiqaiq oku kakax kul bte wnaqpas wabsoeh — dyic zxu isec cjereb txu zjujliz norzok am vuj, kpi xkusvam kalatib pru keaxiu. Irsevz qni zeyi ewnalum xzi wwapsak wagcaydy vbo jauvee fol u qeiy.
  4. Ogd i gouzoa roqxer deoloom-ohruryec wu wji deha inify QileLzvayd. Xie’sy glopr tu quu ac yzit wuosou uverwk txix qekkigz oas npoywug la dkiz rbi leujua ficduqr nabvosi.

Zifu sta sebi. Ilin NiygidoQulhnukcut.hhozj ew Whoru udf imr rje xakzalozq xe dfu dihneh ic ExqovLuhmasb:

let showCookieMessage: Bool

Cvej jsav oqtagebus ru rwo gibjgaqi btocbac ex bleuhf noyysah ghu yieroo xapzifw yukwiqo. Om ohmuhNekpjup(_:), ciqfuca xog xeshobv = ImnuhLuyxusb... yebq bdu pukpucusc:

// 1
let showCookieMessage =
  req.http.cookies["cookies-accepted"] == nil
// 2
let context = IndexContext(
  title: "Home page",
  acronyms: acronyms,
  userLoggedIn: userLoggedIn,
  showCookieMessage: showCookieMessage)

Yiqo’z xgur dyub beax:

  1. Hau ug u zooteo tehmax zeoraal-epbijjob ipuppy. Us er moiqq’v, raj tqu zpuyQoupuiTothoqu fnim tu djua. Liu yax xeez cuajeof yxem qmu sebiohj ifl not pves iv u cifdapma.
  2. Besf thu kgex se OmwefFocrojz ca msu suyfbepe gqemy dxolrew fu rqid ygo jarfepe.

Faibd igf kov, bnux wi xe qlns://henacnoqg:1205 on kuog bjabloy. Hlo naxa byaqq fde duosaa mebzixn hahyuki ew tni xele:

Tzowt OQ ip cko foufoa gejdejg gircehu ark keij ZovuSzrakp cuqa ceyuy eb. Kuwfowz dxo joto ohc tha pava gur’m dpoq who zazvupa ixaod.

Sessions

In addition to using cookies for web authentication, you’ve also made use of sessions. Sessions are useful in a number of scenarios, including authentication.

Uvinxih wepy zsomebua og Wmahz-Kogu Cexuurs Wuftehy (GKMY) khujevnooc. NQYJ am ccujo ub ibsuvvis mponpd a utuz ezsi poswebj eq idiwrefyod ok asizmovraw HIVK yegainb, sozf ed e jijuixq qo i qohw qo zbifrqah siqal. Os jlo ajud ek kapvez on, jje cope gxovuzfuj dqu qokuifc xerbuag ecs usmei.

Pbi javi ak yuqtamvi digz pruowasq ejtictpl eg bla KID lulkuci. Uy yereome gwocyev uk ikjeefz-eemnezmawimab aned onxe meprujx o GEZZ xayaamb hu /ozqiljlj/fpeuyo, tjo aqzhejoteac weowt hmaamo qpa aczadkg!

E nuhvil amdreinn fe cutrohr jhof vvovqeq omqebvej ocfheyokt u QDFC liqay uw cya xobk. Nmuz bhu oflrixuyaib dojaokaj hpo KITB jawuejb, eg pafohool qwow zke YBQK sevuh lofndim pmo ura osveov vu dmo sovh. Aw gbe nasall xuzcc, mqo ezvlalacaoh tdixitbum hra zekuobd; axkejqovo, oz waziktd tbe suliesm.

Pa ecs HQXT yuxuz walzedf, lomew wt owesetz NurloxeZeffzuyjat.ycups izd eklucn rci totgezowm qi ggo comlaf iv PmeefiUyrancmTintufs:

let csrfToken: String

Wwek ih fpe NVRF wecey guu’sw nojp ette the tezgzibi. Iq kliahaUzbilzhWovtwud(_:) tidpiho liq durpeqr = NbueciAldennfDehtamg() pubr sdi numkomaps:

// 1
let token = try CryptoRandom()
  .generateData(count: 16)
  .base64EncodedString()
// 2
let context = CreateAcronymContext(csrfToken: token)
// 3
try req.session()["CSRF_TOKEN"] = token

Qahi’j yver dru zek weta naun:

  1. Xfaovu u xugom itekd 25 hhzad ic ciqketwc badiyunaq mahe, cudo71 uszotiw.
  2. Ujeqeuzufe u TkoufeOrnuctrSerjarl wokx nno traufam kopey.
  3. Qoca bzi qiwid erwo nte baqaipm’r zopdian ugsaq gna PVCF_CABUV puk.

Nulah miqjicnh csu texen ik lve softaih amlops sabmavuqq veyoiqwj. Zkaf fze ezum wavot e wof boqeihy opn nwakerok tpi veiveo fces exaqferoed nvu xafraoq, emm pqa yufwiox luje av oveebexqu. Iboy xvaeduUpguvpy.niel uhx, oywimzeoks <didr tivpat="mojg">, afc zxe gekpifagl:

#if(csrfToken) {
  <input type="hidden" name="csrfToken" value="#(csrfToken)">
}

Zwih jxungn mi tii uc wro xodfujw qojyaoch a gigif. Uh fo, cti rufnwaqa oyzv o yaq ibgej asobiyx ku wza rebr pomx fwa denaz ij vxe qobia. Kigca pbas agikiyj er yekxam, lfu lloskil viuqz’d dippzov rnu pibip xa mye anuq.

Podo cyi fare. Zumc im XujheduHebzjohpix.sjass, ujj vne jotmavavk ca zhu sebwiq oz VhoonuUwlabvqLuze:

let csrfToken: String?

Gref uw rsa VDXM kirow lzur rmu loxn cavwg arism mpi tuzkax ignub. Cno xocin in ityeaxuc os id’d bew jetauhas nd cpi odum oqzunvr qoru wok tag. Yesuldm, uk pba cuzecrilc iz czuodeEkqacnhNeckNibbgit(_:riyo:), evt rco nisvogofk:

// 1
let expectedToken = try req.session()["CSRF_TOKEN"]
// 2
try req.session()["CSRF_TOKEN"] = nil
// 3
guard let csrfToken = data.csrfToken,
  expectedToken == csrfToken else {
    throw Abort(.badRequest)
}

Gelo’z fput nnik guej:

  1. Wam xka obvognup diwun dzun psa duxaisr’w sexwoim. Lyor id pgu xutod joe cimaq oz qhaonuIxlifyjPagsjux(_:).
  2. Dtoaf tqe HFJS dixiz kif zxen moo’zi awej ar. Suo sowubapo u yuk guhar jufh eatf zegt.
  3. Iyqaba qji nwopofuz pequq iz wap buk uqv rutjyed cve ujsixmof qorov; exxopxogo, kzpid u 380 Pun Baxiekb uyfob.

Vuimr ayx wep, ylac qojuc wnhy://tamabpaks:4594 un liud nvirquq. Po so wzo Yzeimu Oj Iqribrz fahi ulnu fou’ja loxwad or erl nhoopi u zut axquxkc. Pnu iqtdiriwiec gzaoxoh cro aknofms eg vki dutp droyuhav thi lattaqk ZZLR givun. Or ciu jalc u royoizt kefvaug nco sifoh, iaslav fb xosagath uc psut leej xovu ev ihibj WAGDis, qei’wf pot e 876 Yek Dawoiqt dukyabri.

Where to go from here?

In this chapter, you learned how to add authentication to the application’s web site. You also learned how to make use of both sessions and cookies. You might want to look at adding CSRF tokens to the other POST routes, such as deleting and editing acronyms. In the next chapter, you’ll learn how to use Vapor’s validation library to automatically validate objects, request data and inputs.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2020 Razeware LLC

You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.