$30 off During Our Annual Pro Sale. View Details »

Authentication done right: Consuming (and Serving) Oauth2.0

Authentication done right: Consuming (and Serving) Oauth2.0

Your brand has multiple products on the web. They all need authentication. But obviously, you’d maintain a common authentication and user database.
Also, in this age, you cannot make a login system without Login with [Facebook|Google|Twitter|...]
For the second thing, you need to consume Oauth2.0 (the industry standard now), for the first thing, you need to make your own Oauth server.
And you want all of this to happen securely (so yeah, little to no frontend JS)

Arnav Gupta

October 26, 2018
Tweet

More Decks by Arnav Gupta

Other Decks in Programming

Transcript

  1. Authentication Done Right
    Learnings from my journey of creating a federated login
    system in NodeJS
    Arnav Gupta

    View Slide

  2. ● Please do not consider me an expert on NodeJS, or cyber security. (My only
    expertise is memes).
    ● This is not a “you should do it this way” sermon
    ● This is our (Coding Blocks’) story – how we made our login system.
    ● Some “security mishap” ™ examples about organizations are shown. I do not
    imply their systems are insecure or they are incompetent. Only that no one is
    infallible (except UIDAI)
    ● Research on your own, and decide your own perf/security/UX tradeoffs.
    Disclaimer

    View Slide

  3. What have we built ?
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  4. © 2018 Coding Blocks, Arnav Gupta
    Client App 1 Client App 1 Client App 1
    Login / User Management System

    View Slide

  5. What have we built ?
    OAuth2 client

    Login via Facebook/Twitter/Github/Google is via their OAuth interface

    Connect to Facebook after Login via Twitter (also OAuth)

    Migrating our old system users to new one invisibly (old interfaced
    massaged into OAuth)
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  6. Client App 1 Client App 1 Client App 1
    © 2018 Coding Blocks, Arnav Gupta
    Login / User Management System
    our own
    old
    system
    migrate

    View Slide

  7. Ingredients ?
    ● PassportJS – Literally Auth0 has
    made everything open source
    with which you can D.I.Y. an exact
    Auth0 clone
    ● PostgreSQL (with Sequelize)
    ● bcrypt – (native)
    © 2018 Coding Blocks, Arnav Gupta
    bcrypt

    View Slide

  8. What have we built ?
    OAuth2 server

    Single Sign On (with Federated Identity)

    All apps login via account.codingblocks.com (and get profile/role details)

    Anyone can make an app and use login via Coding Blocks
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  9. Login / User Management System
    © 2018 Coding Blocks, Arnav Gupta
    other apps . . .
    auth+
    roles
    auth +
    github

    roles update
    coupons/demographics

    View Slide

  10. Ingredients
    ● oauth2orize – Creates a OAuth
    2.0 server automagically
    ● nodemailer (+sendgrid) email
    verify, reset and stuff
    ● speakeasy – TOTP for 2-factor
    login
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  11. Our Guiding Principles
    © 2018 Coding Blocks, Arnav Gupta
    Basic tenets on which our architecture hinges

    View Slide

  12. No JS
    ● 100% working in no-JS browsers
    ● Forget about XSS
    ● CSS sufficient for responsive
    ● Secure HttpOnly cookies
    ● Perspective: Gmail works sans JS
    ● XSS protection is HARD, a f
    ● Performance ++
    ● Bonus: Better for SEO
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  13. bcrypt
    ● Use C++, not JS lib
    ● scrypt less tested, maybe better
    ● PBKDF2 also fine (preference)
    ● Just use bcrypt, don’t tinker
    ● Salts – as random as possible
    ● Work factor (rounds) ≈ 10 is ok
    ● Pepper not needed (methinks)
    ● Stick to blowfish/pbkdf2 - KISS
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  14. 2FA
    ● Encourage 2FA
    ● Don’t force it
    ● speakeasy – TOTP (must)
    ● SMS / Email optional (recommend)
    ● TOTP fallback to SMS/Email
    ● Ideal: Redo 2FA on session resume
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  15. No JWT
    ● JWTs have their place, not here
    ● Server-enforced logout needed
    ● Token lookup time != perf barrier
    ● Remember JWT = Cookie for GDPR
    ● Stateful JWT = Session token only
    ● JWT is avoidable extra knowledge
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  16. Single UI
    ● Only one form takes password
    ● Trust browsers
    ● NO API based login for clients
    ● You are NOT Facebook/Google
    ● Reduce MITM vectors
    ● Browsers enforce & show HTTPS
    ● Consistent redirect UX for users
    ● User’s internet too slow for HTML?
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  17. Let’s start from
    the basics
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  18. Step 1: Decided what we
    cannot protect
    “One of the main cyber-risks is to think they don’t exist. The other is to try to treat all
    potential risks.
    - Stephane Nappo

    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  19. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  20. ● User sharing login credentials (or looking over shoulder)
    ● User’s browser is compromised (or malicious web extensions)
    ● Social-engineered hacks
    ● Users behind 3rd party SSL certificates
    What we kept out of our scope of protection

    View Slide

  21. Identification
    vs
    Authentication
    vs
    Authorization
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  22. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  23. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  24. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  25. Quiz Time!
    © 2018 Coding Blocks, Arnav Gupta
    401 vs 403

    Error Name of 401 ? Can a logged in user get 401 ?

    Error name of 403 ? Can a logged out user get 403 ?

    View Slide

  26. Answers
    © 2018 Coding Blocks, Arnav Gupta
    401 vs 403

    401: Unauthorized (but really means unauthenticated)

    403: Forbidden (which means authenticated but not authorized)

    View Slide

  27. Authentication via
    Authorization
    © 2018 Coding Blocks, Arnav Gupta
    In other words, OAuth SSO via social media accounts

    View Slide

  28. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  29. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  30. © 2018 Coding Blocks, Arnav Gupta
    http://account.codingblocks.com

    View Slide

  31. © 2018 Coding Blocks, Arnav Gupta
    https://account.codingblocks.com
    https://account.codingblocks.com

    View Slide

  32. © 2018 Coding Blocks, Arnav Gupta
    https://account.codingblocks.com
    login
    page
    https://account.codingblocks.com

    View Slide

  33. © 2018 Coding Blocks, Arnav Gupta
    login
    page
    Click: Login with Github

    View Slide

  34. © 2018 Coding Blocks, Arnav Gupta
    https://github.com/login
    client_id = xxxx
    redirect_uri = xxxx

    View Slide

  35. © 2018 Coding Blocks, Arnav Gupta
    github
    login
    page

    View Slide

  36. © 2018 Coding Blocks, Arnav Gupta
    github
    login
    page
    username
    password

    View Slide

  37. © 2018 Coding Blocks, Arnav Gupta
    github
    login
    page
    verify username & password

    View Slide

  38. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  39. © 2018 Coding Blocks, Arnav Gupta
    https://account.codingblocks.com
    auth_token = xxxx
    implicit
    profile
    page

    View Slide

  40. © 2018 Coding Blocks, Arnav Gupta
    https://api.github.com/profile
    {header: auth_token = xxxx }
    profile data
    fetching in
    browser
    profile
    page
    name
    email
    username

    View Slide

  41. © 2018 Coding Blocks, Arnav Gupta
    https://account.codingblocks.com
    grant_code = xxxx
    explicit

    View Slide

  42. © 2018 Coding Blocks, Arnav Gupta
    https://auth.github.com/token
    {header: grant_code = xxxx }
    auth_token

    View Slide

  43. © 2018 Coding Blocks, Arnav Gupta
    https://auth.github.com/token
    {header: grant_code = xxxx }
    auth_token
    https://api.github.com/profile
    {header: auth_token = xxxx }
    name
    email
    username
    profile
    page

    View Slide

  44. © 2018 Coding Blocks, Arnav Gupta
    Open URL
    Fetch Page
    Show auth UI
    Fetch 3rd Party Auth Page
    3rd Party Challenge
    Fulfill challenge
    Request 3rd party login
    Verify Challenge
    Redirect to client (with AUTH_TOKEN)
    Fetch Client Page
    Show Client Page
    Fetch Profile detauls (using AUTH_TOKEN)
    Send profile details

    View Slide

  45. © 2018 Coding Blocks, Arnav Gupta
    Open URL
    Fetch Page
    Show auth UI
    Fetch 3rd Party Auth Page
    3rd Party Challenge
    Fulfill challenge
    Request 3rd party login
    Verify Challenge
    Redirect to client (with GRANT_CODE)
    Fetch Client Page
    Show Client Page
    Request ACCESS_TOKEN using GRANT_CODE
    Provide ACCESS_TOKEN
    Fetch Profile detauls (using AUTH_TOKEN)
    Send profile details

    View Slide

  46. What we learnt in
    the process ?
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  47. Basic security hygiene
    We came a long way without taking care of these. You
    shouldn’t

    CSRF protection (use csurf)

    Basic ExpressJS protection (headers, origins, content types) – use helmet

    Importance of statelessness (when we moved to cluster mode we had to
    do major rewrite as sessions were in memory)
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  48. Basic Security vs Overkill Checklist
    ● X-Frame-Options (likely you want - DENY) :
    helmet/frameguard
    ● Content-Security-Policy
    ○ script-src - helps against XSS, can completely
    disable frontend JS
    ○ use specific CDN img-src, style-src, font-src
    ○ use whitelist for form-action and connect-src
    ● Http Public Key Pinning - Hard to maintain,
    attacker can pin wrong cert, Chrome 72+
    drops support.
    ● HTTP Strict Transport Security – You can
    enforce SSL on application layer. Also try
    HTTPS for at least 6-9 months without issues
    before trying this. You may lock out users.
    Catch 22 – renewing an expired LetsEncrypt
    cert

    View Slide

  49. CSRF protection
    ● Only for endpoints that will
    be hit by our own frontend.
    ● Do not implement on
    endpoints where external
    clients with POST (eg. the
    API)
    © 2018 Coding Blocks, Arnav Gupta
    const csurf = require('csurf')
    app.use(csurf({cookie: true}))
    app.get('/form', (req, res) => res.render('form', {
    _csrf: req.csrfToken()
    }))
    app.post('/submit', (req, res) => {
    // process data
    })





    View Slide

  50. SQL Injection is very very real
    Thumbrule: ORM prepared statements help. BUT . . .

    Step 1: Make sure you use prepared/escaped queries. Manually escape
    rawQueries

    Sequelize 3.x suffered thrice from SQLi vuln. (All fixed in >= v3.20)

    Defense in depth: validate every layer (client, route, controller, model)
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  51. Specs are important
    Being incomplete is ok, but being non-compliant is not

    Specifications like OAuth or other ISO/IEEE/IETF ratified standards have
    gone through multiple RFC periods, technical peer reviews

    However enticing – try to avoid ‘tiny workarounds’ or ‘small changes’

    Fine to keep some features unimplemented, but careful not to make spec-
    compliance impossible in feature.
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  52. ● Used custom “our own way” of refresh tokens
    All client apps use common OAuth libraries, which have support for spec-
    compliant way. Ended up breaking libraries, editing lines in node_modules,
    creating own oauth library, finally giving up.
    ● Didn’t consider client credential grant to be future use case. Major rewrite to
    support payments app to read data of user outside of user token scope
    Ignored specs: Burned our fingers

    View Slide

  53. ● Refresh tokens (people will re-auth after expiry)
    ● Scopes (keep scope data structure, just use ‘profile’ or ‘*’ scope for all)
    ● Client-scoped tokens (non-user authorizations)
    ● Password grant type – actually an insecure backwards compatible vestige, do not
    implement (refer: Single UI)
    Specifically in OAuth, we can ignore / defer …

    View Slide

  54. State Management is UX itself
    In a 2-level SSO maximum user drops happen when we fail to
    pursue all redirectTo and returnTo scenarios

    unverified email > account page > verify > back to exact same screen

    shopping first time > add address > back to same state of cart

    client app > sso portal > 3rd party sso > sso portal > client app (no drops)

    refrain from window.open (XSS vectors, distraction, non linear)
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  55. © 2018 Coding Blocks, Arnav Gupta
    0-click login

    View Slide

  56. © 2018 Coding Blocks, Arnav Gupta
    1-click login

    View Slide

  57. © 2018 Coding Blocks, Arnav Gupta
    2-click login

    View Slide

  58. © 2018 Coding Blocks, Arnav Gupta
    3-click login

    View Slide

  59. Separation of Concerns
    Authentication vs Authorization vs Data API

    Client apps depend on Data API – make it versioned

    Client apps depend on authorization flow – have support for scopes

    Client apps do not depend on authentication flow
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  60. Separation of Concerns
    What in client and what on SSO server ?

    Thumbrule: If needed by more than 2 apps, centralize it

    Thumbrule: If data does not belong to user, do NOT centralize it

    Roles: Present on both SSO and client

    Access social media info – always via the SSO api
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  61. Separation of Concerns
    Data Synchronisation

    At every user <-> client auth, we verify data from SSO server

    For passive data changes to reflect (when user is not using), support for
    data sync webhooks on clients, which SSO server triggers

    For data updates on SSO server, do it via generic API using user-scoped
    tokens. No “special endpoints for special clients”.
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  62. Better mileage with existing
    standards (POLA)
    We found there is an ISO standard for a whole lot of things!

    For demographics (country/state), we use ISO 3611 codes as primary keys,
    after wasting time migrating autoincr int ids multiple times

    RFC6750 for bearer token usage

    Standard headers, params are handled by many libraries in a special
    ways, benefits you forgo if deviating from standards
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  63. Analytics and Monitoring
    Be careful what you log

    No JS = no client side analytics. Which is good. Adblockers spoil data

    Make sure you obfuscate critical data in logs. 3rd party monitoring means
    data is stored with them, not you (legal implications)

    Use standard HTTP error codes (you have no idea how much that helps)

    Careful about log-all wrappers like newrelic, Graylog, Sentry
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  64. Common
    mistake
    ● 2018 Github – logging
    passwords
    ● 2018 Twitter – logging
    password
    ● Wide speculation – they
    were both using similar
    Ruby based logging
    service that dumps all
    HTTP requests
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  65. Explain errors, to the user
    Obscure errors are reported with 0 details and you can’t fix
    your system if you don’t get exact bugs reported

    Wrong email O R password << this is useless

    Something went wrong << ugghhh no

    Obscuring things don’t make them secure (hackers dig deeper, actual
    users get frustrated)
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  66. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  67. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  68. We did this too, and ended up frustrated with . . .

    I can’t login….

    Why?

    I just can’t
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  69. Don’t be big brother
    Asking for data or extra permission lowers conversions

    Take as few details as needed. Clients can request more, come back and
    fill those later (eg. address only when buying physical product)

    Social logins have less steps if you ask for less scopes. Fetching too much
    data was detrimental (discrepancies)

    Refrain from dark patterns (user should feel in control)
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  70. Social Login with
    extra perms
    © 2018 Coding Blocks, Arnav Gupta
    ● Ever changing API docs of
    3rd party login providers
    ● Login with
    Facebook/Google/Twitte
    r not working – common
    occurrence; especially
    after Cambridge
    Analytica
    Login with Google on Disqus, circa 2018

    View Slide

  71. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  72. Try to remain generic
    Specifics are hard to change later

    Assume country can be anything. Don’t assume +91 etc etc

    More roles in future possible. More social logins possible.

    The AWS/Amazon formula – to build better, think you’re building for 3rd
    party clients

    To integrate custom systems like Discus, we have OAuth consuming proxy
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  73. Deduplication is H A R D
    Uniquely identifying users is painful, do it at your own peril

    User has Facebook and Twitter acc with separate emails. Wants to merge

    Emails with dots and plus

    IP Address of login client (to check account sharing)

    2FA via mobile/email vectors can help
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  74. Secure – even from yourself
    Your developers are also ‘outsiders’ for the system

    Create a dev db. Anonymize the names and demographic data

    Open source it (ok at least pretend this is an open source project)

    Never ever ever ever ever ask users to send any detail aside from email id

    Even at MVP/Prototype stage no ‘admin:admin’ credentials please!
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  75. Security is actually very
    simple*
    * Terms and Conditions Apply

    Security vs User Experience tradeoff is NOT NECESSARY

    Security vs Developer Experience tradeoff IS VERY VERY REAL

    There is no “dev environment” – it is always production

    Do not bullshit yourself with “we’ll secure it later”

    Actually follow the basic security tips we know from 5th grade
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  76. If you think you are a cyber-security
    expert, that’s the first sign you aren’t
    © 2018 Coding Blocks, Arnav Gupta
    “It is better to remain silent and be thought a fool than to open one's mouth and remove all
    doubt.
    - Mark Twain or Abraham Lincoln

    View Slide

  77. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  78. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  79. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  80. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  81. © 2018 Coding Blocks, Arnav Gupta

    View Slide

  82. Inspirations
    Other similar systems

    StackExchange Network (imo, the best implementation of dual layer SSO)

    Closer home – HasGeek’s lastuser
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  83. Credits
    Won’t be possible without

    Jared Hanson - Auth0 Chief Arch, author of Passport and oauth2orize

    Eran Hammer – OAuth 1.0 author, and critic of Oauth 2.0 – for telling us
    which parts of OAuth 2.0 are overkill and unnecessary
    © 2018 Coding Blocks, Arnav Gupta

    View Slide

  84. Thank You
    https://github.com/coding-blocks/oneauth
    https://account.codingblocks.com
    @championswimmer
    © 2018 Coding Blocks, Arnav Gupta

    View Slide