From f17139f8a70b1d363b03d518809b1f482c142cbc Mon Sep 17 00:00:00 2001 From: Mark Hendrickson Date: Sat, 9 Jan 2021 19:04:01 +0100 Subject: [PATCH] feat: improve authentication guide - Add Stacks.js references to "Build app" nav - Move BNS and Gaia to new protocols section in same nav - Remove Stacks Connect page, merging contents into authentication guide --- public/images/app-sign-in.png | Bin 25545 -> 0 bytes src/common/navigation.yaml | 31 +- src/pages/build-apps/guides/authentication.md | 203 ++++++++++++- src/pages/build-apps/guides/data-storage.md | 6 +- .../build-apps/references/stacks-connect.md | 285 ------------------ src/pages/build-apps/tutorials/todos.md | 7 +- 6 files changed, 227 insertions(+), 305 deletions(-) delete mode 100644 public/images/app-sign-in.png delete mode 100644 src/pages/build-apps/references/stacks-connect.md diff --git a/public/images/app-sign-in.png b/public/images/app-sign-in.png deleted file mode 100644 index abc74e885b76848cb637469f9d97a58d4d0b384c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25545 zcmZU41z1$g_wdp!Ac(L?C@s<~-AG8Mu!3|5OLv2$G)M?b2}rN7AR*ErAtkjmf+#3R zcYk;Bec#{zc|PZ1y?5sHIWu?W+;gL~G?YjP!Gs_Xh~$a#V;v9(8wLVl-p9uUdQQcJ z--AGwNlzZj>iNw6nU*C7frv2~&)=YKHA&vb{rv%e0lBaO2q2IZ3HrANjV6OaUo!sf zjz?esH%Sn5SB>J$NFNw|17-RjUI-%jF90qmq}mb>6Fn|-RTZCwjSVGhIy*!b=R2C9E$1 zIa9H`E`x>!X5hQ@5-NMY&YFfUp(U4(YlU9nmAc&no1RZx>dceyKHsOg&>KBM20YchA=k0GmoysCLZAykmyUp+ z2ZnyRol*z*pAiMx8CvcT6%n~_-O&#pt^p*k#tq4cfX`rb#%iLd9Bvd@Qq`N**0qjO z2Zp=BA@7@h#DQ(6(QKJephsRC<09|v=5OPm>9^YqRj!$g5|;q}d<6j+jSF{iyIRGk zcyr!Ji7bPCn%gzztdv-!FXXb{D6-f%Tfau()k&l50|O+>preAa8$R;jm*1 zusquSODQeligt{zFnJ}8Rmku1m^*G_AcW29;v)?4LlxTPI#jxiWoij5IC2>$=^6K8 zBJ4t6sy%?Vky3S14d`(M*{oJqj&{rp6!WnZ3_c`X>5aEkNALmq03rvAVAk0wH;Q~R zUUkxNdgI${ch@m_MJquL18vx^{+^S>D&6pDNs5ulUC(1F>D&50Ze%|?S5p}1Fd~Bm`S(Pvk-zvJ{cYhKU+6TP zogyFY7a$P5$YFbY3#QxO7U&?S=NY$IfXQ~_l|K(qk6-oI#R_eeyAlJQ?gm%mE>&?; zCs=A!sA+B+15*KH5pRfvTp3+(z2GR8{rnulS0nyF-8(9NbO9Y1fcG{^qlTE}`lKZO z)99(ekeR84Is$?LxmyapiAegpo$q z`F!J)fmcX1fdN%~Y#X2d#>xN8%Kq1@A-0pgP=8X0sO3#?w&Sl)Xteagv-xi9VU}8< zWo=T~5(&>9x^sg8%C2-R2PP>n-;Q(>9XEJTwOoHeA6q}nYxxY%=JOYG@4fBH6uRpgBY+rpx1nZJizyu5OV4?m-jcq4UjV{;p(lrr2qsl1AHz>X|GE$ ze4y;t?7d!O?*S)(d@NM;=C0f&8{TWmgm08c@A{ck(l}xQ^g&}xp7tddAtx2eDI;HO z7pKK7=Rd}?6MvXaI6P`@U&X`@r>rzAvZ0X)B}!9Fis8N2UFI9zf~{FyvczX4O}r;_ zu@tr_Mll1PNekOLQ5>T;{50G_NWxO5D43&%a)D)}NrIieI z7wFw%+T{86wAIN-GSROVWm9x-&vRpKW}pe&ot6SQ%nJ_u^Y*Z!UP3ZJ6hi9|L1I+! z4|VVRu*Y8p<3FT{T+M7D77aPCHiCGJ$`PCW=OlCb%XoMh^F^iSEHZuyoAoBAVSwwjrbRTK4XZ7S`a;Nlf5VHa5N{Oq@| zW1PVk`0z&Bh%1w6%o4jb`|caq?Sa_XhlvX7MYFM%$r1_tz5Es|5DstW}&G^wf-Md8j&WLdbo?BDJE=)@CJk|E?sm6ovHqkQ;+fQ15 zPwV(bo}8~%bz$lXF-6Z$EutE23lq6^YI)e~97PrHv-*fpY_(Ke5nm++_eEU`IZtp=977djiEHHVte=Z>Z4wDJLydZ4IGCdjAD7GrO*4w&2VClEso#cnzz2 z6;Z)QwV9*JR5qAJ#k44&;>?vAD|-um>AcU{x0jY%ZQjQLJmQw$6e==8`F?#rp4=$Q z7J1yrY%#D#L*|eYTc1lZmfOnop;f7IT5#J(;%d9IXSmxAV4MVTNtBS3Y|&y*o#U+P zgRi*|sgdIi3}ZW|I?B~q4b_rUvE5;d_9}^~=^sx)XSzUdb)4$$nY^i#c@n83Oi5>%SS}o``SN5EJ5GNdHaObzkg5XqoPFR>pC`-SWgLZN++sO$c)_2RkSVy6d{*&&F{~$)Ju)H@@1zS5Ggb$e z(>1ILd1S6rn}WyW<8MEiEuiL16z^IhajNbZ*={k_@N&YsBzzm)jgh4 z(iW|WmumSwd^{7@93oTy&g_dOVexNjU4zPWOS^F6-&V)jLtjV4np!Vf)r>QB+|=C? z;Y;)o9q+8s64ctS*^lD+2<_O_b{csPI_GUBY8v|+IciQBj~+cH%T__@&3f&) z7l~FDE_7ro8{_$&1f0J$PDk`_8U^L<`g4x#e7(1BR6W4Q1Qjf0bD<$ z;Wb$QgPzIWVS(1@x)_Mxp0|DAB?XKSqPs$yQhrcu$ZJ*@omz|*@~c=XiiF>t9gpUN zE7p?uZ(XL1@B^oHrIoo9ODcUW#VY->q)0C@w%G3*J;z1#v+Gg!D&qY0)+KA)qUzaB z``zQ*@9hd`HJxX9kTCbdHr#LhP>~GCGp4b&u#Wr$7x{J~Wp%_QJ(7_r(Zx41_{Br^ zdH<7sJ3RxBi(dt+L6eO3fOJr-o97{?ycNmycs9XfV5;-tDxg3-=yO;Nu67u(^)Mrj zL15e*rD|}8ne3llwmQJ33`JA;Sgpx`$A6scP51&!lrcPTkkCiFpeN5j?aQ<%r=@4+ zpzOqd6mO1X8WM-UcMn&d-!H9|9s4`fJ`{#NuY~AEgL6=Nz|#YLf5Wdm;t{KZZWbWR zM_*v4adTV~c~hzl-QUXeeD$CBpb$$7*sv83>qXFH=G*FhRv)zVz-9s9xWwS^?J_Gu zhGRC3w-9jjoa7y*yijX7_S9Q&Xtk`r7lnPN{>PZq@C^PVDx!2gvDY==yCMn<5Kodyu#_^Qr{vy9pl)4!&x_TMqCJBm7ZJ1BSE*$DbJBM z48uD*tLw19kCkyY!|tE(?!ayisjIIb+do&t&d8Y0^poTiC^{9hLX}7!dQa6lQB-b@ zE$U)eQFK0;m}>O&d+>{U(2+i1d%<|kgY+Q^I4gp76rvjB-RDW>RuY#3W=U2YjWwI{ z@?29J_ntv@QR%R!JHgEzJ4I5-{C8lpNbaVIK5c}%6~VsaqB!>JL`oSrrN--fHpNBz ziSF6slD~M4wy2fjsR^-oKYQtpyJKjCgbP{Ms|?Y&;SxTj(P7s4D5FwIrXW}`Z+3RT zT*6p`O3p=8KQ?>pwS3O3`p8mZ(Sqf0vvTV?tZS8XA}^BU_48CX>YEca_$6UGszLqglYXHq>+l5 zK6&Yvhxz9IGcaeoXz0cx-uWM}gyG7{mm?1bZE4q@CFo|3*OuVBQ1om^!b>2fNL+JZVS&CE8ggG=I$VCuwV=yIYm%&h z&tISKc|SFS-q+)pzd>Hphi~I|L)B&XL)iDq%pD;9##_2BnsTr0XjhS>*K7RETb&!S zt9B$05wgI9l3^(&zzOGB9BaW5YVH|-YvGcDm0YY-#bPg4TFoP6u=&O`5oT#SZt-TA zd4vlUJM-hXu(G6em^;8Gvo~vX*Gv9?8p>HK4Sn{Wj$5jqr(1BQUddlGJgURJg9-uE zUy_5^ParYKQ1~!1Al!`qOq3ihvp`B7W1!x4f+c`6pfKmXk;~9$bMYXAYy`wd3)m_R ztuzNJFZZcB*f}wQ&`zYM1chXY6`u#SFg!Fqz1SL9UYXMji-6cUX+&ZEi}t7dG%CF_ z(>ZH9%2Ya-;Yy_{uk}iK>Q5yG8e(3|oFr+oO*UwJI5)A_vEdQ|p5=b78mMFX?blvZ zivY?I9Mg%!bIxr!$JxV9Cc9&l3$m^#ck+!ScC>?bwa0WPkub0BVB%8|@jd7f=i3Fb zDrGCT-D=Tibkgtr#zr^U*m!h!yW-SsxDnJl_;WmKD4+lHWA%h%1BP4a5U)faneq)- z?Rhgu6=oG1oU?OtQ8=JDov+hXHBkZgtKB!juV_Ic^=m8n4}PxC3~Wt}m@Xs;$2R0x zPlz0u@ch)02sUrmn|mQ~B1;1eL%dvBySNBwtSX%uwid<+NiIG|Wj&ndC} zmrnBj3$tJxP@Rteevt)9!InkQN;@ zdKME`yykmelDhx1F3Gfw8*`cdp|~O-RESQg@jFaV(7FiTJ$67stU1d@MOMf{j6 z`t0hb(%N)&e^#p%3CpI-qZ-AZUwXb}^`AWBcNHToJb=~lFP<4%Zx3YDG!3iZS#OR_ zY34uXEF*jE>zL7?)3IU+i(TNW!G&U_%4uWb@LFgyk%TiyI5+}_XX1GJyfD<89+liP zDhN1$C&V%Z^3d{&k(Ig|IX~&X(MY%5dUub^S!~nM7k}>cqhBgK5t^}D~(pvuau2(fS z#8P zEcj!dwGU4CKT_?=-hrQf)9WHtDM7>$t!ER|uk|Uq>JN&E36mh)0+<5HjM}a2@yZ!} z5`r8~Ma_0UtB4qkA|7SZ)meS>aMMh2$(wmq6S?pf*jY4@Di>fAhOB1b;i~gugxLO) zj*QMw{&6{pm-{{hK@O#O!9t;6r7rY=rG{|IR0XQDH`OEO_tk@+ek$_|WR5Xb$SXVg zHXs|u$2zp>LR?)>fA}3`FvmwO7y&^Q`o76jGT4!SwGt`nut3rWpR-mklQa!QMAu3Q zv-yKJl?rKuGk2f#A)+0AT=XQ4zYpz2$2aJRE23bAt)0(#{PKWR@+s8vSkV~=^j1_{ zeIA3qrq}~oLpW#ujRi)fL#Y3(uWoKS&ai-RMpXOOkU*wTmRirq{K$KcsE1a7oz1p& zN>A)_@5hZaX1u*eZ^pCqT8strVT{#F zPAwN)=7-^(bPsghK_WO6$=M5RTnV?$!&goNitv=>GEKf!fQU1qd5m zu((Qu&zS8jVW!M5VqIA0VXc3m(+F1FZy z2!m=O5g1o#avU!)@6CNw>$@>rrFQl0``s2uvXRlR2dAgg;ORwOt2gK zt$9=ACXm@kg8XWtR3V@=n{d#q=_!SRG^RxKvCtU%o`VV88#-=h8yAm)xj5LO*!&6D zl~97>%rH1M=;yrAf;^v3dhFV>o~+gC875dCJm&$A5C5lb)?z#Y7iro!MD&x0Q3=Vm z*f_|_m6`LA<%{#Ii`?60Ij^~D0u1SsAXe(Tq{4D2kX3xNmQaTrDsF@M$mQ{nbq;7F z6&Q_=qP-78t5!`w1-+|2FJ34rj?k3@L}rAABVL}*thpbbUpYT*Zf$-a^rqL3GpvSy zibDfs)ge(;dG}jgN&l44B>{>9ZMz#j5keh*7;|ez?~St>zGH_{L=1UnBm1YA?agv- z18nXCR58BLEzoJAy1P>#ir#bBv4}K?-}EnStQ&zy`oQ;LAzbGiznaV_9?78&_*mA< zPrvNW7fW@Bu>dBMN8v-U{)l|YGe~)U`fYG~lGQLAaoV+bIWKq%cm;92H3RFTw=?53 zB~wX?CIfUZ>bmq9F?HPcPPvm8)cyrV9;+g@gEWGcmml zCO41#jwdG9EVnq1X+r+TPR)`#BDa!-Q}#D@Y!YPj>NG3#Cnp8j8qA$xwfN-?92Pn} zSQT9hd8e&tJwp}x;2g18_asmDUf6s3oNJer_h0`MYUGl|@>J;q*Wdz*2t0k-cGp$= zI%e{UEOYxjICJe=mf=-n{MXU00hfz>(>F7UIcDTFHVH}mS|#Ct$&Di(bfX}3z-C2`C(M2Mxd*bN%|x!fG)0*uUwH0uoT8y z4FbT1Nf0@dhmsUZF9zI+TcS0X%==a^UIPi3R_4Y}7*Kb4RJS2?vRAG>KxNv8*}m1G zs$M$MOVTFG1JDi(T1STuuq4seuqrBRP-l1k9OxHy;I%Y&Z=+8rZ8TN-g|-!F;wCf# zLXOjQQeBJGQG>2}>NJJ0RdsG6e<$q}NBm#dL{Ka<2hVc5gdy3UJAfH8iCZsoaCKip zI1F@ZJl^pCP+D;pN5=Dx8)c6OclkW)*K2falHm# zTl-#&H`wKM_1pud1wK>&DwJG@!3fJ%`iDUhB#(J+KZzmU+C)4vmYw}-k<(VnrvE2x zuj#wDb~CHp`UU01BR?D0TGuI{Ojaa*?+`>AQ+Yq{k_>-7-%b;~0Q`=g0Da&K+wF_M zAFKr;tr6n7G6nkVZypqJypXApd57g)>!d~b9K$U$RIma+y6GVSr>s2A?b&rPW->Pn zIzh(Ms+~O7d13JTR*A&~Z=~>=QDEMIm48rkrt6Z;(aNz6HcYVt?&oG0_O<*}S;NI9 z@co^McG}BnHiB&%aWHDh0jhm0bBFg9#cQ6l%99wjHc7pmSV7cOhFWnwbpGxpO zQ@BVo9kn6ru7j`ajpZK9bef=LA zN+^;_FS4jeq0sSzC7Jv&qK@OkJE=qLjxpi;K`tg1x=Sw1L2D5r_erg_o`xtQ6(8O~ zJi*;IP)t^oYyYgd=KoZq!Cl{0@+s5NRF4hSYT9D$6uePT>G`psk=n$zHxGs0*#+Q%(1s|nc6bdOUDsH>XJWbMK?F0vzh}uc|Zlz8* zsXaR2Df>qJg#U;5{*TBpx7-vRb9IX*$q662Ivei!hr}vK#SM}UFuFyX4GFq!D+vD{ zUD!{lTQJO^N{{kstTk>oYERzmeEA?t&u~R1>48n`-6}zD6T#!*f?8f=&n9V_`J6)= zR2dugPNxGd+8-m_Sm!?0jV$K(^oNbtZI3WGhHz)93%hHP)>&#qK%zHSu`_&vhUMpo z&|ELY2~qe!i=)cUb)$QVRDAVvfz+Z@6Lgy$EAZlI<(}Qg?=(ju;TX$Anv2q=x9GNE zoB6|sxCJC+KNaU3#e2@@tpC=sb6Iwacn2b)pht$sf&b`;TL?f=#UJn5pen+ zkWm_jBFs(cxb=+dwz>KSSDdFug;~7bLqh>^^H*9avu!h**hm|i_XD@9vXY}-IMx;& zxzpoKX`%(*>&>G-y^un??O>bTkO^o4;IVEED|6n?e0-`(-fWN|O%08B-RI6*f7JOr z2#I^{8wwOj3@wqnyRpfB7AE5Ev;MS*_0T>m4?4U=8uDpImsBB zG}?Sd_tOs9Bm9E5mAb&M0ecT~xN?=w&w|y_V#w8eb#gu$D8q&&5WrTYdvj(bxNd}T z$3o(6LfNsl@0M=THXBAdOt1L3Iy;agOxFK)%WYtmCad7UG@e%8d#lad4!^=RR~P@mAwK&dvbT4$xavOGo{8eW zsZ5TbA%VHnq}{(4L)54PZijPUZRM!k z7d}MR}t>ybu?%6>OZO70c(qD2z=h`15M zFa^3}ewIozJw}JLRbQLtEHzeXqM@7G+`MZ*a1%D@&;f#nFsugDgGo!o5NSrjsPaee zF0N3i_=yH6Fpjr&xrRE?Y=}67L}my!o%JGpY;5ZD{3AkPY|F@_L=L;;sS#=v<;2=n z$br7qV5V8T*%05s8bk0*#9d>F#_2a+D`&l$!38`~>}Pon*Hxz1t!Gyo3|H1`4Flh< zb+-!f7`7LOl6wa)X{^_-jE+r@j)ynAE4Nx*?;a~tAVg~bg(wM)nBvDt+U@WacNU$z z3a`#yAtUEzK&ctJ2tSFevyEvxtqY7UT2qTAoV5O&>K{E%>yrLzhW=IKz?b~a%SMV~ z22to<1&AMLL?&!nlhRI8Aw)HlCp!G6=l!dnH~aFfx$@*p@1Z1km}K6`1>(|uAPI|U zYFkTP{Mi2a`fL_WD_ab)9E1@UFedO_Feb%T18ZABZ67$K=gp#%r31-O!vYPm~j9}9SODWZs9G{W_cytWxXb2-^w^9BEVhhSh#S+=S zF?K=9OOY^}ko`jtALi2Rwf@o4_a5dQqKMlFn&2}E8R8J4<`2Au3HyWK)Y%7=jNzFl4n#cRj?v8;63%COUim(<=80Wt9xA~`NR z1GQXi?OA6!x1LdXYNgMazm?f!mS?TS+Rg|Jlv^CW6(yN}Z}wK8jESINbZ-L8XSK$d zVe0v>JL}=c;3!B?U|dHA+<#zrfkekp+pGI(QS+*bCC|R@B!io+4%J^9A+d1BR1!l z#j*x61rr96@j=Y9O&pq$tQ#UBzcxEfq=f=vx+)w3eYV2up%`z}pbb)$i~c{VU0Wl9 z=QRQ0FS-_I_C<8C%`@6(9)xS~d*|POyvJh*r7jW@CvY`shcGNleu?aCk_y{DBRhyU;a5MF6ETr7rZWfLhdtfcyi~Mtr zn#fB$B@{@aHaNy))Yn~NB2li8*W?ar$*6bWCFdqXV%n_R{6u|Y8z$Qk1@)}HM~3Ax z9#gFOP;wWVQzYcyMrur;@@Cd181HN5q?x+v?CoU1XDh{;hFCB75x?(DyikYVDbvXb z7{B(gdLZW4Yaxn{=ntcHdPPbM3t=isrFyU02|K_j9le4Wj-TI~;PC(k% z&%_BeE&Q`><0|_V*zOP#-|bK$LA{_Pkts4W+8mi46~c3I3%W3~nt2n~Vs&J0u1+vd zjrzB}nlR8VmrtM7nX9d9zui;*9(Pg)3y-!6ce44@xE!NO7N&)=qu<$?-18buf7eO& z#jatCYL5`eA?^3TE^xaHHW^qp`E8V<5lDuzbg9Z3Qo4SRT60-t44(|d;Cf4RlMqHN zteDyj=B_5);h>_r<=!WEE5Wh0i6;aDlY`ej0uq-4tD?(0dA`(3sz^0p*9k8rTCaZo z{CPYvtQnkY9uKr3$(~AuZ}WFiZ{KN5u1}_##hrE5Qx##U>HM-X)HNODkW1xWiY(}E z7JB^42f8MZv^J7w>VjpvSxkH4M(gA@*P(um#1%>5Ofu3rvz)q%811`CHz4^!n`+yqpBmHSegIzne9+V6W&JbU7l8nYAQ$M=@a%{-F*eqZP=P%v!OyIRQ6?K=Tz%Tvir3pRi*cSThR3!vge=1 zT1=|_Au=sruppAM2sQaukdBanMGNai;|L$dL-TUwUU7c}Q)EtisMq z(~!ve!c+YM!T2XkFV{ghzY2GXtZ13uorObQNT68GE0ZB@K|l8uo{Hjq93FJ}fgNla z?9rm(8ob&Eu>af03!YI66w~Dss(^`zM+&7QqzI$Dic}SvK2|wa9}Rsq8yes-3F_=O zXFpq1lD^vCijr=j3k^Ct6=q;K+gA zRJ|!MDAwM-jeL=gDr%|=L6&3E*$<^AM4!ciO1%b+KiJr6FLOXFaSIumGfB64c&j+g zhRHkOsu$F#?1zXlEVz6ZymsC*9GkD;0V-Y=gCwvWyb7Wo6MaFcnO)IIcV?z^5Id`| zH146M{6WP`d6_Kl?ltwr zn?{YbX(h5ZO?)yR`rYq&7Q_|_S;K=Jh!utOsCwrek1bTS`X9b0dQS^1UVotF$l}7< z&Rbn@|1aZt1~RYj|Hj>j$Hj$xKZa153A}$9GZCCSR_gVfQn5L){Q2i&!5NdDiT9w4 zdbx8#)6Afi3ES#Xn$}}}%>jBdb>pqt%hi6rPb!r0iskRv3o~Ml*ufhZ`3`Kae&7|3 zr(_z3IsJS_&Lr(TepHGHs~xdWI)B(AJTz!sHY)XN;bXdqnfhiTHW0{gy$?NgOe8Sk zn|4KNZ(<5k!Yz4cL&AXinqWdH^I6YHZEDPx=Bzr9_K#1?qm$fM3mLeF_ve1qooGGB zHT~r{t$3DIQq+*Obnh)6qS{8M(gLjC>ax2GQJm^71xr)sup*#Jh7=E;8q^MWu6zQ) zdZxoS1|A1+6_NU7?m{T$8oV9ThdUt;@m>(age(j#|7*Hww$f_2gfOQBW)geotA{kv z3nb?RUXR{6a>DfftWGo2Rcv7VcaxMTzCuizExzCO!iZ>mma_ z#I9)ne~VJFe#irYq>p_r?k|_Ei%bUI*%*X8f)5LhO6}s-v4j?ZV60E{ovp^r_{-;adyzeB%;*Iq_V1G-_|Jm0Q=trX z7shE|{4hVXhTq*N+{NEWw^AWG~ z2uS2gtDYXNHP>iZXQAwVhf>>7F%MSrW`8238O=5MgwRg58#D=$rV!T;Zjud@y2)Jv zIkTZ7t0C)%oG*h?#QEI)aLej>ACt`ER4+9f;Z+U~tFG+p0>p1!yoRCix~+1yJ}`L= z?llk0ttmyR>DH5aL7+zE2iZW)3gt}}56HlQYivFf5nnq!%{&}{Ka#3J*b&xP;J5vn zTyDPY%B-2U_JikDmgPt8DN)su3sKGLlmJc1O-u#Ut0V|}ZADjx>hJl3PxS8D$r7F~ z!l=;sshczv(iggabW)%7t{XDh@>Bh1xSkcsQ{deJ&!2F??&I-`i{&2QUctepk1o#C zMY;_50{@^Iwl zFxv-Gp+l00fFZb$2fpguQ}1O%cgbV^@kWQRlNrsm2vcxHL=EXO+}FSm61ey*v|Dhc>w z_|a5ScaeKhoX)wmAlEDM0qn5^^)jx068EuAW~eIaMB~9Btc0%Zb{yo|5_wqziXPz-zhKrR_5a3LpEY)8#;G|A48#N4D9 z5l>MP($Z5^RaGS%q`)f(z#E`{0tWbR>D)@4N|1+#=gsc+dFt-0Ac1OyVdOOHt*^skk16ZegM+PS2f|(8|ZycTH3*eSTHjZ#7oGR5D3THmyp+H@id6!tIOOj`rr?4NnFvrB9pHXZoWIaWMH*!BoY%&uN?{D+;U`I_agao0Y>R30MoAJj zj2NZ-j1c9+y|?a!CLdqi0YCbh8+lz&ZD*jLh9wgU!G$rS^Ho5iqHshy47|TzV!rIW zz3lE>6$ZJD;N$!|ipK3R=DnwF?gMEMhxOpWm<=tTDx))yH^N=6PcH1{reQb%8UV*> zK=5qczfxntQfXkV5wrk%00rO%aJo@c+Y9leOMP6hDMV2C{~vsccw&(L^P!2BF^~sD z_5gYSXYjvf7&Tl`CHSWkV2|#`2ki3%*<%*}?lf2TvQD}OFCc3Q5Cu9R-#Qcw0Q@E4 zra=SP7Y%1N1bkm0luw3R$O~V5&L1gnIMX8*v1Ij~Ftj_GnP{5uYOS4aNtSUHMt_1Y zL;-7;v<7ODn1CXAR^V@mX<7||F|Uihwuq00{M&P!8V%e7U>U$)NTQiQHLiZt_H9GGcHn7jQ`c*-~`9H_i#uh{s=cD_Bk*c{spmMC|v1j?l#J*l31#;2cEnBsRH=Q20;V!D{lg>%Bzb@&dRnbfh+Xj7Wxw$L|MtFCg1}6 zKbU_ zuf7pKGTXic+;pSyzg@W5_J061WDp%?J%5ez-$)49nO9sr%ajT51T(qWf z#`wt7x6h=19lpFW)iROpXy83Hqq>SV5!9Xi#jX4rn#J3U*EMd=x%nT-%co4fdJOFd z+VTXxd5Z=zE3#H6O#glN`jm6zivVZ%x@U&QyGfqJl6h58&y;bY8yFUAb-5+YDv=zC z7Z3D!+Os>|uO6=KKX2qW0F)u(hPh!f-ru>$`Az@69?#IhwZ@$LpTbmGU&t3@jDTwI zUNLP2S?Z8&)vx>XJrUi6oT~D6LquO4zt`lqW3#`CfaI|1b$8$8P45||e}wP}f%U-q zSTERe{ZCtegugZ#q}sO~z6T1)jDSoUqom>j1WtEu-D+|WoOCC$7n)T~A$7bpaR;br z#O{O#xMxZqw&4iD*J-%OTTe~fir}l-XdwmgVVD$ZS+bJGu6{`QLw@99Z28mcBD8~9sZdtAYs1nT zl5flEOIY_14W9sCXJ{GjdsPQPNuDQ`v^vV@Zk~-7j<|U@z6Vf3*qn)Bb@&i*f~2<2 zWlw;jUa@`6VP0M(TC(;O;YfA!a+|~NKF#NW!s&Zt6$wsfU<&#LrVLTRmeRz`6aLrs@y`1hZ2*t#ALOuRc-|N*b!!dJpI)i+{ zNsQ-h#YbC{za`tUF23-XGLPPH6heJByESxKv0;INy3L0fIH)}M~YJ(Tj@zAFN2G>8!a{TL@p01Jlxu4 zWZfT>qKZ#7-QF39;BzqZEnhPWhKD#(>zwpcgFsYO2gn|!DcnMm_3c+#hC}gjTBQ`1U;rgP1V^do$LU|rN zi$Pi*hPpv)x{hTsCGg&SRszY!8**mF=J9`m52FmcxhEqDJ<4s>QY<#o_F2Mn|Du_3 zQy9sy>3Z%Tv58F%nz~;Xj7t6;;oV2}U}YJ^A+>X`A>*@74cY@!Q&Z)gI*QXTa^VSN zWN5C7?7%V3A)~NZd7ZI}YC3&(JxkxLP@8)MuI6*yL>e~@%o9*Z12OD^OZ)P7a@E>X zx=A0uiXp{VuG?dN0xoK${C}ny&cy9~xfY13`HAcX017#6d4cECy#;3{Yqo9Qcg4_* z*!dA0ZG~kudaHDFUZ(HH{mwWm>jK?}e>XW_($z_5YKdse7^XPJMwlg*R}Dlsxd%N` zDeUvO)}-^{zk3@^;SnHeLNR8#|LW4$>WA{y3#aUH>4i9KobO8-0SY1MAJe}AX%kJm9(O?6HR|Gq>a@} znTV%}tzzVys@jCdt-IbO@)8nOT=1Zf^4d@S63elKZ$I8g{-E{w1{=(K2iY_0gH&pF zlbdBLzFepE^z+S06aGqNG$LJIuljQOD`a*CvUfIIF7UnzuhJ-|UIEqsLl8sf>Rzbe zJ5ibyf76~Q%`$3Z844`a7ChtHO-Fx72t+K+CI?-AfZ+$#JZT9^<+<63UFk)3OG#P7 zmBSp*Y#ilYzMr#R&U2qb@k#T%rewN~_RW|LFGr8Wjgw@pup#D-av8n;U|?xWEu8y zb19Q-b&^uGU^>)^Emu#yrx&-Q&5y5qjzsiM(-5$($s0%2)|R^GBTE?YoZ$D{0#oc~ zLOlLU*P}jx^_ymETpNR;esiimmd6g2c@L@XCvzPHlR5kRToN;kk`ylUZZm@@F)l|(Jfr6kGZ`2kyG0} zC8Q)LxfuOpB8cBJwk&-8D34>V2ah}4vXTW$wTQ_Y-{^Qjb<^xj@R>`q#} z5ECYbQB5S;_#!$Kn-82QQ7!=qU-(eE zQvJwPDPjqekKD2gcQvo0rNF8A3Wi-F*!RamcS4UfIV%S|1F31QpIANZO%99Kx^O6jBh^)F$jP!Kj*KEYuz9`;L2!D%yU zS@KygV)C6obOH|PDkpq9(0-Q)fv1FL1flTm{k`o@DN+&i`)fV$lj}Qg8b4cqR{J2a<%y;UY)t_7j8Sdjba%4Y z80@_M%(Qzcto)D9>p2uSz9*#beGL4)2T9+jy>T1{nxil zznePYKJq`W2wkPtMzp-bAAHRF_Dk&JV!_e0LsnK4Y4p$M9*ga)O2mg*e#{dJ)T zHFTN`6$gYqIGq1EHA*YsqTRI>SL7d!Av8RW)tKKGIAPSyUp-jn?QX$Th^`l10@zh+ zG(;Lc+5R*60&TIuM-MuFw_EY3brzDLpR z)-On5v%RZ}EDGF+-{h5XvcGn9&JINCam`1{ zeR$KGEN#dm=2ZG@mRuHep6znwV%PMA^e0zk1F2kA?(w7G;Xl0_XnywOQKNS12IMUq8rSk+<|JW>mPm%d%=mAbd^-|vwDaGKc*Pn&8#;+hXl zosbVO_~C^U+UilMoh*O3uFKnLEEDAJT9QQV&Cs1)z_2*=eI$>FdIBCz86o>0p0V_# zXWBYEZj+~x|Hd1|LTF!OIUt$nMJi2y)_Y!Zxw5eTdt_mMcXw*#D8>2LyyZBT+y(_O>R>5h@PqImD|uPtv_|lBLlfO7QXrei4W%LEBCw% z>jmrLxUMB;!@yYoc%2X7E1$`5P35=9w3zjm!+RIa=ZYk_7Keln0 z@wMrK_lRciW(Ib7C;8F8|G&r=!Nm7|g}epbULWwdWw0e_aAcS{+XX+Ho?W9!Y;(QU zq@x{X#(GU>m8YbtWogFr*MyM}22Mm~XoLLW&uz}55YcqhO z6U)1U(|h~x{OZ)aZZ$T@0%DWG_RySx1e;J<@`e_5U>JPz~}AV7aEIK}qq&UFuB`DU*%MmG>z~a)2J1Xg|0gUUQEfjL$FnEQM3D=BctHzg0N3xjzYZ z|E;N~jsD-^D5(@C3$H{x3!*~-_9j2hUd+H3n?7{WTvGH>Pd>^_V1JqGJKG*xRiqKB zn3>!EaIJ)YzcuOn{QPob17=y|>*pu77jQPXKQ+eFZOrU+cs`J;bm<6ie+{vqV25=p z>~0)=>v^nN`c4hrh!}lcikan4biWmcl$2C>KiW;qi^VnXeYUBIiC?Z}p6^x6yU&eQ z^XtcVr4ny!63$w5&9*3D?e{8O>&ZazBTn1v^Q|^n|LK7KZH3o1V9Wd5SSs-ytQn7T ztcHXnq@?K8Vjvu}@->PzihvD(@>9eq6?0jQlkReZ>u>lmg4SBcao@jsd^?5gZj;B# zpR}f?riD871Rft6N|v#H3#k&R7c{BqvIO_TlMv*LAFEUgt+n0|)L5!e+Dv5&kxI4N zlIDFLR!IruUOC66`;#5mM!oi`UIO|-$)DoLa5g{w|Fw4I@lbzXpJv`@UUU%+t?zx}4oa5ee-j`JbL9Jh_z8Kz(5=Yl9ZEW0;Pqa-`LW?7p&p&03G~`4x zU7~KF@ZG!bTPj2R!6LWeXqkN(?J=fh%SjTx6QmF2ZgaKoWO~J%ug1x-O-$5LW;vWm ztdPVK`6sK-6=M0Y_E*BtsVP*+fw=v-m+)1VZViV_5z#UZxQKh*(7j zXq3Nx%DG!a(jKpodTCY_Av@|SuYs0JIno~PY%6MSPw*&=Hrs#Y^{;NFLD!b_L1VYP zMW3LjKdfGi-HV+bU!^1NM>J$LDVx=`=5TL>@m|F0hZhN{oc{#xqLj=ez_{7>OsGk& zJx0<9MLj;gyBYD$`3Kvej+AQt;+j(X(IT=xqP;iGexTzK)?e*0}SC?tEnB9Yv6d1RWvFSvsIh6R~DsPU9>C@;PW;id;4&=NdJC`j) zg`+r=KwK=n3;J=myn&m?8z^B zX|B)47hhm(f4U!0iNDWZ6P&>(cZ&nUCN~9sq3$~Vyi)$UlSOb|)el_^x4m7R+3_zS zfeyn&T6+hKBQiUpba8c1Gp@3;qXm{BKs%FF z5?LQz@Y1HV+g#FV9NY5M*2Cty!*UYnnxlD#@OTGGc+u0lRzJ|3sL-E`;RD84bOR&A zFF602lqwM<{p^(Tkr9r2LAypx3Vhvjo?s05m;FZ4uQ+8bKAv2%H*%LoftyRCjW{FE zovs*9dwZBl9BeyP^5zu;q|AE3xBGdrm_7eb`e6$P!DP_=&`mMXp4)ebCa-hV4y*f< z6@z~760olKk*MXBlX~Dg#LVG|EzrnSq9|v*cMa$1(3vAqrBQI=s#h|w@XAeYKj09M z*@(|oRE@uwc|Vqru2TaM7ci*J3&*r0<{ofW=aLi+XgzY`NZCt-$Lr4bRBs2Q13*qz z9b?GwW~p26#@rxHo#`8(k(P8&*NtOk0#N4W4Qw@fUDG#h*{siYz1ByD%^@;=sic=X z75Q68Ae{_YC|Rre(-g2leSZXYPhdN-U*zSJuUyyG0}aO!53ce-nz(rIDd87h%t0(^ zxZ6aF{4N?9*Uvdd*em$rU&KsQOG zI=7^)DNNyoAx*2bT*o)LWt)%D2x1k^;`?$G`f|M&IAw+Q*%T1?Joh9Q@7C3sL#2L} z^4*_pSHc(DIL@Zjk;T<^<=q&Y;O7HApQMg7GZfXCZsRcSSLFJn!htH$d{WK{}Mr9(^2o)LW=mGlwO|^*f%@-8*IBjmDSen%8%Icyr;E#FzEA@MwMVJ%P_D zSp9~MNqD;DfEKnTHVvXn6eT5rP{FxU&iAzpA<7Q$Xu?o~%TR|f4vFb``3U1z0Q;EQ6!s;Bh2^4Rs^J>xMyK(`OG1Uo z3;2Y}&taAN1s597DIk>jC&)LUM5XM*zlvY(Plce*3b7Hd!y>KZ3f~uy#(V}1KcxhN zm$1_5-T7ZvRi=%2GVIM9et7lf*&heaPOEvCK6$XN6So4#$M{%uiZM%aBdxLCm7n!#8{*4q1lkFe2(ZhK{&bIIwIQEph!C?4=f=$rN8EVFY*AR)aehV?MVTcP_kqSQuh zfXiC|K}eT5(4$spt=Pbt(%t)6#Gmea;d6PD@bXzO^kwJ7EFD~Cd1Gq|y_sbNwo zXluhWe!=DaVsM4e$%|QF9kcSd5mCdW1kjdX>AE*|TMFqJlwk5T8Wkyhn?|sn-duZ# zD$0`CnbSM`@4weCeZ4OZy@y3Ssv)f}Wx?Fq;tjjf+N+DQCwa;uM9c;8@k<#=Tt#`~gpQetF;!n(3*kkWEc{km z$&+WD-hbSNGofu@-0ys$ti}ShvW`3pq0Y(GqNg_&0pdW`>L{NIT(0!2ACw6L(nMP@38u#CAcbE(p@rLcN=zXYH; zYb;8uwk9bj&B6}W?%h{>Btk%r$fDhLc5h;s+pdiAO$r-?`g!2tMyvmUID{c%jk@S< zyarq_=v#z-qVi~uWKH)^p3bmtKp*@(DStggmw)9!eaLsddzj^!uH)}~TLReX8#X+B7)Rz?TU(D&@~f!)V@*Om zj3Z08RpwsD^g>BFLqwm?3%%=Z1L7VvghW;rTW-N_n|jv0JZhu)-3dwyR1_W37u4VV z(Nbm3rrXWv*i6;2b<~`8(5BnKD0vMB{tqLf0gVCe{sffzz9a9mzBMR;84xY=Y=tvcq10fT-(5~~ z{a`8yOVheUpkE9hUW=>Y)f#P32qg9eQc<6NW%1f{j^(Qya~B)9QC{}dWhK9k+ZK#; zy{hTg#dyNXsjEvO2LmzJw0%W>;!t+ly?Q8q_cI(Y4d#ZAWDL8}>aZpY#ZhKt`;`>-#$ zRz(Asw_AXezEwN9fvn3?Dzch6GP}A`-0`K>iBwPl4-i`KE2d73dO?RAW*nVw%OM$+ zHG6g@0Pr;=CSAhTF(|0TO`#tgyWOwSTy;MT<6N-?EZlqI9Va~|+FN2-PGj|FnKI5I%@QB>LxOn4Gt*VcP<;Q|zmb;Ua!1t|I*Vnu9t%#J*{)?4X#1i*mt3#uF$yto#7=|g8F&=Jz5pXIv z|KNwrtEd!E&-9O^`2B#H(ZqDKVXQBhksLJV3++I6Xvw;@`@>IL1&~Fzb%^IQAyaVD zI*Lc{&JeI*AZ+a2EOX~sd}Xxu(Xsl?AqQqS$A#&dQdK7P*XPIX1|9V=Y8%Pn6@!Z< z{^)q#eLu-^)eG$lO zGrnR+Blz7&CHH*uW6nn%3rEpr<(FA9!6hYAQK^T7n}=qRkR1Bl+CW)hfS+d8*@IAA zV^HT*=5kspXnkLjNNgL*fL3&n5RCv6ebZCXBxzJJyvwC{tgE(c0Q5fL3|*BeGar3B z@@R7p3w?9G;JPSRbKCcX8bNGc(wVJnC~s$n@R(Up8oyRYZcW`2i&T&rxt;cI6M6<| z_U2q_X5~q~lG%IH@IWwHW^>U;leuSVZ62^;p^2~zKj!5%Zxc=xAeyOEUnQF576aV2 z`?j8}%WY~{a~p*{PBl8|5hI|y*ef%6w`5Ola)#$=!8f`; zGd1?+ery<*ys^R91@Y*VOpNh=6L+Ibl3{bT+InyR8mXkpF-dbTmiw)_1xxr!`|e@( znfDOCa6xg0a575|ZQtvhSahPyUE`ar$SAA>$-r@Zo38*dmW(l+To1j)LIjlKZCs73E4#8_6x?%tHJhejOE+NJwpl?*&H5i#M?{b`ha`eCal?L zZ@G3jDMmfilQfP)`OT0A=dTIKx!fr}b7)U50Oin9*`1B7If?XiE-IY~xJ8|9ef_bu z!JOph2)>nF*anD%zvOv-*vl2OS8PEeNK0n;7yC^*5>KpW-#>mtENAB0!?y=FpGvOr zaT+Y=Y}WW!>)d5TpnGE3Dc5u~u%czzZK@gh>GiPexz$b7s+Rqz2XyI4?_6yRQEwrO zjs`1aQZpF3-#Wutr5;0p~9 zQGYjq8Yy9E*S!I|P-*GSAOBV1Xpe!t+SrL-toKH<#r%T-+5WdL*X{upd41GpS?X%G z;w+D4?`_}e2>KfOgX?u@SXAuf#?i(&( ztg@Dkhbp@XkFBt(AMaN26@IwdrI7A>Mpp^aix%im2Gk;^HP6z0zm{5H_`*RJ*-F3p z{2NI@C;};u00SzU{}h_5k+q(npXq-49GM_0Hy8{<1EBt;50rp3- z=fciDzR0vy0OA^9OW`|U!Mw<}z;oJ4eao)gf7(mhemh`~`8g&GkYoWD8F+EC<;&zu>T^Uod2QVP0ajV;SU7@3g;}kqaIjz7w$e5c|V; z;lDrso#oJgB`+q+xB?(Si|M{@oBhUkB5!i0kK4cUF63%Y|D7D}B6}n#J2^ z9MQN-eQfv5s7Xu*UG4mIZudbAWk@2tOlCXo;9W>l)rDYu@=VSMYg1{`f#rTYFFxXS z%7d8E;adzTY#uwi>SFqhkR=tFAbvTd{sQKEtyc`dI+T`S7g=4$*rnS`uJPn)YdF<0 z+e#lE=_42Yne(PZj3x2i*SkyOw)lA7D%|7U-sN3O2~ruO5*M|Vl@g=e1vjOPc@=M+ zfrF0@2`s1X3Mtp*XhzKMALs)P@?OV@r?jCksb2o-2KuoY$;%QA1DsC{VpZVK=gKoY z3sECojT@uRt$F`6cZ2vEbEUEImA24#zTVvdMqEMpERc0qllMh-0u+n0?gpmww4cpp z`(b|V`AIia(VQ(}6#Hw(^9OQl)?BZ9ZDP@)B-xVN;}TEw>AO{3mG|B$$;IZ){PTh3 zQ$W;Dnl;LnKY2MBFEQ;NU1HZ!r-(%joic2upBd+6CCwt9Wa|`GA2Ei3V`MHjsk{mq zQ#MuNw_iK>Q$RLgVI?a#c4X+3a-#1Lq0ti7$DEvaKQ$1U%q;!gc6#voXa4tU*U?vWPHq`c2qMlRVjqC*OS!JZ9o!Q8<&=u(E zL-BnE_QxXf>&gK1*6QIykY0akSyHuu+3VV>pg!WLRUW!%33BX1OGx}9Py;F(>p@%f(WKU0gs%gfP>9p zU8C8m9_M^16r3cIy!A6bN!7XGhFKYl(cPOq^u~>#-e52Q^+3;u`5hGR+~+@w1ocYdK&N0!3W1(J3q79j9nvhY zuDbSbpwMC1q<#Y3BhW#n)3gJ(&_SzN%Y^YBV^yTP%nsIQwR&~O_sE?oq57#8ppk*w z=jR^IkEDXO3hzwS+tKH}G7oNZL5kT2EMmz;j`MAdOa zC+RP}UvQ6(r_&sUe-A6uynpEd00>;3KuIVlLO6g2a7WxyY*rGA5cvqw@_lr;2veFFntl20od|)pgDnmH+H;Sm}!8g0)RmS%o11c zxkpSRYMsy9Br($;dOM<9{XYWuKeGH!0?y%Y-4OPX { - window.location.reload(); + let userData = userSession.loadUserData(); + // Save or otherwise utilize userData post-authentication }, userSession: userSession, }); } ``` -## Handle pending sign in (still needed??) +`showConnect` triggers the display of a modal that initiates the authentication process for users, one in which they'll authenticate with a _Secret Key_ that's used to encrypt their private data. + +![Modal displayed by showConnect function](/images/todos/get-started.png) + +The `showConnect` function accepts a number of properties within a parameter object such as: + +- The app's `name` and `icon`: provided as strings comprising the `appDetails` object property. +- The `redirectTo` string: used to provide a URL to which the user should be redirected upon successful authentication. The `finished` callback serves a similar purpose by handling successful authentication within a context of a popup window. +- The `userSession` object initiated above. + +Once the user selects the button presented in this modal, they are passed to the Stacks Wallet for authenticator with the `authRequest` token as a GET parameter. From there they can confirm authentication and generate a new _Secret Key_ or Stacks identity before doing so, as needed before coming back to the app. + +## Handle pending authentication + +Unless the user has confirmed authentication within the context of a popup window, they will get redirected back to the app via the `redirectTo` address provided above, at which point the app needs to handle the pending authentication state using the `authResponse` value provided as a GET parameter: ```jsx import { AppConfig, UserSession, showConnect } from '@stacks/connect'; @@ -50,14 +121,130 @@ import { AppConfig, UserSession, showConnect } from '@stacks/connect'; const appConfig = new AppConfig(['store_write', 'publish_data']); const userSession = new UserSession({ appConfig }); -function componentDidMount() { +window.onload = function () { if (userSession.isSignInPending()) { userSession.handlePendingSignIn().then(userData => { - window.history.replaceState({}, document.title, '/'); - this.setState({ userData: userData }); + // Save or otherwise utilize userData post-authentication }); } else if (userSession.isUserSignedIn()) { - this.setState({ userData: userSession.loadUserData() }); + // Handle case in which user is already authenticated } -} +}; ``` + +The `isSignInPending` method of the `userSession` object is used to detect whether the user needs to handle a pending authentication state upon page load. + +The `handlePendingSignIn` method is then used to handle that state, returning a `userData` object with all the data needed to save the user's information into their session. + +The authenticated state can later be detected by the `isUserSignedIn` method in case any particular handling is needed then. + +~> Note that implementing `handlePendingSignIn` is especially important for supporting authentication within the context of mobile apps. + +If the user has indeed confirmed authentication in the context of a popup window, the authenticator will resolve the pending authentication state automatically with the app within the parent window. + +It will then trigger the `finished` function provided above, which can be used similarly to save the user's information into their session as retrieved with `userSession.loadUserData()`. + +## Key pairs + +Authentication with Stacks makes extensive use of public key cryptography generally and ECDSA with the `secp256k1` curve in particular. + +The following sections describe the three public-private key pairs used, including how they're generated, where they're used and to whom private keys are disclosed. + +### Transit private key + +The transit private is an ephemeral key that is used to encrypt secrets that +need to be passed from the authenticator to the app during the +authentication process. It is randomly generated by the app at the beginning of +the authentication response. + +The public key that corresponds to the transit private key is stored in a single +element array in the `public_keys` key of the authentication request token. The +authenticator encrypts secret data such as the app private key using this +public key and sends it back to the app when the user signs in to the app. The +transit private key signs the app authentication request. + +### Identity address private key + +The identity address private key is derived from the user's keychain phrase and +is the private key of the Stacks username that the user chooses to use to sign in +to the app. It is a secret owned by the user and never leaves the user's +instance of the authenticator. + +This private key signs the authentication response token for an app to indicate that the user approves sign in to that app. + +### App private key + +The app private key is an app-specific private key that is generated from the +user's identity address private key using the `domain_name` as input. It is +deterministic in that for a given Stacks username and `domain_name`, the same +private key is generated each time. + +The app private key is securely shared with the app on each authentication, encrypted by the authenticator with the transit public key. + +## authRequest Payload Schema + +```jsx +const requestPayload = { + jti, // UUID + iat, // JWT creation time in seconds + exp, // JWT expiration time in seconds + iss, // legacy decentralized identifier generated from transit key + public_keys, // single entry array with public key of transit key + domain_name, // app origin + manifest_uri, // url to manifest file - must be hosted on app origin + redirect_uri, // url to which the authenticator redirects user on auth approval - must be hosted on app origin + version, // version tuple + do_not_include_profile, // a boolean flag asking authenticator to send profile url instead of profile object + supports_hub_url, // a boolean flag indicating gaia hub support + scopes, // an array of string values indicating scopes requested by the app +}; +``` + +## authResponse Payload Schema + +```jsx +const responsePayload = { + jti, // UUID + iat, // JWT creation time in seconds + exp, // JWT expiration time in seconds + iss, // legacy decentralized identifier (string prefix + identity address) - this uniquely identifies the user + private_key, // encrypted private key payload + public_keys, // single entry array with public key + profile, // profile object or null if passed by profile_url + username, // Stacks username (if any) + core_token, // encrypted core token payload + email, // email if email scope is requested & email available + profile_url, // url to signed profile token + hubUrl, // url pointing to user's gaia hub + version, // version tuple +}; +``` + +## Decode authRequest or authResponse + +To decode a token and see what data it holds: + +1. Copy the `authRequest` or `authResponse` string from the URL during authentication. +2. Navigate to [jwt.io](https://jwt.io/). +3. Paste the full token there. + + The output should look similar to below: + + ```json + { + "jti": "f65f02db-9f42-4523-bfa9-8034d8edf459", + "iat": 1555641911, + "exp": 1555645511, + "iss": "did:btc-addr:1ANL7TNdT7TTcjVnrvauP7Mq3tjcb8TsUX", + "public_keys": ["02f08d5541bf611ded745cc15db08f4447bfa55a55a2dd555648a1de9759aea5f9"], + "domain_name": "http://localhost:8080", + "manifest_uri": "http://localhost:8080/manifest.json", + "redirect_uri": "http://localhost:8080", + "version": "1.3.1", + "do_not_include_profile": true, + "supports_hub_url": true, + "scopes": ["store_write", "publish_data"] + } + ``` + + The `iss` property is a decentralized identifier or `did`. This identifies the user and the username to the app. The specific `did` is a `btc-addr`. diff --git a/src/pages/build-apps/guides/data-storage.md b/src/pages/build-apps/guides/data-storage.md index 08ea1cf7..ba94adbf 100644 --- a/src/pages/build-apps/guides/data-storage.md +++ b/src/pages/build-apps/guides/data-storage.md @@ -11,7 +11,7 @@ images: ## Introduction -This guide explains how to save and retrieve data for users with [Gaia](/build-apps/references/gaia) by implementing the `connect` and `storage` packages of [Stacks.js](https://blockstack.github.io/stacks.js/). +This guide explains how to save and retrieve data for users with [Gaia](/build-apps/references/gaia) by implementing the [`connect`](https://github.com/blockstack/ux/tree/master/packages/connect#stacksconnect) and [`storage`](https://github.com/blockstack/ux/tree/master/packages/storage#stacksstorage) packages of Stacks.js. Data storage provides a way for users to save both public and private data off-chain while retaining complete control over it. @@ -19,6 +19,10 @@ Storing data off of the blockchain ensures that apps can provide users with high See [the Todos app tutorial](/build-apps/tutorials/todos) for a concrete example of this functionality in practice. +## Authentication + +TODO: Add indicator that authentication guide should be followed first + ## How data is stored Gaia storage is a key-value store. diff --git a/src/pages/build-apps/references/stacks-connect.md b/src/pages/build-apps/references/stacks-connect.md deleted file mode 100644 index fe7ee248..00000000 --- a/src/pages/build-apps/references/stacks-connect.md +++ /dev/null @@ -1,285 +0,0 @@ ---- -title: Stacks Connect -description: Open protocol for connecting apps built with Stacks -images: - large: /images/pages/authentication.svg - sm: /images/pages/authentication-sm.svg ---- - -Stacks Connect is an open protocol for connecting apps built with the Stacks blockchain, such as consumer apps with authenticators and wallets. - -## Authentication flow - -For an application developer, the application flow is similar to the typical client-server flow used by centralized sign in services (for example, OAuth). However, with Stacks Connect, the authentication flow happens entirely client-side. - -An app and authenticator, such as [the Stacks Wallet](https://blockstack.org/wallet), communicate during the authentication flow by passing back and forth two tokens. The requesting application sends the authenticator an `authRequest` token. Once a user approves a sign-in, the authenticator responds to the application with an `authResponse` token. These tokens are JSON Web Tokens, and they are passed via URL query strings. - -![](/images/app-sign-in.png) - -When a user chooses to authenticate a decentralized application, it calls the `doOpenAuth()` method which sends an `authRequest` to the authenticator. Stacks auth passes the token in via a URL query string in the `authRequest` parameter: - -`https://app.blockstack.org/#/sign-up?authRequest=j902120cn829n1jnvoa...` - -When the authenticator receives the request, it generates an (`authResponse`) token to the application using an _ephemeral transit key_ . The ephemeral transit key is just used for the particular instance of the application, in this case, to sign the `authRequest`. The application stores the ephemeral transit key during the request generation. The public portion of the transit key is passed in the `authRequest` token. The authenticator uses the public portion of the key to encrypt an _app-private key_ which is returned via the `authResponse`. - -During sign in, the authenticator generates the app-private key from the user's _identity-address private_ key and the application's `appDomain`. The app private key serves three functions: - -- It is used to create the credentials that give an app access to the Gaia storage bucket for that specific app. -- It is used in the end-to-end encryption of files stored for the app in the user's Gaia storage. -- It serves as a cryptographic secret that apps can use to perform other cryptographic functions. - -Finally, the app private key is deterministic, meaning that for a given user ID and domain name, the same private key is generated each time. - -## Scopes - -Scopes define the permissions requested by an app for granting during authentication. - -Apps may request any of the following scopes: - -| Scope | Definition | -| -------------- | ------------------------------------------------------------------------------------ | -| `store_write` | Read and write data to the user's Gaia hub in an app-specific storage bucket. | -| `publish_data` | Publish data so that other users of the app can discover and interact with the user. | - -The permissions scope should be specified through the [`AppConfig`](https://blockstack.github.io/stacks.js/classes/appconfig.html) -object. If no `scopes` array is provided to the `redirectToSignIn` or `makeAuthRequest` functions, the default is to request `['store_write']`. - -## Manifest file - -Decentralized apps have a manifest file. This file is based on the [W3C web app manifest specification](https://w3c.github.io/manifest/). -The following is an example manifest file. - -```json -{ - "name": "Todo App", - "start_url": "http://todos.blockstack.org", - "description": "A simple todo app build on Stacks", - "icons": [ - { - "src": "http://todos.blockstack.org/logo.png", - "sizes": "400x400", - "type": "image/png" - } - ] -} -``` - -The Stacks Wallet retrieves the manifest file from the app during the authentication process and displays the -information in it such as the app `name` and to the user during sign in. The location of the app manifest file is specific -in the authentication request token and **must** be on the same origin as the app requesting authentication. - -The manifest file **must** have [Cross-origin resource sharing (CORS) headers](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) -that allow the manifest file to be fetched from any arbitrary source. This usually means returning a header like this: - -``` -Access-Control-Allow-Origin: * -``` - -How you implement CORS depends in part on which platform/service you use to serve your application. For example, Netlify -and Firebase have two different ways of configuring CORS. Consult your vendor documentation for more information. - -## Key pairs - -Stacks Auth makes extensive use of public key cryptography. Blockstack uses ECDSA with the `secp256k1` curve. The -following sections describe the three public-private key pairs used in the authentication process: - -- how they're generated -- where they're used -- to whom the private key is disclosed - -### Transit private key - -The transit private is an ephemeral key that is used to encrypt secrets that -need to be passed from the authenticator to the decentralized app during the -authentication process. It is randomly generated by the app at the beginning of -the authentication response. - -The public key that corresponds to the transit private key is stored in a single -element array in the `public_keys` key of the authentication request token. The -authenticator encrypts secret data such as the app private key using this -public key and sends it back to the app when the user signs in to the app. The -transit private key signs the app authentication request. - -### Identity address private key - -The identity address private key is derived from the user's keychain phrase and -is the private key of the Stacks username that the user chooses to use to sign in -to the app. It is a secret owned by the user and never leaves the user's -instance of the authenticator. - -This private key signs the authentication response token for an app to indicate that the user approves sign in to that app. - -### App private key - -The app private key is an app-specific private key that is generated from the -user's identity address private key using the `domain_name` as input. It is -deterministic in that for a given Stacks username and `domain_name`, the same -private key is generated each time. - -The app private key is securely shared with the app on each authentication, encrypted by the authenticator with the transit public key. - -## JSON Web Token signatures - -Both the `authRequest` and the `authResponse` tokens are [JSON Web Tokens](https://jwt.io/), and they are passed via URL query strings. - -Stacks authentication tokens are based on the [RFC 7519 OAuth JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519) -with additional support for the `secp256k1` curve used by Bitcoin and many other -cryptocurrencies. - -This signature algorithm is indicated by specifying `ES256K` in the token's -`alg` key, specifying that the JWT signature uses ECDSA with the secp256k1 -curve. Stacks auth provide both [JavaScript](https://github.com/blockstack/jsontokens-js) -and -[Ruby](https://github.com/blockstack/ruby-jwt-blockstack/tree/ruby-jwt-blockstack) -JWT libraries with support for this signing algorithm. - --> The Stacks JWT implementation is different from other implementations because of the underlying cryptography we employ. There are libraries in [JavaScript](https://github.com/blockstack/jsontokens-js) and [Ruby](https://github.com/blockstack/ruby-jwt-blockstack) available on the Blockstack Github to allow you to work with these tokens. - -### Example: authRequest payload schema - -```jsx -const requestPayload = { - jti, // UUID - iat, // JWT creation time in seconds - exp, // JWT expiration time in seconds - iss, // legacy decentralized identifier generated from transit key - public_keys, // single entry array with public key of transit key - domain_name, // app origin - manifest_uri, // url to manifest file - must be hosted on app origin - redirect_uri, // url to which the authenticator redirects user on auth approval - must be hosted on app origin - version, // version tuple - do_not_include_profile, // a boolean flag asking authenticator to send profile url instead of profile object - supports_hub_url, // a boolean flag indicating gaia hub support - scopes, // an array of string values indicating scopes requested by the app -}; -``` - -### Example: authResponse payload schema - -```jsx -const responsePayload = { - jti, // UUID - iat, // JWT creation time in seconds - exp, // JWT expiration time in seconds - iss, // legacy decentralized identifier (string prefix + identity address) - this uniquely identifies the user - private_key, // encrypted private key payload - public_keys, // single entry array with public key - profile, // profile object or null if passed by profile_url - username, // Stacks username (if any) - core_token, // encrypted core token payload - email, // email if email scope is requested & email available - profile_url, // url to signed profile token - hubUrl, // url pointing to user's gaia hub - version, // version tuple -}; -``` - -## Decode authRequest - -To decode the token and see what information it holds: - -1. Copy the `authRequest` string from the URL. - - - -2. Navigate to [jwt.io](https://jwt.io/). -3. Paste the full token there. - - The output should look similar to below: - - ```json - { - "jti": "f65f02db-9f42-4523-bfa9-8034d8edf459", - "iat": 1555641911, - "exp": 1555645511, - "iss": "did:btc-addr:1ANL7TNdT7TTcjVnrvauP7Mq3tjcb8TsUX", - "public_keys": ["02f08d5541bf611ded745cc15db08f4447bfa55a55a2dd555648a1de9759aea5f9"], - "domain_name": "http://localhost:8080", - "manifest_uri": "http://localhost:8080/manifest.json", - "redirect_uri": "http://localhost:8080", - "version": "1.3.1", - "do_not_include_profile": true, - "supports_hub_url": true, - "scopes": ["store_write", "publish_data"] - } - ``` - - The `iss` property is a decentralized identifier or `did`. This identifies the user and the user name to the application. The specific `did` is a `btc-addr`. - -## User profiles - -Profile data is stored using Gaia on the user's selected storage provider. An example of a `profile.json` file URL using -default provided storage: - -``` -https://gaia.blockstack.org/hub/1EeZtGNdFrVB2AgLFsZbyBCF7UTZcEWhHk/profile.json -``` - -Follow these steps to create and register a profile for a BNS username (`identifier`): - -1. Create a JSON profile object -2. Split up the profile into tokens, sign the tokens, and put them in a token file -3. Create a zone file that points to the web location of the profile token file - -```jsx -"account": [ - { - "@type": "Account", - "service": "twitter", - "identifier": "naval", - "proofType": "http", - "proofUrl": "https://twitter.com/naval/status/12345678901234567890" - } -] -``` - -## Create a profile - -```jsx -const profileOfNaval = { - '@context': 'http://schema.org/', - '@type': 'Person', - name: 'Naval Ravikant', - description: 'Co-founder of AngelList', -}; -``` - -## Sign a profile as a single token - -```jsx -import { wrapProfileToken, Person } from '@stacks/profiles'; - -const privateKey = 'e546ba96ee34220287d0c177418011addf8d71b32fb81ae8e33a1d7510fa5d0d01'; - -const person = new Person(profileOfNaval); -const token = person.toToken(privateKey); -const tokenFile = [wrapProfileToken(token)]; -``` - -## Verify an individual token - -```jsx -import { verifyProfileToken } from '@stacks/profiles'; - -try { - const decodedToken = verifyProfileToken(tokenFile[0].token, publicKey); -} catch (e) { - console.log(e); -} -``` - -## Recover a profile from a token file - -```jsx -const recoveredProfile = Person.fromToken(tokenFile, publicKey); -``` - -## Validate profile schema - -```jsx -const validationResults = Person.validateSchema(recoveredProfile); -``` - -### Transaction signing - -TBD: info on transaction signing protocol diff --git a/src/pages/build-apps/tutorials/todos.md b/src/pages/build-apps/tutorials/todos.md index b156c4d9..1db9980d 100644 --- a/src/pages/build-apps/tutorials/todos.md +++ b/src/pages/build-apps/tutorials/todos.md @@ -69,10 +69,9 @@ You should see the app's landing page: ### Step 1: Choose **Get started** to start onboarding into the app. -The app displays a standardized introductory modal using -Stacks Connect. +The app displays a standardized introductory modal using the `@stacks/connect` library. -![The Stacks Connect Modal](/images/todos/get-started.png) +![Modal displayed by showConnect function](/images/todos/get-started.png) This modal is displayed using the `authenticate` function exported by the `src/auth.js` module, which organizes all Stacks resources needed for authentication in the app: @@ -109,7 +108,7 @@ export function getPerson() { } ``` -The `authenticate` function implements the `showConnect` function imported from the `@stacks/connect` library. +The `authenticate` function implements the `showConnect` function imported from the `connect` package of Stacks.js. `showConnect` triggers the display of a modal that initiates the authentication process for users, one in which they'll authenticate with a _Secret Key_ that's used to encrypt their private data.