diff --git a/Gemfile b/Gemfile index 11ebfce..e824bb2 100644 --- a/Gemfile +++ b/Gemfile @@ -15,12 +15,12 @@ gem "webpacker", "~> 5.0" gem "bootsnap", ">= 1.4.4", require: false gem "devise" -gem "devise-bootstrap-views", "~> 1.0" +gem "devise-bootstrap-views" gem "devise-i18n" - gem "administrate" gem "graphql" gem "tailwindcss-rails" +gem "httparty" group :development, :test do gem "dotenv-rails" diff --git a/Gemfile.lock b/Gemfile.lock index 42e8f19..2fd4fc7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -113,6 +113,9 @@ GEM graphql (1.12.14) graphql_playground-rails (2.1.0) rails (>= 5.1.0) + httparty (0.18.1) + mime-types (~> 3.0) + multi_xml (>= 0.5.2) i18n (1.8.10) concurrent-ruby (~> 1.0) jbuilder (2.11.2) @@ -143,12 +146,16 @@ GEM mini_mime (>= 0.1.1) marcel (1.0.1) method_source (1.0.0) + mime-types (3.3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2021.0704) mini_mime (1.1.0) mini_portile2 (2.6.1) minitest (5.14.4) momentjs-rails (2.20.1) railties (>= 3.1) msgpack (1.4.2) + multi_xml (0.6.0) nio4r (2.5.8) nokogiri (1.12.1) mini_portile2 (~> 2.6.1) @@ -304,11 +311,12 @@ DEPENDENCIES bootsnap (>= 1.4.4) capybara devise - devise-bootstrap-views (~> 1.0) + devise-bootstrap-views devise-i18n dotenv-rails graphql graphql_playground-rails + httparty jbuilder (~> 2.7) listen (~> 3.3) pg (~> 1.1) diff --git a/app/controllers/concerns/authenticable.rb b/app/controllers/concerns/authenticable.rb new file mode 100644 index 0000000..ddcc81e --- /dev/null +++ b/app/controllers/concerns/authenticable.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +module Authenticable + def current_auth + @current_auth ||= Auth::Authenticate.new(bearer_token).profile + end + + def bearer_token + pattern = /^Bearer / + header = request.headers["Authorization"] + header.gsub(pattern, "") if header&.match(pattern) + end +end diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index c161a37..8f9e23c 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -1,17 +1,16 @@ # frozen_string_literal: true class GraphqlController < ApplicationController - # If accessing from outside this domain, nullify the session - # This allows for outside API access while preventing CSRF attacks, - # but you'll have to authenticate your user separately - # protect_from_forgery with: :null_session + include Authenticable + + protect_from_forgery with: :null_session def execute variables = prepare_variables(params[:variables]) query = params[:query] operation_name = params[:operationName] context = { - # Query context goes here, for example: - current_user: current_admin_user, + current_user: current_admin_user, # || current_auth.current_user, + current_auth: current_auth, } result = XStakeSchema.execute(query, variables: variables, context: context, operation_name: operation_name) render(json: result) @@ -22,7 +21,6 @@ class GraphqlController < ApplicationController private - # Handle variables in form data, JSON body, or a blank value def prepare_variables(variables_param) case variables_param when String @@ -34,7 +32,7 @@ class GraphqlController < ApplicationController when Hash variables_param when ActionController::Parameters - variables_param.to_unsafe_hash # GraphQL-Ruby will validate name and type of incoming variables. + variables_param.to_unsafe_hash when nil {} else diff --git a/app/services/auth/auth0_client.rb b/app/services/auth/auth0_client.rb new file mode 100644 index 0000000..8d782b0 --- /dev/null +++ b/app/services/auth/auth0_client.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true +module Auth + class Auth0Client + class << self + def find_profile(token) + Profile.new(user_profile_attributes(token)) + end + + def user_profile_attributes(token) + HTTParty.get( + "https://#{ENV["AUTH_DOMAIN"]}/userinfo", + headers: { + "Content-Type" => "application/json", + "Authorization": "Bearer #{token}", + } + ).with_indifferent_access + end + end + end +end diff --git a/app/services/auth/authenticate.rb b/app/services/auth/authenticate.rb new file mode 100644 index 0000000..dce1225 --- /dev/null +++ b/app/services/auth/authenticate.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true +module Auth + class Authenticate + attr_reader :jwt_token + + def initialize(jwt_token) + @jwt_token = jwt_token + end + + def profile + Auth0Client.find_profile(jwt_token) + end + end +end diff --git a/app/services/auth/profile.rb b/app/services/auth/profile.rb new file mode 100644 index 0000000..6574c51 --- /dev/null +++ b/app/services/auth/profile.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true +module Auth + class Profile + attr_reader :id, :email + + def initialize(attributes) + @id = attributes[:sub] + @email = attributes[:email] + end + + def customer + @customer ||= Customer.find_by(email: email, auth_id: id) + end + end +end