[{"data":1,"prerenderedAt":384},["ShallowReactive",2],{"project-31333933":3},{"id":4,"name":5,"line":6,"line_source":7,"title":8,"url":9,"domain":10,"maker":11,"posted_at":12,"year":13,"points":14,"comments":15,"rank_in_year":16,"shown_in_year":17,"top":18,"tier":19,"category":20,"subcategory":21,"format":22,"keywords":23,"reception":28,"sentiment":29,"stance_mix":30,"scored_comments":35,"aspects":36,"strip":46,"post_text":47,"what":48,"said":76,"maker_in_thread":195,"shape":206,"grid":209,"attempts":345,"same_week":346,"maker_shows":31,"maker_hidden":376,"reader_version":377,"card":378},"31333933","prefixed-api-key","Prefixed, dual-token, base58 encoded API Keys","title","Show HN: Prefixed, dual-token, base58 encoded API Keys","https:\u002F\u002Fgithub.com\u002Fseamapi\u002Fprefixed-api-key","github.com","seveibar",1652224905,2022,100,54,612,2570,24,"notable","dev_tools","api key library","library",[24,25,26,27],"api keys","base58","token authentication","secret scanning","mixed",-0.103,{"positive":31,"neutral":32,"mixed":33,"critical":34},5,6,2,9,22,{"docs":37,"design":40,"execution":41,"privacy":43,"idea":44,"performance":45},{"positive":38,"critical":39},0,1,{"positive":33,"critical":39},{"positive":38,"critical":42},3,{"positive":38,"critical":42},{"positive":33,"critical":38},{"positive":38,"critical":39},[42,38,38,42,38,39,39,38,42,38,38,39,38,39,38,39,42,42,38,31,38,38,38,38,39,42,39,38,42,42,38,31,38,38,39,38,38,38,38,31],null,{"problem":49,"audience":47,"pricing":47,"motivation":56,"time_to_build":47,"stack":60,"openness":73,"stage":74,"team_size":75},{"value":50,"source":51,"evidence":52},"Generating and verifying API keys that are easy to copy, scan for in leaks, and store safely","maker",[53],{"id":54,"by":11,"text":55},"31333967","Author here: I was curious what HN would think of this token style, we're currently using it for getseam.com . I think it has a lot of advantages over other token styles (e.g.…",{"value":57,"source":51,"evidence":58},"Maker's company getseam.com uses this token style and they wanted HN feedback on it",[59],{"id":54,"by":11,"text":55},[61,65],{"value":62,"source":51,"evidence":63},"TypeScript",[64],{"id":54,"by":11,"text":55},{"value":66,"source":67,"evidence":68},"Node.js crypto","commenter",[69],{"id":70,"by":71,"text":72},"31334642","jffry","Thanks for sharing! As somebody who has been bitten before by copy-pasting a key I appreciate that as the headlining benefit :) Thoughts from an ergonomic perspective: - Base58…","open_source","launched","unknown",{"praise":77,"objections":105,"requests":142,"questions":161,"alternatives":180},[78,89,97],{"value":79,"source":67,"evidence":80},"Prefix enables secret scanning and easy leak detection",[81,85],{"id":82,"by":83,"text":84},"31337167","bluehatbrit","It's the issuers company or product name. GitHub for example prefix their tokens with \"gh_\" which makes it easy to scan for tokens uploaded to repos or even across the web if…",{"id":86,"by":87,"text":88},"31335081","remram","If you are prefixing it, why not prefix it with a full domain name? mycompany_com_BRTRKFsL_51FwqftsmMDHHbJAMEXXHCgG is not much longer than…",{"value":90,"source":67,"evidence":91},"Base58 is easy to copy and double-click select",[92,96],{"id":93,"by":94,"text":95},"31334630","__sy__","from the RFC: > Base58 is designed with a number of usability characteristics in mind that Base64 does not consider. First, similar looking letters are omitted such as 0 (zero),…",{"id":54,"by":11,"text":55},{"value":98,"source":67,"evidence":99},"Clear, useful design with a documented README",[100,104],{"id":101,"by":102,"text":103},"31334489","koolba","Couple comments upon looking at the actual code: - You can promisify randomBytes once and reuse it rather than twice for every invocation - There shouldn’t be a default value for…",{"id":70,"by":71,"text":72},[106,115,130,135],{"value":107,"source":67,"evidence":108},"Timing-unsafe hash comparison and unsafe split parsing",[109,110,114],{"id":101,"by":102,"text":103},{"id":111,"by":112,"text":113},"31335893","mdaniel","I don't follow what the security property is of involving sha256 against the b58 encoded value? If it serves as a checksum against tpyoss, wouldn't one wish to include the…",{"id":70,"by":71,"text":72},{"value":116,"source":67,"evidence":117},"Base58 is unnecessary and slow to decode compared with base32 or base62",[118,122,126],{"id":119,"by":120,"text":121},"31335488","adastra22","Don’t use base58. Even bitcoin, which originated or at least popularized base58 has moved away from it. They are a pain to encode and decode, with a naive implementation…",{"id":123,"by":124,"text":125},"31337661","formerly_proven","Using base58 here strikes me as simply unnecessary. You could just use the base58 alphabet and directly generate random strings in that alphabet, easily and cheaply. The only…",{"id":127,"by":128,"text":129},"31335600","teraflop","The RFC doesn't mention an important drawback of base58, which is that the computational cost of encoding or decoding it is quadratic in the length of the input -- unlike base64,…",{"value":131,"source":67,"evidence":132},"Default key prefix and underscores in prefix cause problems",[133,134],{"id":70,"by":71,"text":72},{"id":101,"by":102,"text":103},{"value":136,"source":67,"evidence":137},"Underscores break double-click selection on iPhone",[138],{"id":139,"by":140,"text":141},"31334233","joshuawarner32","On iPhone the long-press-to-select doesn’t cross the underscore boundary, so you end up just selecting part of the token. Maybe you can do without those underscores?",[143,151,156],{"value":144,"source":67,"evidence":145},"Use a slow hash such as bcrypt, scrypt or argon2 for the long token",[146,147],{"id":70,"by":71,"text":72},{"id":148,"by":149,"text":150},"31335075","benatkin","What meaningfully changes the requirements is that takes many more attempts to brute force an API key because they're longer. I'd consider using a weaker bcrypt\u002Fscrypt\u002Fargon2 for…",{"value":152,"source":67,"evidence":153},"Validate the prefix and avoid split-based parsing",[154,155],{"id":111,"by":112,"text":113},{"id":101,"by":102,"text":103},{"value":157,"source":67,"evidence":158},"Rename short token and long token to clearer terms",[159,160],{"id":70,"by":71,"text":72},{"id":70,"by":71,"text":72},[162,169,176],{"value":163,"source":67,"evidence":164},"Why base58 was chosen",[165],{"id":166,"by":167,"text":168},"31334497","anm89","Why base 58? I've never even heard of base 58.",{"value":170,"source":67,"evidence":171},"Why store a hash of the long token instead of signing the short token",[172],{"id":173,"by":174,"text":175},"31337679","tetch","Nice work! May I inquire as to the design decision behind storing a hash of the long token server-side for verification of the long token, versus digitally signing the short…",{"value":177,"source":67,"evidence":178},"Whether the prefix should include a domain name",[179],{"id":86,"by":87,"text":88},[181,188],{"value":182,"source":67,"evidence":183},"Stripe-style prefixed tokens",[184],{"id":185,"by":186,"text":187},"31334833","godmode2019","Very cool, I use something similar but a fixed length for the prefix and uppercase Basically a copy of stripe tokens",{"value":189,"source":67,"evidence":190},"pika Snowflake-based IDs",[191],{"id":192,"by":193,"text":194},"31335071","phineyes","I created a similar project recently! Main difference is that I use base64 to encode unique Snowflake IDs, which are timestamped: https:\u002F\u002Fgithub.com\u002Fhopinc\u002Fpika :)",{"comments":196,"answered":197,"key":198},11,7,[199,202,205],{"id":200,"text":201},"31334407","fixed thank you :) - and great suggestion! edit based on your edit: Added your recommendation to the README, thank you!",{"id":203,"text":204},"31334808","my mistake! fixed :)",{"id":54,"text":55},{"commenters":207,"max_depth":34,"maker_replies":196,"first_comment_mins":31,"lifespan_hours":208},28,40,[210,213,215,218,219,222,224,226,229,231,234,236,239,241,244,246,248,251,252,256,259,260,263,264,265,267,271,273,276,278,280,283,286,290,292,295,299,301,304,307,310,312,316,319,323,325,327,331,334,336,339,341,343,344],[211,38,42,212,149,38],"31334387","project\u002Fmixed\u002Fdocs",[200,38,38,214,11,39],"maker\u002Fpositive\u002Fdocs",[216,38,38,217,149,38],"31334786","commenter\u002Fneutral\u002Fdocs",[203,38,38,214,11,39],[220,38,42,221,149,38],"31344346","project\u002Fneutral\u002Fdocs",[54,39,38,223,11,39],"project\u002Fpositive\u002Fidea",[139,39,39,225,140,38],"project\u002Fcritical\u002Fdesign",[227,39,38,228,94,38],"31334281","other\u002Fneutral\u002Fnone",[101,39,39,230,102,38],"project\u002Fcritical\u002Fexecution",[232,39,38,233,11,39],"31334681","maker\u002Fpositive\u002Fexecution",[70,39,42,235,71,38],"project\u002Fmixed\u002Fdesign",[237,39,38,238,102,38],"31334703","commenter\u002Fneutral\u002Fprivacy",[240,39,38,238,149,38],"31335302",[242,39,38,243,11,39],"31334759","maker\u002Fpositive\u002Fprivacy",[148,39,39,245,149,38],"project\u002Fcritical\u002Fprivacy",[86,39,42,247,87,38],"project\u002Fneutral\u002Fidea",[249,39,38,250,11,39],"31336580","maker\u002Fneutral\u002Fdesign",[119,39,39,230,120,38],[253,39,38,254,255,38],"31336444","commenter\u002Fneutral\u002Fexecution","epse",[257,39,38,258,120,38],"31337595","commenter\u002Fcritical\u002Fexecution",[123,39,39,230,124,38],[261,39,42,247,262,38],"31335658","splix",[82,39,31,223,83,38],[111,39,39,245,112,38],[266,39,38,233,11,39],"31336594",[268,33,31,269,270,38],"31355600","project\u002Fpositive\u002Fnone","miller_joe",[192,42,38,272,193,38],"other\u002Fneutral\u002Fidea",[274,42,38,275,149,38],"31335210","commenter\u002Fneutral\u002Fidea",[277,42,38,275,193,38],"31335294",[279,42,38,275,149,38],"31335628",[281,42,38,282,193,38],"31336016","commenter\u002Fcritical\u002Fidea",[284,42,38,228,285,38],"31349614","jd_mongodb",[287,288,39,245,289,38],"31338251",4,"kevincox",[173,31,42,291,174,38],"project\u002Fneutral\u002Fprivacy",[293,31,38,294,11,39],"31340617","maker\u002Fneutral\u002Fprivacy",[296,32,39,297,298,38],"31338379","project\u002Fcritical\u002Fdocs","wccrawford",[300,32,38,214,11,39],"31340782",[302,197,42,221,303,38],"31336542","lefrenchy",[305,197,38,306,11,39],"31336629","maker\u002Fneutral\u002Fdocs",[166,308,42,309,167,38],8,"project\u002Fneutral\u002Fdesign",[311,308,38,228,71,38],"31334501",[313,308,31,314,315,38],"31334728","project\u002Fpositive\u002Fdesign","NavinF",[317,308,38,228,318,38],"31335215","UncleEntity",[320,308,38,321,322,38],"31335353","commenter\u002Fneutral\u002Fdesign","nick238",[324,308,38,250,11,39],"31340700",[127,308,39,326,128,38],"project\u002Fcritical\u002Fperformance",[328,308,38,329,330,38],"31335862","commenter\u002Fneutral\u002Fperformance","dividuum",[332,308,38,329,333,38],"31336146","oefrha",[335,308,38,329,128,38],"31336786",[337,308,38,338,330,38],"31337238","commenter\u002Fcritical\u002Fperformance",[340,308,38,329,128,38],"31341529",[342,308,38,329,330,38],"31344369",[93,308,31,314,94,38],[185,34,31,223,186,38],[],[347,352,357,362,366,371],{"id":348,"name":349,"points":350,"category":351},"31335904","Himalayas",457,"web_saas",{"id":353,"name":354,"points":355,"category":356},"31275476","Dead Trees",389,"games",{"id":358,"name":359,"points":360,"category":361},"31333436","Oldest Search",295,"productivity",{"id":363,"name":364,"points":365,"category":356},"31296227","Battle of Flags",212,{"id":367,"name":368,"points":369,"category":370},"31297917","Weron",204,"infrastructure",{"id":372,"name":373,"points":374,"category":375},"31294306","Ankify",180,"learning_science",false,"v2",{"id":379,"name":5,"line":6,"ai":376,"cat":20,"year":13,"points":14,"comments":15,"rec":28,"s":380,"top":18,"kw":381,"maker":11,"domain":10,"tier":19,"attempts":38,"strip":382,"url":9,"mix":383,"praise":79,"objection":107,"sub":21},31333933,-0.1,[24,25,26,27],"3003011030010101330500001310330500100005",[31,32,33,34],1793991045521]