A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 33KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang=fr>
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset=utf-8>
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name=viewport content="width=device-width,minimum-scale=1,initial-scale=1,shrink-to-fit=no">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>The God Login (archive) — David Larlet</title>
  13. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  14. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons/apple-touch-icon.png">
  15. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons/favicon-32x32.png">
  16. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons/favicon-16x16.png">
  17. <link rel="manifest" href="/manifest.json">
  18. <link rel="mask-icon" href="/static/david/icons/safari-pinned-tab.svg" color="#5bbad5">
  19. <link rel="shortcut icon" href="/static/david/icons/favicon.ico">
  20. <meta name="apple-mobile-web-app-title" content="David Larlet">
  21. <meta name="application-name" content="David Larlet">
  22. <meta name="msapplication-TileColor" content="#da532c">
  23. <meta name="msapplication-config" content="/static/david/icons/browserconfig.xml">
  24. <meta name="theme-color" content="#f0f0ea">
  25. <!-- That good ol' feed, subscribe :p. -->
  26. <link rel=alternate type="application/atom+xml" title=Feed href="/david/log/">
  27. <meta name="robots" content="noindex, nofollow">
  28. <meta content="origin-when-cross-origin" name="referrer">
  29. <!-- Canonical URL for SEO purposes -->
  30. <link rel="canonical" href="http://blog.codinghorror.com/the-god-login/">
  31. <style>
  32. /* http://meyerweb.com/eric/tools/css/reset/ */
  33. html, body, div, span,
  34. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  35. a, abbr, address, big, cite, code,
  36. del, dfn, em, img, ins,
  37. small, strike, strong, tt, var,
  38. dl, dt, dd, ol, ul, li,
  39. fieldset, form, label, legend,
  40. table, caption, tbody, tfoot, thead, tr, th, td,
  41. article, aside, canvas, details, embed,
  42. figure, figcaption, footer, header, hgroup,
  43. menu, nav, output, ruby, section, summary,
  44. time, mark, audio, video {
  45. margin: 0;
  46. padding: 0;
  47. border: 0;
  48. font-size: 100%;
  49. font: inherit;
  50. vertical-align: baseline;
  51. }
  52. /* HTML5 display-role reset for older browsers */
  53. article, aside, details, figcaption, figure,
  54. footer, header, hgroup, menu, nav, section { display: block; }
  55. body { line-height: 1; }
  56. blockquote, q { quotes: none; }
  57. blockquote:before, blockquote:after,
  58. q:before, q:after {
  59. content: '';
  60. content: none;
  61. }
  62. table {
  63. border-collapse: collapse;
  64. border-spacing: 0;
  65. }
  66. /* http://practicaltypography.com/equity.html */
  67. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  68. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  69. @font-face {
  70. font-family: 'EquityTextB';
  71. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  72. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  73. font-weight: 300;
  74. font-style: normal;
  75. font-display: swap;
  76. }
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: italic;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  89. font-weight: 700;
  90. font-style: normal;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'ConcourseT3';
  95. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  96. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  97. font-weight: 300;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  102. body {
  103. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  104. font-family: "EquityTextB", Palatino, serif;
  105. background-color: #f0f0ea;
  106. color: #07486c;
  107. font-kerning: normal;
  108. -moz-osx-font-smoothing: grayscale;
  109. -webkit-font-smoothing: subpixel-antialiased;
  110. text-rendering: optimizeLegibility;
  111. font-variant-ligatures: common-ligatures contextual;
  112. font-feature-settings: "kern", "liga", "clig", "calt";
  113. }
  114. pre, code, kbd, samp, var, tt {
  115. font-family: 'TriplicateT4c', monospace;
  116. }
  117. em {
  118. font-style: italic;
  119. color: #323a45;
  120. }
  121. strong {
  122. font-weight: bold;
  123. color: black;
  124. }
  125. nav {
  126. background-color: #323a45;
  127. color: #f0f0ea;
  128. display: flex;
  129. justify-content: space-around;
  130. padding: 1rem .5rem;
  131. }
  132. nav:last-child {
  133. border-bottom: 1vh solid #2d7474;
  134. }
  135. nav a {
  136. color: #f0f0ea;
  137. }
  138. nav abbr {
  139. border-bottom: 1px dotted white;
  140. }
  141. h1 {
  142. border-top: 1vh solid #2d7474;
  143. border-bottom: .2vh dotted #2d7474;
  144. background-color: #e3e1e1;
  145. color: #323a45;
  146. text-align: center;
  147. padding: 5rem 0 4rem 0;
  148. width: 100%;
  149. font-family: 'ConcourseT3';
  150. display: flex;
  151. flex-direction: column;
  152. }
  153. h1.single {
  154. padding-bottom: 10rem;
  155. }
  156. h1 span {
  157. position: absolute;
  158. top: 1vh;
  159. left: 20%;
  160. line-height: 0;
  161. }
  162. h1 span a {
  163. line-height: 1.7;
  164. padding: 1rem 1.2rem .6rem 1.2rem;
  165. border-radius: 0 0 6% 6%;
  166. background: #2d7474;
  167. font-size: 1.3rem;
  168. color: white;
  169. text-decoration: none;
  170. }
  171. h2 {
  172. margin: 4rem 0 1rem;
  173. border-top: .2vh solid #2d7474;
  174. padding-top: 1vh;
  175. }
  176. h3 {
  177. text-align: center;
  178. margin: 3rem 0 .75em;
  179. }
  180. hr {
  181. height: .4rem;
  182. width: .4rem;
  183. border-radius: .4rem;
  184. background: #07486c;
  185. margin: 2.5rem auto;
  186. }
  187. time {
  188. display: bloc;
  189. margin-left: 0 !important;
  190. }
  191. ul, ol {
  192. margin: 2rem;
  193. }
  194. ul {
  195. list-style-type: square;
  196. }
  197. a {
  198. text-decoration-skip-ink: auto;
  199. text-decoration-thickness: 0.05em;
  200. text-underline-offset: 0.09em;
  201. }
  202. article {
  203. max-width: 50rem;
  204. display: flex;
  205. flex-direction: column;
  206. margin: 2rem auto;
  207. }
  208. article.single {
  209. border-top: .2vh dotted #2d7474;
  210. margin: -6rem auto 1rem auto;
  211. background: #f0f0ea;
  212. padding: 2rem;
  213. }
  214. article p:last-child {
  215. margin-bottom: 1rem;
  216. }
  217. p {
  218. padding: 0 .5rem;
  219. margin-left: 3rem;
  220. }
  221. p + p,
  222. figure + p {
  223. margin-top: 2rem;
  224. }
  225. blockquote {
  226. background-color: #e3e1e1;
  227. border-left: .5vw solid #2d7474;
  228. display: flex;
  229. flex-direction: column;
  230. align-items: center;
  231. padding: 1rem;
  232. margin: 1.5rem;
  233. }
  234. blockquote cite {
  235. font-style: italic;
  236. }
  237. blockquote p {
  238. margin-left: 0;
  239. }
  240. figure {
  241. border-top: .2vh solid #2d7474;
  242. background-color: #e3e1e1;
  243. text-align: center;
  244. padding: 1.5rem 0;
  245. margin: 1rem 0 0;
  246. font-size: 1.5rem;
  247. width: 100%;
  248. }
  249. figure img {
  250. max-width: 250px;
  251. max-height: 250px;
  252. border: .5vw solid #323a45;
  253. padding: 1px;
  254. }
  255. figcaption {
  256. padding: 1rem;
  257. line-height: 1.4;
  258. }
  259. aside {
  260. display: flex;
  261. flex-direction: column;
  262. background-color: #e3e1e1;
  263. padding: 1rem 0;
  264. border-bottom: .2vh solid #07486c;
  265. }
  266. aside p {
  267. max-width: 50rem;
  268. margin: 0 auto;
  269. }
  270. /* https://fvsch.com/code/css-locks/ */
  271. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  272. font-size: 1rem;
  273. line-height: calc( 1.5em + 0.2 * 1rem );
  274. }
  275. h1 {
  276. font-size: 1.9rem;
  277. line-height: calc( 1.2em + 0.2 * 1rem );
  278. }
  279. h2 {
  280. font-size: 1.6rem;
  281. line-height: calc( 1.3em + 0.2 * 1rem );
  282. }
  283. h3 {
  284. font-size: 1.35rem;
  285. line-height: calc( 1.4em + 0.2 * 1rem );
  286. }
  287. @media (min-width: 20em) {
  288. /* The (100vw - 20rem) / (50 - 20) part
  289. resolves to 0-1rem, depending on the
  290. viewport width (between 20em and 50em). */
  291. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  292. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  293. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  294. margin-left: 0;
  295. }
  296. h1 {
  297. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  298. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  299. }
  300. h2 {
  301. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  302. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  303. }
  304. h3 {
  305. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. }
  309. @media (min-width: 50em) {
  310. /* The right part of the addition *must* be a
  311. rem value. In this example we *could* change
  312. the whole declaration to font-size:2.5rem,
  313. but if our baseline value was not expressed
  314. in rem we would have to use calc. */
  315. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  316. font-size: calc( 1rem + .6 * 1rem );
  317. line-height: 1.5em;
  318. }
  319. p, li, pre, details {
  320. margin-left: 3rem;
  321. }
  322. h1 {
  323. font-size: calc( 1.9rem + 1.5 * 1rem );
  324. line-height: 1.2em;
  325. }
  326. h2 {
  327. font-size: calc( 1.5rem + 1.5 * 1rem );
  328. line-height: 1.3em;
  329. }
  330. h3 {
  331. font-size: calc( 1.35rem + 1.5 * 1rem );
  332. line-height: 1.4em;
  333. }
  334. figure img {
  335. max-width: 500px;
  336. max-height: 500px;
  337. }
  338. }
  339. figure.unsquared {
  340. margin-bottom: 1.5rem;
  341. }
  342. figure.unsquared img {
  343. height: inherit;
  344. }
  345. @media print {
  346. body { font-size: 100%; }
  347. a:after { content: " (" attr(href) ")"; }
  348. a, a:link, a:visited, a:after {
  349. text-decoration: underline;
  350. text-shadow: none !important;
  351. background-image: none !important;
  352. background: white;
  353. color: black;
  354. }
  355. abbr[title] { border-bottom: 0; }
  356. abbr[title]:after { content: " (" attr(title) ")"; }
  357. img { page-break-inside: avoid; }
  358. @page { margin: 2cm .5cm; }
  359. h1, h2, h3 { page-break-after: avoid; }
  360. p3 { orphans: 3; widows: 3; }
  361. img {
  362. max-width: 250px !important;
  363. max-height: 250px !important;
  364. }
  365. nav, aside { display: none; }
  366. }
  367. ul.with_columns {
  368. column-count: 1;
  369. }
  370. @media (min-width: 20em) {
  371. ul.with_columns {
  372. column-count: 2;
  373. }
  374. }
  375. @media (min-width: 50em) {
  376. ul.with_columns {
  377. column-count: 3;
  378. }
  379. }
  380. ul.with_two_columns {
  381. column-count: 1;
  382. }
  383. @media (min-width: 20em) {
  384. ul.with_two_columns {
  385. column-count: 1;
  386. }
  387. }
  388. @media (min-width: 50em) {
  389. ul.with_two_columns {
  390. column-count: 2;
  391. }
  392. }
  393. .gallery {
  394. display: flex;
  395. flex-wrap: wrap;
  396. justify-content: space-around;
  397. }
  398. .gallery figure img {
  399. margin-left: 1rem;
  400. margin-right: 1rem;
  401. }
  402. .gallery figure figcaption {
  403. font-family: 'ConcourseT3'
  404. }
  405. footer {
  406. font-family: 'ConcourseT3';
  407. display: flex;
  408. flex-direction: column;
  409. border-top: 3px solid white;
  410. padding: 4rem 0;
  411. background-color: #07486c;
  412. color: white;
  413. }
  414. footer > * {
  415. max-width: 50rem;
  416. margin: 0 auto;
  417. }
  418. footer a {
  419. color: #f1c40f;
  420. }
  421. footer .avatar {
  422. width: 200px;
  423. height: 200px;
  424. border-radius: 50%;
  425. float: left;
  426. -webkit-shape-outside: circle();
  427. shape-outside: circle();
  428. margin-right: 2rem;
  429. padding: 2px 5px 5px 2px;
  430. background: white;
  431. border-left: 1px solid #f1c40f;
  432. border-top: 1px solid #f1c40f;
  433. border-right: 5px solid #f1c40f;
  434. border-bottom: 5px solid #f1c40f;
  435. }
  436. </style>
  437. <h1>
  438. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  439. The God Login (archive)
  440. <time>Pour la pérennité des contenus liés. Non-indexé, retrait sur simple email.</time>
  441. </h1>
  442. <section>
  443. <article>
  444. <h3><a href="http://blog.codinghorror.com/the-god-login/">Source originale du contenu</a></h3>
  445. <p>I graduated with a Computer Science minor from the University of Virginia in 1992. The reason it's a minor and not a major is because to major in CS at UVa you had to go through the Engineering School, and I was absolutely not cut out for that kind of hardcore math and physics, to put it mildly. The beauty of a minor was that I could cherry pick all the cool CS classes and skip everything else.</p>
  446. <p>One of my favorite classes, the one I remember the most, was Algorithms. I always told people my Algorithms class was the one part of my college education that influenced me most as a programmer. I wasn't sure exactly why, but a few years ago I had a hunch so I looked up <a href="http://www.cs.cmu.edu/~pausch/Randy/Randy/Vita.html">a certain CV</a> and realized that Randy Pausch – yes, <a href="http://en.wikipedia.org/wiki/The_Last_Lecture">the <em>Last Lecture</em> Randy Pausch</a> – taught that class. The timing is perfect: University of Virginia, Fall 1991, CS461 Analysis of Algorithms, 50 students.</p>
  447. <p>I was one of them.</p>
  448. <p>No wonder I was so impressed. Pausch was an incredible, charismatic teacher, a testament to the old adage that your should choose your teacher first and the class material second, if you bother to at all. It's so true.</p>
  449. <p>In this case, the combination of great teacher and great topic was extra potent, as algorithms are central to what programmers do. Not that we invent new algorithms, but we need to understand the code that's out there, grok why it tends to be fast or slow due to the tradeoffs chosen, and <a href="http://blog.codinghorror.com/everything-is-fast-for-small-n/">choose the <em>correct</em> algorithms</a> for what we're doing. That's essential.</p>
  450. <p>And one of the coolest things Mr. Pausch ever taught me was to ask this question:</p>
  451. <blockquote>
  452. <p>What's the God algorithm for this?</p>
  453. </blockquote>
  454. <p>Well, when sorting a list, obviously God wouldn't bother with a stupid Bubble Sort or Quick Sort or Shell Sort like us mere mortals, God would just immediately place the items in the correct order. Bam. One step. The <a href="http://bigocheatsheet.com/">ultimate lower bound on computation</a>, O(1). Not just fixed time, either, but literally one instantaneous step, <em>because you're freakin' God</em>. </p>
  455. <p><img src="http://blog.codinghorror.com/content/images/2015/01/god-you-asked-for-a-sign.jpg" alt=""/></p>
  456. <p>This kind of blew my mind at the time. </p>
  457. <p>I always suspected that programmers became programmers because <a href="http://blog.codinghorror.com/bridges-software-engineering-and-god/">they got to play God</a> with the little universe boxes on their desks. Randy Pausch took that conceit and turned it into a really useful way of setting boundaries and asking yourself hard questions about what you're doing and why.</p>
  458. <p>So when we set out to build a login dialog for <a href="http://www.discourse.org">Discourse</a>, I went back to what I learned in my Algorithms class and asked myself:</p>
  459. <blockquote>
  460. <p>How would God build this login dialog?</p>
  461. </blockquote>
  462. <p>And the answer is, of course, <strong>God wouldn't bother to build a login dialog at all.</strong> Every user would already be logged into GodApp the second they loaded the page because God knows who they are. Authoritatively, even.</p>
  463. <p>This is obviously impossible for us, because God isn't one of our investors.</p>
  464. <p>But.. how <em>close can we get</em> to the perfect godlike login experience in Discourse? That's a noble and worthy goal.</p>
  465. <p><img src="http://blog.codinghorror.com/content/images/2015/01/discourse-login-dialog.png" alt=""/></p>
  466. <p>Wasn't it Bill Gates who <a href="https://www.commandprompt.com/community/pyqt/x3581">once asked</a> why the hell every programmer was writing the same File Open dialogs over and over? It sure feels that way for login dialogs. I've been saying for a long time that <a href="http://blog.codinghorror.com/cutting-the-gordian-knot-of-web-identity/">the best login is no login at all</a> and I'm a staunch supporter of <a href="http://blog.codinghorror.com/your-internet-drivers-license/">logging in with your Internet Driver's license</a> whenever possible. So we absolutely support that, if you've configured it.</p>
  467. <p><img src="http://blog.codinghorror.com/content/images/2015/01/common-oauth-logins.png" alt=""/></p>
  468. <p>But today I want to focus on the <strong>core, basic login experience: user and password.</strong> That's the default until you configure up the other methods of login.</p>
  469. <p>A login form with two fields, two buttons, and a link on it seems simple, right? Bog standard. It is, until you consider all the ways the simple act of logging in with those two fields can go wrong for the user. Let's think.</p>
  470. <h4 id="lettheuserenteranemailtologin">Let the user enter an email to log in</h4>
  471. <p>The critical fault of OpenID, as much as <a href="http://blog.codinghorror.com/openid-does-the-world-really-need-yet-another-username-and-password/">I liked it</a> as an early login solution, was its assumption that users could accept an URL as their "identity". This is flat out crazy, and in the long run this central flawed assumption in OpenID broke it as a future standard. </p>
  472. <p><strong>User identity is always email, plain and simple</strong>. What happens when you forget your password? You get an email, right? Thus, email is your identity. Some people even propose <a href="http://notes.xoxco.com/post/27999787765/is-it-time-for-password-less-login">using email as the only login method</a>.</p>
  473. <p><img src="http://blog.codinghorror.com/content/images/2015/01/discourse-log-in-email.png" alt=""/></p>
  474. <p>It's fine to have a username, of course, but <em>always</em> let users log in with either their username or their email address. Because I can tell you with 100% certainty that when those users forget their password, and they will, all the time, they'll need that email anyway to get a password reset. Email and password are strongly related concepts and they belong together. Always!</p>
  475. <p>(And a fie upon services that don't allow me to use my email as a username or login. I'm looking at you, Comixology.)</p>
  476. <h4 id="telltheuserwhentheiremaildoesntexist">Tell the user when their email doesn't exist</h4>
  477. <p>OK, so we know that email is de-facto identity for most people, and this is a logical and necessary state of affairs. But <em>which</em> of my 10 email addresses did I use to log into your site? </p>
  478. <p>This was the source of a <a href="https://meta.discourse.org/t/different-password-reset-for-wrong-username-email/15909">long discussion at Discourse</a> about whether it made sense to reveal to the user, when they enter an email address in the "forgot password" form, whether we have that email address on file. On many websites, here's the sort of message you'll see after entering an email address in the forgot password form:</p>
  479. <blockquote>
  480. <p>If an account matches <a class="__cf_email__" href="http://blog.codinghorror.com/cdn-cgi/l/email-protection" data-cfemail="f89699959db89d80999588949dd69b9795">[email protected]</a>, you should receive an email with instructions on how to reset your password shortly.</p>
  481. </blockquote>
  482. <p>Note the coy "if" there, which is a <a href="http://www.troyhunt.com/2012/05/everything-you-ever-wanted-to-know.html">hedge against all the security implications of revealing whether a given email address exists on the site</a> just by typing it into the forgot password form. </p>
  483. <p>We're deadly serious about picking safe defaults for Discourse, so out of the box you won't get exploited or abused or overrun with spammers. But after experiencing the real world "which email did we use here again?" login state on dozens of Discourse instances ourselves, we realized that, in this specific case, being user friendly is <em>way</em> more important than being secure.</p>
  484. <p><img src="http://blog.codinghorror.com/content/images/2015/01/forgot-password.png" alt=""/></p>
  485. <p>The new default is to let people know when they've entered an email we don't recognize in the forgot password form. This will save their sanity, and yours. You can turn on the extra security of being coy about this, if you need it, via a site setting.</p>
  486. <h4 id="lettheuserswitchbetweenloginandsignupanytime">Let the user switch between Log In and Sign Up any time</h4>
  487. <p>Many websites have started to show login and signup buttons side by side. This perplexed me; aren't the acts of logging in and signing up very different things? </p>
  488. <p>Well, from the user's perspective, they don't appear to be. This Verge login dialog illustrates just how close the sign up and log in forms really are. Check out this animated GIF of it in action.</p>
  489. <p><img src="http://blog.codinghorror.com/content/images/2015/01/login-vs-sign-up.gif" alt=""/></p>
  490. <p>We've acknowledged that similarity by having either form accessible at any time from the two buttons at the bottom of the form, as a toggle:</p>
  491. <p><img src="http://blog.codinghorror.com/content/images/2015/01/login-vs-create-new-account.png" alt=""/></p>
  492. <p>And both can be kicked off directly from any page via the Sign Up and Log In buttons at the top right:</p>
  493. <p><img src="http://blog.codinghorror.com/content/images/2015/01/sign-up-vs-log-in-discourse.png" alt=""/></p>
  494. <h4 id="pickcommonwords">Pick common words</h4>
  495. <p>That's the problem with language, we have so many <em>words</em> for these concepts:</p>
  496. <ul>
  497. <li>Sign In</li>
  498. <li>Log In</li>
  499. <li>Sign Up</li>
  500. <li>Register</li>
  501. <li>Join &lt;site&gt;</li>
  502. <li>Create Account</li>
  503. <li>Get Started</li>
  504. <li>Subscribe</li>
  505. </ul>
  506. <p>Which are the "right" ones? <a href="http://ux.stackexchange.com/questions/1080/using-sign-in-vs-using-log-in">User research data isn't conclusive</a>.</p>
  507. <p>I tend to favor the shorter versions when possible, mostly because I'm a fan of the whole brevity thing, but there are valid <a href="http://uxmovement.com/buttons/why-sign-up-and-sign-in-button-labels-confuse-users/">cases to be made</a> for each depending on the circumstances and user preferences.</p>
  508. <p><img src="http://blog.codinghorror.com/content/images/2015/01/bad-okay-good-login-buttons.png" alt=""/></p>
  509. <p>Sign In may be slightly more common, though Log In has some <a href="http://www.designcult.org/2011/08/why-do-we-call-in-logging-in.html">nautical and historical computing basis</a> that makes it worthy:</p>
  510. <blockquote>
  511. <p>A couple of years ago I did a survey of top websites in the US and UK and whether they used “sign in”, “log in”, “login”, “log on”, or some other variant. The answer at the time seemed to be that if you combined “log in” and “login”, it exceeded “sign in”, but not by much. I’ve also noticed that the trend toward “sign in” is increasing, especially with the most popular services. Facebook seems to be a “log in” hold-out. </p>
  512. <p><img src="http://blog.codinghorror.com/content/images/2015/01/log-in-vs-sign-in-graph.png" alt="" title=""/></p>
  513. </blockquote>
  514. <h4 id="workwithbrowserpasswordmanagers">Work with browser password managers</h4>
  515. <p>Every login dialog you create should be tested to work with the default password managers in …</p>
  516. <p>At an absolute minimum. Upon subsequent logins in that browser, you should see the username and password automatically autofilled.</p>
  517. <p><img src="http://blog.codinghorror.com/content/images/2015/01/log-in-autofill.png" alt=""/></p>
  518. <p>Users rely on these default password managers built into the browsers they use, and any proper modern login form should respect that, and be designed sensibly, e.g. the password field should have <code>type="password"</code> in the HTML and a name that's readily identifable as a password entry field.</p>
  519. <p>There's also <a href="https://lastpass.com/">LastPass</a> and so forth, but I generally assume if the login dialog works with the built in browser password managers, it will work with third party utilities, too.</p>
  520. <h4 id="handlecommonusermistakes">Handle common user mistakes</h4>
  521. <p>Oops, the user is typing their password with caps lock on? You should let them know about that.</p>
  522. <p><img src="http://blog.codinghorror.com/content/images/2015/01/password-entry-caps-lock-is-on.png" alt=""/></p>
  523. <p>Oops, the user entered their email as <a class="__cf_email__" href="http://blog.codinghorror.com/cdn-cgi/l/email-protection" data-cfemail="e38d828e86a3848e828fcd808c8e">[email protected]</a> instead of <a class="__cf_email__" href="http://blog.codinghorror.com/cdn-cgi/l/email-protection" data-cfemail="e48a858981a48389858d88ca878b89">[email protected]</a>? Or <a class="__cf_email__" href="http://blog.codinghorror.com/cdn-cgi/l/email-protection" data-cfemail="305e515d5570585f445d51595c1e535d">[email protected]</a> instead of <a class="__cf_email__" href="http://blog.codinghorror.com/cdn-cgi/l/email-protection" data-cfemail="620c030f07220a0d160f030b0e4c010d0f">[email protected]</a>? You should either fix typos in common email domains for them, or let them know about that.</p>
  524. <p>(I'm also a big fan of <a href="http://answers.microsoft.com/en-us/ie/wiki/ie11-iewindows8_1/the-use-of-the-password-reveal-eye-button-in/19a9dee2-fb0c-4c26-a6bc-ac02cf98d80e">native browser "reveal password" support</a> for the password field, so the user can verify that she typed in or autofilled the password she expects. Only Internet Explorer and I <em>think</em> Safari offer this, but all browsers should.)</p>
  525. <h4 id="helpuserschoosebetterpasswords">Help users choose better passwords</h4>
  526. <p>There are many schools of thought on <s>forcing</s> helping users choose passwords that aren't unspeakably awful, e.g. <a href="http://blog.codinghorror.com/dictionary-attacks-101/">password123 and iloveyou and so on</a>. </p>
  527. <p>There's the common password strength meter, which updates in real time as you type in the password field.</p>
  528. <p><img src="http://blog.codinghorror.com/content/images/2015/01/dropbox-password-strength-meters.png" alt=""/></p>
  529. <p>It's clever idea, but it gets awful preachy for my tastes on some sites. The implementation also leaves a lot to be desired, as it's left up to the whims of the site owner to decide what password strength means. One site's "good" is another site's "get outta here with that Fisher-Price toy password". It's frustrating.</p>
  530. <p>So, with Discourse, rather than all that, I decided we'd default on a solid absolute minimum password length of 8 characters, and then verify the password to make sure it is not one of the <a href="http://thepasswordproject.com/">10,000 most common known passwords</a> by checking its hash.</p>
  531. <p><img src="http://blog.codinghorror.com/content/images/2015/01/create-new-account-password-too-common.png" alt=""/></p>
  532. <h4 id="dontforgetthekeyboard">Don't forget the keyboard</h4>
  533. <p>I feel like keyboard users are a dying breed at this point, but for those of us that, when presented with a login dialog, like to rapidly type</p>
  534. <p><kbd><a class="__cf_email__" href="http://blog.codinghorror.com/cdn-cgi/l/email-protection" data-cfemail="cca2ada1a98ca9b4ada1bca0a9e2afa3a1">[email protected]</a></kbd>, <kbd>tab</kbd>, <kbd>p4$$w0rd</kbd>, <kbd>enter</kbd></p>
  535. <p>… <em>please</em> verify that this works as it should. Tab order, enter to submit, etcetera.</p>
  536. <h4 id="ratelimitallthethings">Rate limit all the things</h4>
  537. <p>You should be <a href="http://blog.codinghorror.com/rate-limiting-and-velocity-checking/">rate limiting everything users can do, everywhere</a>, and that's especially true of the login dialog.</p>
  538. <p>If someone forgets their password and makes 3 attempts to log in, or issues 3 forgot password requests, that's probably OK. But if someone makes a thousand attempts to log in, or issues a thousand forgot password requests, that's a little weird. Why, I might even venture to guess they're possibly … <em>not human</em>.</p>
  539. <p><img src="http://blog.codinghorror.com/content/images/2015/01/too-many-failed-log-in-attempts.png" alt=""/></p>
  540. <p>You can do fancy stuff like temporarily disable accounts or start showing a CAPTCHA if there are too many failed login attempts, but this can easily become a griefing vector, so be careful.</p>
  541. <p>I think a nice middle ground is to insert standard pauses of moderately increasing size after repeated sequential failures or repeated sequential forgot password requests from the same IP address. So that's what we do.</p>
  542. <h4 id="stuffiforgot">Stuff I forgot</h4>
  543. <p>I tried to remember everything we went through when we were building our ideal login dialog for Discourse, but I'm sure I forgot something, or could have been more thorough. Remember, <a href="https://github.com/discourse/discourse">Discourse is 100% open source</a> and by definition a work in progress – so as my friend <a href="http://tirania.org/blog/">Miguel de Icaza</a> likes to say, when it breaks, you get to keep both halves. Feel free to test out our implementation and give us your feedback in the comments, or point to other examples of great login experiences, or cite other helpful advice.</p>
  544. <p>Logging in involves a simple form with two fields, a link, and two buttons. And yet, after reading all this, I'm sure you'll agree that it's deceptively complex. Your best course of action is not to build a login dialog at all, but instead rely on authentication from an outside source whenever you can.</p>
  545. <p>Like, say, God.</p>
  546. </article>
  547. </section>
  548. <nav id="jumpto">
  549. <p>
  550. <a href="/david/blog/">Accueil du blog</a> |
  551. <a href="http://blog.codinghorror.com/the-god-login/">Source originale</a> |
  552. <a href="/david/stream/2019/">Accueil du flux</a>
  553. </p>
  554. </nav>
  555. <footer>
  556. <div>
  557. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  558. <p>
  559. Bonjour/Hi!
  560. Je suis <a href="/david/" title="Profil public">David&nbsp;Larlet</a>, je vis actuellement à Montréal et j’alimente cet espace depuis 15 ans. <br>
  561. Si tu as apprécié cette lecture, n’hésite pas à poursuivre ton exploration. Par exemple via les <a href="/david/blog/" title="Expériences bienveillantes">réflexions bimestrielles</a>, la <a href="/david/stream/2019/" title="Pensées (dés)articulées">veille hebdomadaire</a> ou en t’abonnant au <a href="/david/log/" title="S’abonner aux publications via RSS">flux RSS</a> (<a href="/david/blog/2019/flux-rss/" title="Tiens c’est quoi un flux RSS ?">so 2005</a>).
  562. </p>
  563. <p>
  564. Je m’intéresse à la place que je peux avoir dans ce monde. En tant qu’humain, en tant que membre d’une famille et en tant qu’associé d’une coopérative. De temps en temps, je fais aussi des <a href="https://github.com/davidbgk" title="Principalement sur Github mais aussi ailleurs">trucs techniques</a>. Et encore plus rarement, <a href="/david/talks/" title="En ce moment je laisse plutôt la place aux autres">j’en parle</a>.
  565. </p>
  566. <p>
  567. Voici quelques articles choisis :
  568. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  569. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  570. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  571. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  572. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  573. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  574. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  575. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  576. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  577. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  578. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  579. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  580. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  581. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  582. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  583. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  584. </p>
  585. <p>
  586. On peut <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">échanger par courriel</a>. Si éventuellement tu souhaites que l’on travaille ensemble, tu devrais commencer par consulter le <a href="http://larlet.com">profil dédié à mon activité professionnelle</a> et/ou contacter directement <a href="http://scopyleft.fr/">scopyleft</a>, la <abbr title="Société coopérative et participative">SCOP</abbr> dont je fais partie depuis six ans. Je recommande au préalable de lire <a href="/david/blog/2018/cout-site/" title="Attention ce qui va suivre peut vous choquer">combien coûte un site</a> et pourquoi je suis plutôt favorable à une <a href="/david/pro/devis/" title="Discutons-en !">non-demande de devis</a>.
  587. </p>
  588. <p>
  589. Je ne traque pas ta navigation mais mon
  590. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  591. conserve des logs d’accès.
  592. </p>
  593. </div>
  594. </footer>
  595. <script type="text/javascript">
  596. ;(_ => {
  597. const jumper = document.getElementById('jumper')
  598. jumper.addEventListener('click', e => {
  599. e.preventDefault()
  600. const anchor = e.target.getAttribute('href')
  601. const targetEl = document.getElementById(anchor.substring(1))
  602. targetEl.scrollIntoView({behavior: 'smooth'})
  603. })
  604. })()
  605. </script>