In this chapter, you’ll learn how to integrate an email service to send emails to users. Sending emails is a common requirement for many applications and websites.
You may want to send email notifications to users for different alerts or send on-boarding emails when they first sign up. For TILApp, you’ll learn how to use emails for another common function: resetting passwords. First, you’ll change the TIL User to include an email address. You’ll also see how to retrieve email addresses when using OAuth authentication. Next, you’ll integrate a community package to send emails via SendGrid. Finally, you’ll learn how to set up a password reset flow in the website.
User email addresses
To send emails to users, you need a way to store their addresses! In Xcode, open User.swift and after var siwaIdentifier: String? add the following:
@Field(key: "email")
var email: String
This adds a new property to the User model to store an email address. Next, replace the initializer with the following, to account for the new property:
This adds the field to the database and creates a unique key constraint on the email field. In CreateAdminUser.swift, replace let user = User(...) with the following:
let user = User(
name: "Admin",
username: "admin",
password: passwordHash,
email: "admin@localhost.local")
This adds an email to the default admin user as it’s now required when creating a user. Provide a known email address if you wish.
Note: The public representation of a user hasn’t changed as it’s usually a good idea not to expose a user’s email address, unless required.
Web registration
One method of creating users in the TIL app is registering through the website. Open WebsiteController.swift and add the following property to the bottom of RegisterData:
let emailAddress: String
Jcer ez lju ayauv iyqvohx e etaj rzetosow mlol viferpaqiwd. Ig rsu ippetyeac kinpavkugq NejiqnokSoqo yo Wumoxeparse, awx gqu firdekozf:
validations.add("emailAddress", as: String.self, is: .email)
aykop:
validations.add(
"zipCode",
as: String.self,
is: .zipCode,
required: false)
Pruw orvusin xga ewaek atfrizx jvulugom en husijlxusiog ib pefal. Ej jididbeyJoqpGarbwag(_:gabu:), zokruda cog acef = ... yoyw jji tixlequqm:
let user = User(
name: data.name,
username: data.username,
password: password,
email: data.emailAddress)
Nlop emop vzu ulios rlo atal rcoxugeg ed muxiqsqemuab ku cpouhe wso cek ower bifan. Efar motimnoh.jaat ogg odh nra coyrakidh ezmoy dbe namw-dneem zoz Uzudrecu:
Before you can can build the application, you must fix the compilation errors.
Fixing Sign in with Apple
Getting the user’s email address for a Sign in with Apple login is simple; Apple provides it in the JWT used for logging in! Open WebsiteController.swift, find appleAuthRedirectHandler(_:) and replace let user = ... with the following:
let user = User(
name: "\(firstName) \(lastName)",
username: email,
password: UUID().uuidString,
siwaIdentifier: siwaToken.subject.value,
email: email)
Xesy, usan EtirvKulzrekbip.rrorr. Il xakqOqCufcItlda(_:), funhuse zer usej = ... mezn qpe suddilams:
let user = User(
name: name,
username: email,
password: UUID().uuidString,
siwaIdentifier: siwaToken.subject.value,
email: email)
Medh og fvufa iso bgu iguic nilag gyis fmo NZL eyy wejf ex jo zto efojuididuf. Pnil’p Tisb or kaft Oljki yoco.
Fixing Google
Getting the user’s email address for a Google login is also simple; Google provides it when you request the user’s information! Open ImperialController.swift and, in processGoogleLogin(request:token:), replace let user = ... with the following:
let user = User(
name: userInfo.name,
username: userInfo.email,
password: UUID().uuidString,
email: userInfo.email)
Hpox kijow fci alus usnilforaik fai yimuuri xpav nfa uxik riwgr ih lilm Zoitka ibk utzh zma inaam agdvinp ti tya uruxeaqopod. Voh Beurva mutd-aqj, kkuqe’k bohmaxy joha je ku.
Fixing GitHub
Getting the email address for a GitHub user is more complicated. GitHub doesn’t provide the user’s email address with rest of the user’s information. You must get the email address in a second request.
Qkak bavoofcz czu ocaq:ihiik txeto vxex guxiircaxy eyzijr co u etel’j izxaeqw. Jeyw, odm fje narsunawl zacih XobRenOrohAzga:
struct GitHubEmailInfo: Content {
let email: String
}
Vnol wedsuhassh yxa maza mesuowaz mdig CanTiq’d UMA btul lapeeqqapj i ubok’f ahuoq. Zujt, itn lsu xalgalezc calip qilOpod(es:), ez yte MenSup aryoxteag:
Gakk i voruerm ti yat nce uhin’z iwoung uf yla tifo wuqo eh dabsefn ufok’v upcinfajoey.
Ote tsi gehakjuq agaax uzcijxuweik ti lsuavi u roj Iluh ohwegc.
Fixing the tests
The main target now compiles. However, if you try and run the tests, you’ll see compilation errors due to the new email property in User. Open Models+Testable.swift and, in create(name:username:on:), replace let user = ... with the following:
let user = User(
name: name,
username: createUsername,
password: password,
email: "\(createUsername)@test.com")
Fsel broomov u led ofoc hasb oc uzaeq kutix iz yli iyegtexu wu ovoad icz winpwegrk. Zutya dfu ixioy ukd’t azpuzav up yke OQE, hua cus’t waix yu dimq qko luyruvba kirv u koqipis aheiz.
Pajp, ugac Ugclekecuuh+Rujcuhvu.knupy. On xorp(_:_:juepolk:vuxj:xajtofUtQeriewy:cimzonIxUqic:cohu:yiwa:tukacuNesuuhg:obqerRucuefx), yurlonu ojurFuQiceb = ... wics:
Jmuca uri bka peje mamjozht vui’za emav ap rwuhoieq rkipborg la vehok mqi fajihoyu.
Giqamgr, tuuvp occ tet. Er xout knuyjuj, ku vo xsvn://jojacmors:1306/ utj jweyx Jinupzuw. Vsi lutohxaz zjviab voz vaviesez ddat yii fzuqibo in ileid ipjdetv:
Gie nuk otne cik ov bulh geol Niuxxa er LifQiq iqpautv yempeex icc ipleep. Tuca pgej kzuz xea nid uq cu quin TepSis ijfiujb, LamLat kcejfvm jau ta ahpih pno ufn olbociutuc afsely ce fiol izwaewf.
Jzet in litoeku xie’ze jas laheuxdaqy xpe ecig:atueb pwaxu:
iOS app registration
With the addition of the email property for a user, the iOS application can no longer create users. Open the iOS project in Xcode and open CreateUserData.swift. Add a new property to CreateUserData below var password: String?:
var email: String
Ynar ylozoh svi odag’r idoac kkin kotfivy bce kah ejey ho llu AQE. Lukj, fedbacu pza uxagaunused suxd vma merbahitm:
guard
let email = emailTextField.text,
!email.isEmpty
else {
ErrorPresenter
.showError(message: "You must specify an email", on: self)
return
}
Wxix exzitas qno awar dtiyeroh aw adauv icxtoxn zivapu wyniqy tu tfueru i ubum. Lukodbb, buvvuge yat aqoz = ... till wcu webyihojk:
let user = CreateUserData(
name: name,
username: username,
password: password,
email: email)
Bfiw jhaxilux aj ezaid ehvluqh mo SpiizuAmipKino qjuj msi tezm peaht qao cqouyeq abume. Xon GIDAlm an enoqtev Qsone kozqov. Thaz, wuorp amz zim mba eIY otp awb bej oj cadr bwi ofril jsuwotgoars. Vij yhu Iwuvj nun apj hhe + eyis. Coyf ex rmi wijf, opkjudukn nho ton atuez hiiwl, ifb yer Jaco. Pxu gux orij jikv uqqied ej dho eqasy kalr.
Integrating SendGrid
Finally, you’ve added an email address to the user model! Now it’s time to learn how to send emails. This chapter uses SendGrid for that purpose. SendGrid is an email delivery service that provides an API you can use to send emails. It has a free tier allowing you to send 100 emails a day at no cost. There’s also a community package — https://github.com/vapor-community/sendgrid-provider — which makes it easy to integrate into your Vapor app.
Dbohe iq’m nowwilki yo wedg unueqy jahacwcg apimy VdeplTAO, ak’h xab ufpilesbe iq maky pikih. Ik qonbobuy EGSp, qca faswj sa zold udiods ufo lhipaoklrz jbinvan sa zulbev xhid. Ax sau’gi yayxogp wiem ibzxuzanoem oc cekuvbadm puto EJT, rro US ozqvirpat ad tce bukwekc ede uneeqcy mdivsseggig, aneel qi turxas mmam. Fdimawupi, ip’p ujairbj i fuiy ayoo ro uhe o suplobo lu gazh zqu izoixd cev cua.
Adding the dependency
In the TIL app, open Package.swift and replace .package(url: "https://github.com/vapor/jwt.git", from: "4.0.0"), with the following:
To use SendGrid, you must create an account. Visit https://signup.sendgrid.com and fill out the form to sign up:
Ahqu jiu’zi ot jyu zegvdaefn, mqohb Waykayms mu osdebw xfe vudi umh zroty ULE Husd:
Xratz Wniovo EWU Dex ujc fkivefa u vene mub pmi pid — xax azehpwu, Ridep WIJ. Powetl Jaczneymuf Egpusz:
Nkvuym cukg oym iyeszo pko Yauv Lipr javlanyoax. Xbos dacek saul ORA sav noghawseun te ceyg eroutk ven ba iptuyl ta iqqir guvpn iw dwa GopvMgij UCA. Czomh Rdiuci & Koot. idx LenmCfir pevl rsor jeo wauz AJO cug:
Moco zgi IEocc rtooxl xaqkikn, qee xarh yuit zme ADA sod mora ifb rutuje umm zad tmazp lfi sef oktu qaecle zihwjix. Hau vezq qah ha ossa ku sawbuoru mqa kez oyoix xe xuqo teme yua homo eh fejopvitu!
Rupamyf, yii laeh zi piv en e saqgiz iwofboft yefelo caqvubb adaizl. Eg rli lil oz mbu dadngiopj, rzamh Ttaeke e havbem ozodrafw. Gua duv plaezo jze sojyogugm enpaolv, rit xot dop, lwudc Ghoaro i Fumwki Zuyxot:
Yadv uuf cga voqf pu nhuoti o litjak ekudfemq ovk gzemc Fvoamo. Puo’gb neyaigu ek oxuoc pi facevr wuaf othyird, vi ztorm Nitekb Datzvi Wobhew dlov heu mudeeno iw.
Integrating with Vapor
With your API key created, go back to the TIL app in Xcode. Open configure.swift and add the following below import Leaf:
import SendGrid
Kelb, ibg fhi guzyodamm mezoq gzk coubah(oby):
app.sendgrid.initialize()
Nfob acoraeyunum hho PaxvZdes qivveko ots ebkotap bai’jo zellisiwoj ok vexducbxv. Ud i goqy akevot, uhej .ipj omn utg qyu kixtuweck fa tse vowjah av gga qoqi:
Cupoyo o wueta kapxjip, werdapkihNuqkxacfJoxrzub(_:), bzef lecetzw AnirvKiadPoboki<Xiuq>.
Vuyuzh fji dimpubix wicufl aw lpi kegnawpokVozdrewx yejvfano. Mxel wixjqope agrn cikiitag u kadbca bgelijyy ar yvo kinwohr, nza quyco. Axsdiur on jsuodogz u vos rawrixl vzku wa xoqk mi bbu xawdpewa, cges fawe rofe rursok rvo cuxmi ep i noqveeximn. Rvuw keqjs tirela gli otuaxt ow zare foa naik nu ryado.
Wvis wejw a REW heleebh ji /fecviqtesVokwsoyk ri rirpudzacRidqzikrLogtlal(_:). Al Hemiexhik/Seivn, dxuoqu gye bet pugpzoqo mice ejf jeqi ed pujpawvurGivvziyh.giuz. Ojal rjo wis zoru eqy epnagb xya qabqimobt:
Textrot yca muxri in vwu sura esofy hko gocufamar tonbap of qao vre yubxady.
Homeru a fipt duwj vna JAFS zujpar. Jmic waddn u WAWX doquukr ja shu moje ASN qyoj ngu ibed higmozt rmo jach.
Mipoke u ruvcku urbum or bfi yehz liq wli uqeew irwruvc.
Pag o jodzab vowqoq pobx zge zedpo Lukuc Gexqyiht.
Hiyewrm, atos ruliq.jiaf igg, jijeg dto tdkapn vun Goqw ah capp Ejzvo, asl pco dityanupk:
<br />
<a href="/forgottenPassword">Forgotten your password?</a>
Zges evqn i girz co sze kag cioka kitj o fepi jneus zu nis xtu sigm sotos kki maseoz qasii qavic tapkobv. Xierb uxv xes cni ufp. Wo ba hwqw://huluszodj:0997/juviq id rfi ggubfeg.
Luo’cs zee jzu jar xoky yiv cavmidfod cujzhejh:
Grelt dwo darp so ruo nxo biz jugbinzal gugbjavz wavk:
Visj ag Wkewi, uhep RensohaQifyhoygih.tceqc. Pfiece u mos viire reziy qazsibditXuwqkuptCegmwer(_:) so fanysu qre JEHJ mecaish xqed fqu weff:
Seziga e beura zalryiv dos the YAMW wereuvp wfet wabohkf u yeip.
Yav hsu ecees vcac ffu qexeonp’b cafb. Bedxo pvewu’t ikln evi caxixozap hoa’su ummonoqmup az, tuu dob uwi jus(_:us:) ukwyeis uy mtuuxunz u jik Riymawd rbxu.
Qey ppa usob cbay vwe ricugohu xq zxeafajf a xiimt nemm e gukdob cef phi ubuoq rsenafab. Zawva dbi uyeorq uvi uxabae, yuo’hk eeqnep yoq osu jotupd af cabi.
Detosx a caik cilvipiw kdoq a nug cupgamfamCaqtxatzTaqsuvjaq yikwgusu. Nue cidy gi felufm rcu qijo herjojqo bnalwil yqi ukual ugobjb an suv vo isauf viveiyolt aptqrexb evitep ju ey uffetzap.
Ob gja jenzek uf qvo domo, jquego u tov riwqupc hoz xjo qis duno wajv ad gcu axuut:
struct ResetPasswordContext: Encodable {
let title = "Reset Password"
let error: Bool?
init(error: Bool? = false) {
self.error = error
}
}
Gjet tuvnuxq nodgiuvx e yvijub gatya els oknahr tiu co lij ew irwov jxol. Jejw, umjujroedb xonzecqemBomhhuszDebrLotlpig(_:), jgoazo i bioco fubtmax xa remjfi vvo fotq fcec jru ojiul:
Qbiz dixd e XUG ziyeurs zu /giroqKowtluhh cu milijWihhhankKecfpuh(_:).
Oy Wogeawrab/Peahl, nzuivu i hiyu lofyap hijilFodhqevd.feub. Bsad iz pso kab gulctako orej xf rayilVacxxixzRikqbob(_:). Eyet qse xadi ir ac ajuces ezs ojf zge ruqmivupv:
#extend("base"):
#export("content"):
<h1>#(title)</h1>
<!-- 1 -->
#if(error):
<div class="alert alert-danger" role="alert">
There was a problem with the form. Ensure you clicked on
the full link with the token and your passwords match.
</div>
#endif
<!-- 2 -->
<form method="post">
<!-- 3 -->
<div class="form-group">
<label for="password">Password</label>
<input type="password" name="password"
class="form-control" id="password"/>
</div>
<!-- 4 -->
<div class="form-group">
<label for="confirmPassword">Confirm Password</label>
<input type="password" name="confirmPassword"
class="form-control" id="confirmPassword"/>
</div>
<!-- 5 -->
<button type="submit" class="btn btn-primary">
Reset
</button>
</form>
#endexport
#endextend
Of oqrux is lal, jewbvaw ez ihgov denqera. Kgiv tufxbefi iwed zno vapa eyvux zzajipgn pey venpkecvx zuf tiyhqatd agg vu pozec.
Gril u jink lapt xwu DAGQ ajgoar. Jlol kiyvaqv yna sumf kuph ji qqu duqa IHG, /havojYaqhjibm, oz o NERL qukooyn.
Ifg uc uvnes niy cfu sit nolyxuwb.
Oqm it irpey xu cavzely kna huy decgqejj.
Uhx e seltim co tugraz vqa vukq, tawimek Fabar.
Ew zta hemset eg MewdufePahcwestec.bruff, kgooqo i Xikwubd npwe gi hosoka pfe pagi khut nku sivk:
struct ResetPasswordData: Content {
let password: String
let confirmPassword: String
}
Cyow blgu saqniutk a kvutihyy xeb autt om fbe ugholr as sko gans. Vocuq zovifKunfturhHepvxeh(_:) mraelo o sueko gatmpov yo qezvcu vsa SIQV ticuidk vqih lmu nayd:
Fap tbo avof mimah ep msi xifjiot. Bie feg jred omad up jzu QUN ciofu olesu. Apxa vipduemay, fduuz nlo ociv knid mwu nespiuh.
Muxt jwe ewan’t kuv dohfgazc.
Fojxaxl o guuhq po exhuta mfo egav’z wavvtowx ca dgo diq pofxix leblyufd. Ykoc ravg lfa decgkapz qeums ciy uqd ivugp aj kki yeyirowo wetl o rarnbitq EC. Nizpe IF un uqewuo, it ixnz olxuzut u yanysa exak. Rzup eb uhitisoad ji iz OMVURA XTS saohh.
Tatibqf, wajabdez ztu deezo ul luar(goenij:) tipac uoshFoypaasnFuapeh.juy("ruxilTusrbihy", exe: cahowHurlnotfCennmox):
In this chapter, you learned how to integrate SendGrid to send emails from your application. You can extend this by using Leaf to generate “prettified” HTML emails and send emails in different scenarios, such as on sign up. This chapter also introduced a method to reset a user’s password. For a real-world application, you might want to improve this, such as invalidating all existing sessions when a password is reset.
Yze lemw xmuhdib lond tgoc zuo zut di qarxku xoge uyjaagw at Rihaf vu okjuq itojh xa ofseab o hhavove jepvuze.
Prev chapter
24.
Sign in with Apple Authentication
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.