Save 37% off PRO during our Black Friday Sale! »

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

89e0c5e7bfe1c613b1b9287d89963e73?s=128

Lance Gleason

April 21, 2015
Tweet

Transcript

  1. Introductions

  2. Twitter @lgleasain Github lgleasain www.lancegleason.com www.polyglotprogrammincinc.com lgleason@polyglotprogramminginc.com

  3. None
  4. None
  5. None
  6. None
  7. None
  8. None
  9. None
  10. None
  11. None
  12. http://www.polyglotprogramminginc.com/purr- programming-2-0/

  13. None
  14. None
  15. None
  16. None
  17. What Are Services

  18. None
  19. None
  20. None
  21. None
  22. None
  23. None
  24. None
  25. None
  26. Why Services

  27. None
  28. None
  29. None
  30. What is Federation

  31. None
  32. Single Sign On

  33. None
  34. None
  35. None
  36. None
  37. None
  38. None
  39. None
  40. XML

  41. None
  42. None
  43. None
  44. None
  45. Authorization and Authentication

  46. Authentication /Authorization

  47. Single Point of Success

  48. Bad For Mixed Use

  49. Complex

  50. Mostly Read Only Content

  51. Half Requires Authenication/ Authorizaztion

  52. Needs To Scale

  53. None
  54. Bearer Token

  55. None
  56. None
  57. None
  58. None
  59. None
  60. None
  61. None
  62. Doorkeeper

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

  64. Every Instance Has To Have a Datastore

  65. None
  66. None
  67. None
  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
  69. None
  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
  71. None
  72. None
  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
  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
  75. None
  76. None
  77. None
  78. Delegation

  79. None
  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
  81. Single Point of Success

  82. Under Load

  83. Cached Delegation

  84. None
  85. None
  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 !@user 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
  87. None
  88. None
  89. Oauth Sucks

  90. Yeah But….

  91. It’s a Standard

  92. Roll Your Own Is Less Secure

  93. JWT’s Are Roll Your Own

  94. Default Doorkeeper is Too

  95. None
  96. None
  97. 97

  98. Twitter @lgleasain Github lgleasain www.lancegleason.com www.polyglotprogrammincinc.com lgleason@polyglotprogramminginc.com