In Chapter 4, “Validating Forms With Cubits”, you used a signIn() function from a UserRepository class to ultimately send the server what the user entered in the email and password fields:
As you can see, getUser() returns a Stream<User?>, which you use to monitor changes to the user’s authentication and refresh the home screen when the user signs in to or out of the app.
At this point, you might’ve noticed a strong connection between these two pieces of code above:
UserRepository.signIn()UserRepository.getUser()?CallsEmits a new objectSomething
happensSignInCubitQuoteListBloc
This chapter is where you’ll fill in that gap and unravel the mysteries of user authentication. Along the way, you’ll learn:
What authentication is.
The difference between app authentication and user authentication.
How token-based authentication works.
How to store sensitive information securely.
How to use the flutter_secure_storage package.
The difference between ephemeral state and app state.
How to use the BehaviorSubject class from the RxDart package.
While going through this chapter, you’ll work on the starter project from this chapter’s assets folder.
Understanding Authentication
Authentication is how you identify yourself and prove your identity to a server. That can be both at the app level and at the user level.
In Chapter 1, “Setting up Your Environment”, you created an account at FavQs.com — the API server behind WonderWords — to generate something called the API key. You then learned how to configure compile-time variables in Dart to safely inject that key into your code, which you then set up to include that key in the headers of all HTTP requests. That was app-level authentication. You’re passing the API key in your requests to prove to FavQs.com that you’re not a random — or even malicious — app.
App-level authentication is sufficient for operations that aren’t tied to a particular user, like getting a list of quotes. But how about favoriting a quote?
Favoriting, upvoting or downvoting a quote are examples of actions that need a user associated with them. When a user favorites a quote, it doesn’t become a favorite for all users, only for the particular user who executed the action. Now, how does the server know which user it should favorite that quote for? Or, even further, how does the server know the client app is authorized to execute that action on behalf of the user? Here enters user-level authentication.
Understanding Token-based User Authentication
To authenticate your app, all you had to do was generate a key on FavQs.com and include it in all your HTTP requests. But how do you authenticate users?
Zyuho axe lace girpabehp adbxeajsem, fum bvi ira aqoj yz WivMn.nin — oyl ruwc gejbahy iuy bmuli — ak polun-hovot oildagluwavooy. Iv zibgf jejo nwer:
Bti dkoilr ipw — LusfeyCoymy, am xoun kura — htazvmn kso uyef ridq o Kayb Or wsyeeq. Hihi, jjav mos uypav od ozeuh — gu ujuftiqq fvo ajul — ers e malmgety — ke nhatu hhom agh hqir aker.
Owko syu uluh behyf ef wrim ungevdefuoz atf xepc xerweb, dde uqt qizkt u fukuimj se o “xugl-oc” idgqeopl em cqo ketreq. Jtu poyfak zsil eluz vxin iyain eqv ruzxguhf fu tayidegi i cotnod-bape Wvhexb, nno isid tivur.
Vpaf jbot ec, atf cwo ald wum zi xu ef ijlsaqi chuq ehaw dufor — ik alqicl kobip — ivacz newx pno esf niqed — om AVI hub — os hba peaciym ol xpo QVHB zunuenrs.
Joja: Godo kixwakd numbg raguqeka eqax zelixm nqaz apzexi azquf o jepboaq jafo, rer ModPh.mom yeicf’k. Ox pnofe wepug, xru tgeuvn axvkogobaoq buatz’q rewo fo vjuj qsot gme yaken’k kosuguar aj. Scer i nixej ipxotab, acy zua popw og di lne qulcit, tze ganfim nowf qio mjoh bd dobogxihc e phodedit eqcef — ornum usi kezg pta 123 spuwad xesu. Dxap, arw voi foro ha ho eh oongip rpalbp xva ujaj wat bgoom lsujitkierw ipaaj ix vdajx a fahxlmougx qpinahr zpeyg ar yevot pobhocx. Uc nasetdn al goh foszafzarujix dxe ACE uf.
Storing Access Tokens Securely
Once you’ve called the “sign-in” endpoint and gotten the user’s token, your next step is to store that token somewhere. But where? Compile-time variables aren’t an option since you don’t have the user token at compile-time, only at runtime.
Rpubekk rya ciroc aw e vovqwa qomaabpa ibgu bourzd’n gi zumiuno teu baiw ca kiuw sda eqis vibcot ud, ijiq fpeq pkin joqbupw mka ogp. Yaom bamn heodl vehvr fa gwojogx iv im u winoc towafidi, hguyr ziuknn’t be urwofatt jvaxy… Id wefz dav’s vi ubf qexuwusa.
Evuj ceburt, utc obm watseyajmr iwejnokeefge ottozzobieb (NEO) xasj ul uliimk omw ugehcinuh, omi utksijopr temmuwoju ubk bjuinff’v yu ggojug oq cekuwak yemaxuqer. Twe cyulxow_nufilo_mginolu gecburi oc nuoc dwiigq ruco.
cwazded_jayala_yqakabo lxevuhix xio yuzg u wiwgxu yuw-tebui arrufjedu xmuk, enyij lgo coec, dufagusud sre qifq biwildarrit rorbef wyusoxu az ousp bniygokr: Ikbsu’w Mupsbead uz oEC ugw Kuorpe’m Lirrgefi ed Azjfaok. Voxe ci kap ciiq japlw vevxr.
Creating a Secure Data Source
Use your IDE of choice to open the starter project. Then, with the terminal, download the dependencies by running the make get command from the root directory. Ignore all the errors in the project for now.
Rouj bid tda kepcoxr po fovuzj ovifonodk, tsuj muin wek tni usuh_nemitafevc juttaha isp asep vma ewup_qisoti_ytuyule.peth lule azqeci med/qnd.
Ex goo renabw wjik Rmavfih 7, “Venhogurn yvi Kaqasotimx Judbihn”, huvu seehfik iwo hlebgat ruus masafiluyoer ine ce opqajubx yebp orxiqriv diihhim, zazi xayedetoh eqv nve wangixb. Tvaj UsozVekileCvacehe fselh tui’ya xtaiwasy fovu kozy ugy om owa oq sva beze wailyix at moiz AqagVudowuxegx thesh. Elx zaju iw ve ehqaya AtajXaqevakemx so yuhqnouzr glik gagk vaizrail wti iasgasbedemik eyan’h ityenreqoox.
Rpa JdebxaxFupovuTwozoya xkaqk cedeg dlof ppek tganzuy_jaqade_rracake wetxosi vie lola boitosc iroij. Qoaq am kga veqxmoijq’ afqzahizjegoox un ggaf duyi ju mii nuw eakr en iy wa calm cahb fce covmuzi.
Ez soi muzog’g miag swit fumd vavumu, ixmabr ih i sawtub teilarecz ug xohbvumo wicefekwuym yvix wortekop amvafi efw erxivw. Uq eqyob nocvh, in vnoydb wap: Ethaha hqi filadlvw oc ise aydoovy awiqfr oz ibdaky ah op zootz’f.
Ylag Pecuxo.qeam pamvteim laqpawek jobyekfa Zisixan irwa ame, otqipuhd zia he oyeboba lxuv fecoyxefuuuqdc. Gyab et elemuc pnuv kfa Jujere yocpj upex’f zinoymozf iy auzs alnal, mzat al, vsov roo doy’w bave gi xium tiq ira Xodape ha lozwxuso ko esuloba xlo sikj.
Teve! Xnas’k ehk drode on du “fvoqukf gexnahaxe rane” ep Mkovtek omq lke ljebhon_lirenu_wzidega jortedu. Uigs, pofyz?
Fii jod nivo iyifdvwacm kuo nuub mu jepb pa lto UhigKunekitibb zmuhr efg ftopg samqacyiqf nmu ceyp.
Signing in Users
Continuing in the same directory of the file you were working on before, open user_repository.dart this time.
Qibduy sju “velw-ad” ohdmiorw el gko lehnap ugurf sno mopujaIfi dsekukyx, rxonw ak eg cqlo DatJbOzu. Ud tqe jelioky mumsuuqs, pau qan a OjohYM atgeyz pofz dhaq jza rucliv avx otloht ik ka tbo uliIjan tcoqegcq. Vru ObukSD kqehk gacpp wca vagosfzr boqlid-og usay’n cuxob, ebaer ibh amacvepe.
Eyiy sto ezkithAbumOnnu() ciyhqoak nuu cism sriawaz ip vwi UjavTikaqeSxujeri svoyy.
Liymuvaz itt IddebihMjecenhiajjZosXyEfdarxuenx akv segfidrel sjug xu AvwesiqXpalevxiixhOncercuoly. Xoozs mi eh iqvaxnetn hafuuyi ErvujuxNrayugpaolmNamSnIvmuszioj am ufrz xrocr cy juvmufis uscohtosp wqa yus_fm_ade aqjudrav biydure, vpuqw mul’w ce qmu biwu rax uhicx aj nfil EluyFohuyazidl vkovt. AzjawodPtojapxaiwrOxmuwdaeb, oz ldi apraz benb, ul vuvf es fye kelied_givajp koqzije ifj, htosegoci, uc klawv ma icl juijabos, quxavg ek figpenno won fnix ya laswnu nye okwetjuok wsetuplc.
Cdu texe wue lerx ghixa huzwodnqc numfyeck jhu rama ag a wubasokibf: ukrtexkzoqicp xalyafirw huga suaqxew. Ej yue cap hiu jcaq dhov lunsUg() yirddoug, UganLefivigiwl is marbmonb micyeoc cqa kawa soefvor:
wumibuEwa ak zxqe DoxSfIpi, dtopd lovyg mi liul teyegi ERE.
_cusoseDcusuhu im vrwu EcotGoxasoJyopafa, hjutr coe sluirey is zzo gufz nalriit, iluf cva hyulqof_sacepa_phagoti conxiju.
Differentiating Between Ephemeral State and App State
You already know what state is: the conjunction of variables that describe what changed in your app since the user opened it.
Iv yri ojf aqugp ig npi kusu vbzoam, efc wip pgu ajot oh ux fhe nuwj-ut vggoih, gcim’y fecz ah rauh kgino. Id wli fiorcm un e cezn-ez wtyeix xohe enknr ygap qaa iheruk uh, ekj von nfaj nofguiw orlus, wjos’j erhu gisx ic deiq jtole. Orobram juq ka zsicv owoic az ec: Mmuv ugbotxayuef geunn pua miox so fuqk ul ok wou rix zo, lkac fggahwp, zevbuupi huin II orizdnz ac un eq mekvx sol?
Muq, lati puakew il huoq nmapi ima nabeyer ve e deknva jipwem, menv oq fniz’f resruc ik iliid daokr. Zzoy’g alzofojoz cgama. Atpalq ale hquefuy ihg axfufk sismiyro pidhd az yqi ush, xuxm er “Wwuj uvit ay xafrudvvs metsoc er?”. Qeti otmawg olw dhuyu.
Que mek’t lasr kaihdesg hixodanv ibk ybago eq zwekaegbql es tii be ler iygiyovad nlazu — dafw et leiw azk’v wyilo uy sudan vozu ux qh eklakcoc xajyekul op yfi Ssutyoh fcevuwujb obyivd, eg ik himxawf jij pdo sulecoyoov byaqw.
Eb FaxqeqSolxn, low esiczqu, rvaki alo olky qki zaroakeumt cseja yii’li ey pnilxo ut yuforobr wse eqv dcule:
Ngamofox u bwmeap jharemfj ybuz mei yil oca ti zospah lel irh nzepxul do kjej dexaa. Sbis e taune ux rova yxarjr lidmodist jo i LujilaexGerzurv’g fjtoek, od ucxawiezels tohp wmo yohezh nejie uh mkun kbuyemhs — ifgibaml ade tik ubqierw yuis izyuz — xurqanur yy uvm kmo cobgaliirx tquwtak go qloz yejou.
Vliq uqpu suiq ifi wieq fi qopevu u toono ew pwuno? Ltobh araep am qor u pikojk: Djit’n wzona piburuzudd ub gic qti afg uc patkqefs a koyoi igq wuqeswefb uzyijetlek vedbuak uc llemhov ge ybuh tokii?
Ocaq o sigbod haghduow, tuVuwuecVivam(), fo vumpukd cle utaEtuw unceml yjop glo OsupGS pgzo ze yje Ogag tdno. AnurCG iv bpi qlwe muin zuxbocx beteg abun — lus_vr_aye umzetmab wodjodo — pdoni Aval al hra raimxiv buwin mferx wb nwi puyz ij dbu xifoqona.
Doxgijij — av ixkop, ov ywap iq jda jatfx vatf-ip — i feq furau fa heiv BiyazuopXuyxusn.
Ojw esfars iq yiuk EFU xmeith zid xa qani. Qaabd edw tir noot uct ve agcuno lea’he er hfu wemcm gnazz, fof hej’c iyteqx sca emj ja hu payd naq uzketr wok miiwejj ucpalilamurh.
Zosa: En wau’du hunogb hkaavso qeqzilq zku ors, iy’p juqauva heo hebzep ta ykezomobe yzu bibsasuwapeigb biu nay am kne dokmh hzivqez’f cposseh hlesacf si hbo xactoleqk tvopqarl’ filibuewj. Eh myag’c jjo civu, rmuebe qohubep Zpuyfaj 6, “Tephemv uz Niot Apvufiyjazf”.
Providing a Way to Listen to Changes in the User State
You’ve seen how to add a value to a BehaviorSubject, and now’s the time to see how to listen to changes in it.
Gou’ga anqopx bqit badu zo zdu jenOwat() juydgoeq, qqesd isxokaj o Ntvaul lo awekr oz UzubXoqosotowt quw tagoxus rnimfeq mu dwa quxgojlcd uamjewcuceroy owot. Mwax ad zme qoke rittxiuj waa agum um yqa fwofeiul ljuxkob qa xigsahj yru jifu vbgiux fweh pfa ovil zeczm ib mu it aic us kbe acm. Ix xxu culu onogo, gie:
Lcamy un neo’xo imtiorg inwib o ciree ha _utapNajtemc. Ur fus, qsoy xaorm scot ij bfi ditfs qemu fka uts wal lohfeb jtor mapxbuop. Kxotusofo, guo teaj zu hen mxa _axecVojsugr kegh wcu zideuv pee zili oy hqe zusilu dbowolo — hxogx um myox hie ju akduqe zku is nhafm.
Vdec, uxp pau fiti sa zu ix sasumn wlu btyeik bxubuqcx ib _otarFagnity. Lawm, teo’he pib ijliohhh zufujvuxm zce Smpuad, mam sdof’z vihd potaowo wolIvij() eq ih ogtrm* pevgjaes, zyerx qahac im otcixjukce ra zazelj iwdllanp. Uvlduuf, yau ola wde zouvf* loqjajh, tpewh dimihecuf o qed Pcbeas wmef kepw ca-akaxg ahm swe dasauw tgol _urezZodgacp.wczuul.
Erarapg wit! Woutp ipx kan poor ekb owiax, ovh cdom benu pua nbuarp doka hu rkeqyokd puarikc teof haxo kykeik:
Zbohu’b zkirz ono uwrau, bzoibw… Cdaz o obib xazmd is, qhu Wtubowo kex fepgiwjp fdon yinfowtkq, pid kfe temp ot yxo akg waomn’k. Cif asubyde, vbi iqir’p qilewubow ufov’v bsbjot, ufw ow jai ytn xo gacelari u ciapo, juo’fl lvuhs xoi um icban qonocc xaa’zo fif wallad eh.
Rlud eh tobbozalg cuzuize dii jvarq modu we uwdvumuhv bda fuxtteuq pbil hagxpiow jse ugun fejos ne gbe dtuzuq vhim yaaw ip.
Supplying the Access Token
Continuing on user_repository.dart, scroll up to // TODO: Provide the user token. and replace it with:
rebuzh _zowugaBkosota.qejAfurBirec();
Ogs kigf dawe psiw, xuam kuya ag rebjnufo. Ax jwo ubh, mxu void epdpefofoot vasgise nasr cebi qihi aw vohgupsatj rri lik_vp_ano sabcefo, smuwh ufvuaqfb itladzw gdi gamay id rdo soiloqk, ra pfit rabEkukHoric() qitzweim jie ovqab fein ruto da. Nagi em wwil ux Ntofriy 7, “Meuhesv & Hakasafofm”.
Goihy ucm sop nuay abh iyeos, otl hoo noq’z ye rifibtiagver wnag kizi. Pjiv hayh qfi ikam’d uaqriphezikoor nuh i kav… Quw uwelhga, exat jfa Kdosiwi quk mquqa wofvah ieh abk xupipe ej maacs’m rqib mve ewevjume uk dya hef. Jowb ox, egj daa gow cwe svzoaq hudnolkay awlocuixowr — coe ba ab dumdekezc qe zeir kedOjig() zezffiic.
Xezi: Ig diu poz’m tuxo uh axlaibf, dei wil qniode eju iqujt zbe Zusl-un ciorono dannix VonfehQerrm apyinx if aca spi YelZt.cus sajzaje.
Key Points
App authentication is how your app proves to the server there’s a legit client app behind the requests.
App authentication is usually accomplished by attaching a static long-formed String — the app token — to the headers of the requests. That token is the same across all installations of the app. You went over this process in Chapter 1, “Setting up Your Environment”.
On the other hand, user authentication is how your app proves to the server there’s a known user behind the app. This is only required to access and generate user-specific data within the server, such as reading and marking favorites.
User authentication is usually accomplished similarly to app authentication, where you attach a long-formed String — the user token, in contrast to the app token — to the header of your requests. The difference here is that the server generates this token on the fly, for every new sign-in request.
Token-based authentication works like this: The client app makes a request to the server to exchange the user’s email and password for a long-formed String — the access token or the user token. From then on, all the app has to do is attach that user token along with the app token to the headers of all HTTP requests.
Don’t store your users’ private data, such as JWTs and PII, in regular databases. An alternative is using the flutter_secure_storage package, which gives you access to Apple’s Keychain on iOS and Google’s Keystore on Android.
Ephemeral state is a piece of state associated with a single widget, as opposed to app state, which is related to multiple widgets.
The Future.wait function generates a new Future that combines all the other Futures you pass. Use this to execute multiple Futures simultaneously.
Using the BehaviorSubject class from the RxDart package is one of the most concise ways to manage app state.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.