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. 1.

    RAILS AUTH - DIY AUTHENTICATION IN RAILS & API, DIY

    INSTEAD OF DEVISE Created by / Jesse Wolgamott @jwo
  2. 3.

    WHY NOT JUST USE DEVISE? Di cult to understand Di

    cult to customize Di cult to extend
  3. 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!
  4. 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
  5. 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
  6. 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
  7. 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 '
  8. 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 % >
  9. 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!
  10. 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?
  11. 12.
  12. 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 .
  13. 14.
  14. 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
  15. 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
  16. 17.
  17. 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
  18. 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
  19. 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 % >
  20. 21.

    SOOO BASICALLY, WE CREATED DEVISE. It's Secure It's rather easy

    to implement It's Easy to Extend and Customize
  21. 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.
  22. 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 "
  23. 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
  24. 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
  25. 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 "
  26. 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
  27. 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
  28. 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
  29. 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 }
  30. 34.