DIY Rails Authentication

DIY Rails Authentication

Let's explore what it takes to do DIY authentication using Rails' has_secure_password and Rails sessions. It's more simplistic than Devise and often takes less time - freal.

Code: https://github.com/jwo/presentations-diy-auth-code

3b5cb0411cbd1012acab1b60f7ce3606?s=128

Jesse Wolgamott

September 13, 2016
Tweet

Transcript

  1. RAILS AUTH - DIY AUTHENTICATION IN RAILS & API, DIY

    INSTEAD OF DEVISE Created by / Jesse Wolgamott @jwo
  2. AUTHENTICATION & AUTHORIZATION Authentication: Knowing who you are Authorization: Granting

    access based on conditions
  3. WHY NOT JUST USE DEVISE? Di cult to understand Di

    cult to customize Di cult to extend
  4. GENERAL FLOW: 1. Request Page 2. Redirect to Sign in

    if not signed in 3. Sign in 4. Request Page 5. Now we view page!
  5. REDIRECT TO SIGN IN IF NOT SIGNED IN c l

    a s s Y o u r C o n t r o l l e r < A p p l i c a t i o n C o n t r o l l e r b e f o r e _ a c t i o n d o i f @ c u r r e n t _ u s e r . n i l ? r e d i r e c t _ t o s i g n _ i n _ p a t h , a l e r t : " P l e a s e S i g n I n " e n d e n d e n d
  6. i f @ c u r r e n t

    _ u s e r . n i l ? So, we need something that sets @ c u r r e n t _ u s e r r e d i r e c t _ t o s i g n _ i n _ p a t h So, we need a s i g n _ i n _ p a t h
  7. c l a s s A p p l i

    c a t i o n C o n t r o l l e r < A c t i o n C o n t r o l l e r : : B a s e p r o t e c t _ f r o m _ f o r g e r y w i t h : : e x c e p t i o n b e f o r e _ a c t i o n d o @ c u r r e n t _ u s e r = U s e r . f i n d _ b y i d : s e s s i o n [ : u s e r _ i d ] e n d e n d Before every single action is executed, Rails will look at the session and get the user_id It will try to nd a User by that id, so c u r r e n t _ u s e r may be nil Every action now has access to @ c u r r e n t _ u s e r
  8. SessionController: c l a s s S e s s

    i o n s C o n t r o l l e r < A p p l i c a t i o n C o n t r o l l e r d e f n e w e n d d e f c r e a t e e n d d e f d e l e t e e n d e n d Routes g e t ' s i g n _ i n ' = > ' s e s s i o n s # n e w ' , a s : : s i g n _ i n p o s t ' s i g n _ i n ' = > ' s e s s i o n s # c r e a t e ' d e l e t e ' s i g n _ i n ' = > ' s e s s i o n s # d e l e t e '
  9. We'll end up at / s i g n _

    i n if we are not already signed in. We need a form: < % = f o r m _ t a g d o % > < d i v > < % = l a b e l _ t a g : u s e r n a m e % > < % = t e x t _ f i e l d _ t a g : u s e r n a m e , p a r a m s [ : u s e r n a m e ] % > < / d i v > < d i v > < % = l a b e l _ t a g : p a s s w o r d % > < % = p a s s w o r d _ f i e l d _ t a g : p a s s w o r d , " " % > < / d i v > < d i v > < % = s u b m i t _ t a g " S i g n I n " , c l a s s : " b t n " % > < / d i v > < % e n d % >
  10. Since we did not specify a location, Rails will use

    P O S T and the / s i g n _ i n path. That matches our Sessions#create. Yay!
  11. Now we get to the core of the matter. How

    can we store a user's password, verify the user's password is correct, but never be able to reverse engineer the user's password?
  12. BCRYPT

  13. HAS_SECURE_PASSWORD c l a s s U s e r

    < A p p l i c a t i o n R e c o r d h a s _ s e c u r e _ p a s s w o r d v a l i d a t e s : u s e r n a m e , p r e s e n c e : t r u e , u n i q u e n e s s : t r u e e n d This requires us to have a database eld named p a s s w o r d _ d i g e s t .
  14. THIS REQUIRES US TO HAVE A DATABASE FIELD NAMED P

    A S S W O R D _ D I G E S T .
  15. when you set a user's password @ u s e

    r . p a s s w o r d = ' 1 2 3 4 5 ' , it will encrypt it into p a s s w o r d _ d i g e s t You cannot reverse engineer 1 2 3 4 5 You must give the password again to see if it's correct: @ u s e r . a u t h e n t i c a t e ( " 1 2 3 4 5 " ) = > # < U s e r i d = " 3 " . . . . / > @ u s e r . a u t h e n t i c a t e ( " 4 2 " ) = > n i l
  16. c l a s s S e s s i

    o n s C o n t r o l l e r < A p p l i c a t i o n C o n t r o l l e r d e f c r e a t e u s e r = U s e r . f i n d _ b y u s e r n a m e : p a r a m s [ : u s e r n a m e ] i f u s e r & & u s e r . a u t h e n t i c a t e ( p a r a m s [ : p a s s w o r d ] ) s e s s i o n [ : u s e r _ i d ] = u s e r . i d r e d i r e c t _ t o r o o t _ p a t h , n o t i c e : " S i g n e d i n ! " e l s e f l a s h . n o w [ : a l e r t ] = " S o m e t h i n g i s w r o n g w i t h y o u r u s e r n a m e a n d / o r p a s s w o r d " r e n d e r : n e w e n d e n d e n d
  17. PROTIPS

  18. c l a s s A p p l i

    c a t i o n C o n t r o l l e r < A c t i o n C o n t r o l l e r : : B a s e p r o t e c t _ f r o m _ f o r g e r y w i t h : : e x c e p t i o n b e f o r e _ a c t i o n d o @ c u r r e n t _ u s e r = U s e r . f i n d _ b y i d : s e s s i o n [ : u s e r _ i d ] e n d d e f a u t h e n t i c a t e _ u s e r ! u n l e s s @ c u r r e n t _ u s e r d o r e d i r e c t _ t o s i g n _ i n _ p a t h , n o t i c e : " P l e a s e S i g n I n " e n d e n d d e f c u r r e n t _ u s e r @ c u r r e n t _ u s e r e n d h e l p e r _ m e t h o d : c u r r e n t _ u s e r
  19. Allow you to in your controllers: c l a s

    s Y o u r C o n t r o l l e r < A p p l i c a t i o n C o n t r o l l e r b e f o r e _ a c t i o n : a u t h e n t i c a t e _ u s e r ! e n d
  20. Allows you to use in your ERB views: < %

    = i f u s e r _ s i g n e d _ i n ? % > H i < % = c u r r e n t _ u s e r % > . < % e l s e % > < % = l i n k _ t o ' S i g n U p ' , n e w _ u s e r _ p a t h % > < % e n d % >
  21. SOOO BASICALLY, WE CREATED DEVISE. It's Secure It's rather easy

    to implement It's Easy to Extend and Customize
  22. WHAT TO TAKE AWAY FOR BOTH DIY AND DEVISE @

    c u r r e n t _ u s e r is not special. It's an ActiveRecord Object All the routes and controllers aren't special: you can customize then.
  23. SECURING AN API

  24. HAS_SECURE_TOKEN Requires you to have a t o k e

    n eld. c l a s s U s e r < A c t i v e R e c o r d : : B a s e h a s _ s e c u r e _ t o k e n e n d u s e r = U s e r . n e w u s e r . s a v e u s e r . t o k e n # = > " p X 2 7 z s M N 2 V i Q K t a 1 b G f L m V J E "
  25. Each web request could then pass a token as an

    auth_token parameter. c l a s s A p i : : U s e r s C o n t r o l l e r < A p i C o n t r o l l e r b e f o r e _ a c t i o n d o @ c u r r e n t _ u s e r = U s e r . f i n d _ b y t o k e n : p a r a m s [ : a u t h _ t o k e n ] r e n d e r " A u t h T o k e n R e q u i r e d " , s t a t u s : 4 0 1 u n l e s s @ c u r r e n t _ u s e r e n d e n d
  26. SIGNING IN TO API c l a s s A

    p i : : S e s s i o n s C o n t r o l l e r < A p i C o n t r o l l e r d e f c r e a t e @ c u r r e n t _ u s e r = U s e r . f i n d _ b y u s e r n a m e : p a r a m s [ : u s e r n a m e ] i f @ c u r r e n t _ u s e r & & @ c u r r e n t _ u s e r . a u t h e n t i c a t e ( p a r a m s [ : p a s s w o r d ] ) r e n d e r j s o n : { u s e r : @ c u r r e n t _ u s e r , a u t h _ t o k e n : @ c u r r e n t _ u s e r . t o k e n } , e l s e r e n d e r e r r o r s : [ " U s e r n a m e o r P a s s w o r d i s I n v a l i d " ] , s t a t u s : 4 2 2 e n d e n d e n d
  27. DOWNSIDES There's only one token per user If regenerated, would

    sign out all phones/sessions/etc
  28. DOORKEEPER DOORKEEPER IS AN OAUTH 2 PROVIDER FOR RAILS

  29. ENABLING PASSWORD GRANT. / c o n f i g

    / i n i t i a l i z e r s / d o o r k e e p e r . r b D o o r k e e p e r . c o n f i g u r e d o o r m : a c t i v e _ r e c o r d r e s o u r c e _ o w n e r _ f r o m _ c r e d e n t i a l s d o U s e r . f i n d _ b y ( e m a i l : p a r a m s [ : u s e r n a m e ] ) . t r y ( : a u t h e n t i c a t e , p a r a m s [ : p a s s w o r d e n d a c c e s s _ t o k e n _ m e t h o d s : f r o m _ b e a r e r _ a u t h o r i z a t i o n , : f r o m _ a c c e s s _ t o k e n _ p a r a m g r a n t _ f l o w s % w ( p a s s w o r d ) e n d D o o r k e e p e r . c o n f i g u r a t i o n . t o k e n _ g r a n t _ t y p e s < < " p a s s w o r d "
  30. from_bearer_authorization (header): ' A u t h o r i

    z a t i o n ' : ' B e a r e r T O K E N H E R E ' from_access_token_param : ? a c c e s s _ t o k e n = T O K E N H E R E
  31. IN YOUR CONTROLLER c l a s s A p

    i : : B o o k s C o n t r o l l e r < A p i : : V 1 : : A p i C o n t r o l l e r b e f o r e _ a c t i o n : d o o r k e e p e r _ a u t h o r i z e ! d e f i n d e x r e n d e r j s o n : { b o o k s : c u r r e n t _ u s e r . b o o k s } e n d p r i v a t e d e f c u r r e n t _ u s e r U s e r . f i n d ( d o o r k e e p e r _ t o k e n . r e s o u r c e _ o w n e r _ i d ) i f d o o r k e e p e r _ t o k e n e n d e n d
  32. 1. Already have User with secure password 2. add to

    gem le d o o r k e e p e r 3. b u n d l e i n s t a l l 4. Add le c o n f i g / i n i t i a l i z e r s / d o o r k e e p e r . r b 5. Add to routes: u s e _ d o o r k e e p e r 6. r a i l s g e n e r a t e d o o r k e e p e r : m i g r a t i o n 7. r a i l s d b : m i g r a t e
  33. SIGNING IN POST JSON to / o a u t

    h / t o k e n { " g r a n t _ t y p e " : " p a s s w o r d " , " u s e r n a m e " : " j w o " , " p a s s w o r d " : " 1 2 3 4 5 " } Result will have { " a u t h _ t o k e n " : " e e b d a d d b 2 c 2 d e 2 8 1 7 d b d 6 b e b e 0 6 b 0 a 7 f f a 3 4 f f d 3 8 a d e b 7 d 0 " , " e x p i r e s " : 1 2 3 4 5 6 7 8 9 0 }
  34. BENEFITS OF DOORKEEPER 1. Multiple signing per account 2. Can

    be expanded later 3. Fits into Grape
  35. LIVE DEMO WE'LL DO IT LIVE

  36. Links: / t: @jwo g: @jwo Slides Code (Before and

    After)