Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Federating Oauth In Ruby

Federating Oauth In Ruby

Presentation given at Railsconf 2015 about a sane approach to federating Oauth in Rails by Lance Gleason of Polyglot Programming

Lance Gleason

April 21, 2015
Tweet

More Decks by Lance Gleason

Other Decks in Technology

Transcript

  1. Introductions

    View Slide

  2. Twitter @lgleasain
    Github lgleasain
    www.lancegleason.com
    www.polyglotprogrammincinc.com
    [email protected]

    View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. http://www.polyglotprogramminginc.com/purr-
    programming-2-0/

    View Slide

  13. View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. What
    Are
    Services

    View Slide

  18. View Slide

  19. View Slide

  20. View Slide

  21. View Slide

  22. View Slide

  23. View Slide

  24. View Slide

  25. View Slide

  26. Why
    Services

    View Slide

  27. View Slide

  28. View Slide

  29. View Slide

  30. What is
    Federation

    View Slide

  31. View Slide

  32. Single
    Sign On

    View Slide

  33. View Slide

  34. View Slide

  35. View Slide

  36. View Slide

  37. View Slide

  38. View Slide

  39. View Slide

  40. XML

    View Slide

  41. View Slide

  42. View Slide

  43. View Slide

  44. View Slide

  45. Authorization and
    Authentication

    View Slide

  46. Authentication
    /Authorization

    View Slide

  47. Single
    Point of
    Success

    View Slide

  48. Bad For
    Mixed
    Use

    View Slide

  49. Complex

    View Slide

  50. Mostly
    Read Only
    Content

    View Slide

  51. Half Requires
    Authenication/
    Authorizaztion

    View Slide

  52. Needs To
    Scale

    View Slide

  53. View Slide

  54. Bearer
    Token

    View Slide

  55. View Slide

  56. View Slide

  57. View Slide

  58. View Slide

  59. View Slide

  60. View Slide

  61. View Slide

  62. Doorkeeper

    View Slide

  63. gem 'doorkeeper'
    rails generate doorkeeper:install
    rails generate doorkeeper:migration
    rake db:migrate

    View Slide

  64. Every
    Instance
    Has To Have
    a Datastore

    View Slide

  65. View Slide

  66. View Slide

  67. View Slide

  68. resource_owner_authenticator do
    @user = env[:clearance].current_user
    unless @user
    session[:return_to] = request.fullpath
    redirect_to(routes.sign_in_url)
    end
    @user
    end

    View Slide

  69. View Slide

  70. resource_owner_authenticator do
    #could be a call to a central
    authentication service.
    @user = env[:clearance].current_user
    unless @user
    session[:return_to] = request.fullpath
    redirect_to(routes.sign_in_url)
    end
    @user
    end

    View Slide

  71. View Slide

  72. View Slide

  73. module OmniAuth
    module Strategies
    autoload :VulcanService, Rails.root.join('lib', 'strategies',
    'vulcan_service')
    autoload :KlingonManager, Rails.root.join('lib', 'strategies',
    'klingon_manager')
    autoload :Bieber, Rails.root.join('lib', 'strategies', 'beiber')
    end
    end
    Rails.application.config.middleware.use OmniAuth::Builder do
    provider :Vulcan, "#{APP_CONFIG['vulcan_url']}/login"
    provider :KlingonService, "#{APP_CONFIG['klingon_service_url']}/login"
    provider :BeiberManager, "#{APP_CONFIG['beiber_manager_url']}/login"
    provider :developer
    end

    View Slide

  74. require 'strategies/common'
    module OmniAuth
    module Strategies
    class VulcanService
    include Common
    include OmniAuth::Strategy
    args [:authentication_url]
    def request_phase
    # some request code to authenticate
    # live long and prosper
    end
    #todo: add checks to make sure the values returned here are valid
    def callback_phase
    request = Rack::Request.new env
    cookies = request.cookies
    response = Rack::Response.new
    cookie_monster = VulcanCookieMonster.new cookies
    common_vulcan_callback cookie_monster: cookie_monster, response: response,
    cookies: cookies
    end
    private
    def strategy_id
    'comcast_tve'
    end
    end
    end
    end

    View Slide

  75. View Slide

  76. View Slide

  77. View Slide

  78. Delegation

    View Slide

  79. View Slide

  80. class ApplicationController < ActionController::API
    def check_authorization
    authorization = request.headers[‘Authorization']
    if authorization
    party_response = HTTParty.get("#{APP_CONFIG['doorkeeper_service_url']}:#{ENV[DOORKEEPER_SERVICE_PORT']}/
    check_key.json",
    query: {'signature' => Hancock.sign, 'oauth_token' => authorization})
    parsed_response = party_response.parsed_response
    if parsed_response['user_key']
    expires = Marshal.load(parsed_response['expires_at'].force_encoding('UTF-8')).to_json
    @user = { 'user_id' => parsed_response['user_id'], 'user_key' => parsed_response['user_key'],
    'email' => parsed_response['email'], 'expires_at' => expires }
    else
    response.status = 401
    render json: {authorized: false} and return
    end
    end
    else
    response.status = 401
    render json: {authorized: false} and return
    end
    end
    end

    View Slide

  81. Single
    Point of
    Success

    View Slide

  82. Under
    Load

    View Slide

  83. Cached
    Delegation

    View Slide

  84. View Slide

  85. View Slide

  86. require 'redis'
    class ApplicationController < ActionController::API
    def check_authorization
    authorization = request.headers['Authorization']
    if authorization
    redis = Redis.new(:host => ENV['REDIS_HOST'], :port => ENV['REDIS_PORT'])
    redis_user = redis.get(authorization)
    if redis_user != nil
    @user = JSON.parse(redis_user)
    expires_at = DateTime.parse(@user['expires_at'])
    end
    if [email protected] or expires_at < DateTime.now
    party_response =
    HTTParty.get("#{APP_CONFIG['doorkeeper_service_url']}:#{ENV['DOORKEEPER_SERVICE_PORT']}/check_key.json",
    query: {'signature' => Hancock.sign, 'oauth_token' => authorization})
    parsed_response = party_response.parsed_response
    if parsed_response['user_key']
    expires = Marshal.load(parsed_response['expires_at'].force_encoding('UTF-8')).to_json
    @user = { 'user_id' => parsed_response['user_id'], 'user_key' => parsed_response['user_key'],
    'email' => parsed_response['email'], 'expires_at' => expires }
    redis.set authorization, @user.to_json
    else
    response.status = 401
    render json: {authorized: false} and return
    end
    end
    else
    response.status = 401
    render json: {authorized: false} and return
    end
    end
    end

    View Slide

  87. View Slide

  88. View Slide

  89. Oauth
    Sucks

    View Slide

  90. Yeah
    But….

    View Slide

  91. It’s a
    Standard

    View Slide

  92. Roll Your
    Own Is
    Less Secure

    View Slide

  93. JWT’s Are
    Roll Your
    Own

    View Slide

  94. Default
    Doorkeeper
    is Too

    View Slide

  95. View Slide

  96. View Slide

  97. 97

    View Slide

  98. Twitter @lgleasain
    Github lgleasain
    www.lancegleason.com
    www.polyglotprogrammincinc.com
    [email protected]

    View Slide