From f5dc7a10a3ee521827f8b53e07a3922e879ff707 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sun, 26 Oct 2025 15:27:43 +0100 Subject: [PATCH] Implement OmniAuth GitHub authentication --- Gemfile | 5 +- Gemfile.lock | 64 ++++++++++++++++--- .../users/omniauth_callbacks_controller.rb | 19 ++++++ app/models/user.rb | 16 ++++- app/views/devise/sessions/new.html.erb | 6 +- config/initializers/devise.rb | 2 +- config/routes.rb | 3 +- 7 files changed, 100 insertions(+), 15 deletions(-) create mode 100644 app/controllers/users/omniauth_callbacks_controller.rb diff --git a/Gemfile b/Gemfile index 9ecea93e..8267c832 100644 --- a/Gemfile +++ b/Gemfile @@ -24,6 +24,9 @@ gem 'jwt', '~> 2.8' gem 'kaminari' gem 'lograge' gem 'oj' +gem 'omniauth-github', '~> 2.0.0' +gem 'omniauth-google-oauth2' +gem 'omniauth-rails_csrf_protection' gem 'parallel' gem 'pg' gem 'prometheus_exporter' @@ -49,7 +52,7 @@ gem 'sprockets-rails' gem 'stackprof' gem 'stimulus-rails' gem 'strong_migrations', '>= 2.4.0' -gem 'tailwindcss-rails', '>= 3.3.2' +gem 'tailwindcss-rails', '= 3.3.2' gem 'turbo-rails', '>= 2.0.17' gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] diff --git a/Gemfile.lock b/Gemfile.lock index 513f3d86..f818ff05 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -171,6 +171,12 @@ GEM factory_bot (~> 6.5) railties (>= 6.1.0) fakeredis (0.1.4) + faraday (2.14.0) + faraday-net_http (>= 2.0, < 3.5) + json + logger + faraday-net_http (3.4.1) + net-http (>= 0.5.0) ffaker (2.25.0) ffi (1.17.2-aarch64-linux-gnu) ffi (1.17.2-arm-linux-gnu) @@ -196,6 +202,7 @@ GEM rgeo-geojson (~> 2.1) zeitwerk (~> 2.5) hashdiff (1.1.2) + hashie (5.0.0) httparty (0.23.1) csv mini_mime (>= 1.0.0) @@ -256,6 +263,8 @@ GEM multi_json (1.15.0) multi_xml (0.7.1) bigdecimal (~> 3.1) + net-http (0.6.0) + uri net-imap (0.5.12) date net-protocol @@ -279,9 +288,36 @@ GEM racc (~> 1.4) nokogiri (1.18.10-x86_64-linux-gnu) racc (~> 1.4) + oauth2 (2.0.17) + faraday (>= 0.17.3, < 4.0) + jwt (>= 1.0, < 4.0) + logger (~> 1.2) + multi_xml (~> 0.5) + rack (>= 1.2, < 4) + snaky_hash (~> 2.0, >= 2.0.3) + version_gem (~> 1.1, >= 1.1.9) oj (3.16.11) bigdecimal (>= 3.0) ostruct (>= 0.2) + omniauth (2.1.4) + hashie (>= 3.4.6) + logger + rack (>= 2.2.3) + rack-protection + omniauth-github (2.0.1) + omniauth (~> 2.0) + omniauth-oauth2 (~> 1.8) + omniauth-google-oauth2 (1.2.1) + jwt (>= 2.9.2) + oauth2 (~> 2.0) + omniauth (~> 2.0) + omniauth-oauth2 (~> 1.8) + omniauth-oauth2 (1.8.0) + oauth2 (>= 1.4, < 3) + omniauth (~> 2.0) + omniauth-rails_csrf_protection (1.0.2) + actionpack (>= 4.2) + omniauth (~> 2.0) optimist (3.2.1) orm_adapter (0.5.0) ostruct (0.6.1) @@ -321,6 +357,10 @@ GEM raabro (1.4.0) racc (1.8.1) rack (3.2.2) + rack-protection (4.2.1) + base64 (>= 0.1.0) + logger (>= 1.6.0) + rack (>= 3.0.0, < 4) rack-session (2.1.1) base64 (>= 0.1.0) rack (>= 3.0.0) @@ -474,6 +514,9 @@ GEM simplecov_json_formatter (~> 0.1) simplecov-html (0.13.1) simplecov_json_formatter (0.1.4) + snaky_hash (2.0.3) + hashie (>= 0.1.0, < 6) + version_gem (>= 1.1.8, < 3) sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) @@ -491,14 +534,15 @@ GEM attr_extras (>= 6.2.4) diff-lcs patience_diff - tailwindcss-rails (4.3.0) + tailwindcss-rails (3.3.2) railties (>= 7.0.0) - tailwindcss-ruby (~> 4.0) - tailwindcss-ruby (4.1.13) - tailwindcss-ruby (4.1.13-aarch64-linux-gnu) - tailwindcss-ruby (4.1.13-arm64-darwin) - tailwindcss-ruby (4.1.13-x86_64-darwin) - tailwindcss-ruby (4.1.13-x86_64-linux-gnu) + tailwindcss-ruby (~> 3.0) + tailwindcss-ruby (3.4.17) + tailwindcss-ruby (3.4.17-aarch64-linux) + tailwindcss-ruby (3.4.17-arm-linux) + tailwindcss-ruby (3.4.17-arm64-darwin) + tailwindcss-ruby (3.4.17-x86_64-darwin) + tailwindcss-ruby (3.4.17-x86_64-linux) thor (1.4.0) timeout (0.4.3) tsort (0.2.0) @@ -513,6 +557,7 @@ GEM unicode-emoji (4.1.0) uri (1.0.3) useragent (0.16.11) + version_gem (1.1.9) warden (1.2.9) rack (>= 2.0.9) webmock (3.25.1) @@ -566,6 +611,9 @@ DEPENDENCIES kaminari lograge oj + omniauth-github (~> 2.0.0) + omniauth-google-oauth2 + omniauth-rails_csrf_protection parallel pg prometheus_exporter @@ -600,7 +648,7 @@ DEPENDENCIES stimulus-rails strong_migrations (>= 2.4.0) super_diff - tailwindcss-rails (>= 3.3.2) + tailwindcss-rails (= 3.3.2) turbo-rails (>= 2.0.17) tzinfo-data webmock diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb new file mode 100644 index 00000000..53da9f19 --- /dev/null +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController + def github + @user = User.from_omniauth(request.env['omniauth.auth']) + + if @user.persisted? + flash[:notice] = I18n.t 'devise.omniauth_callbacks.success', kind: 'GitHub' + sign_in_and_redirect @user, event: :authentication + else + session['devise.github_data'] = request.env['omniauth.auth'].except('extra') + redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n") + end + end + + def failure + redirect_to root_path, alert: "Authentication failed: #{params[:message]}" + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 71269d64..737b509b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,7 +3,8 @@ class User < ApplicationRecord # rubocop:disable Metrics/ClassLength include UserFamily devise :database_authenticatable, :registerable, - :recoverable, :rememberable, :validatable, :trackable + :recoverable, :rememberable, :validatable, :trackable, + :omniauthable, omniauth_providers: %i[github] has_many :points, dependent: :destroy has_many :imports, dependent: :destroy @@ -145,6 +146,19 @@ class User < ApplicationRecord # rubocop:disable Metrics/ClassLength points.where.not(city: [nil, '']).distinct.pluck(:city).compact end + def self.from_omniauth(access_token) + data = access_token.info + user = User.where(email: data['email']).first + + return user if user + + binding.pry + User.create( + email: data['email'], + password: Devise.friendly_token[0, 20] + ) + end + private def create_api_key diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index b471a5cf..0de3bae8 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -47,10 +47,10 @@
<%= f.submit (@invitation ? "Sign in & Accept Invitation" : "Log in"), class: 'btn btn-primary' %>
+ <% end %> - <% unless @invitation %> - <%= render "devise/shared/links" %> - <% end %> + <% unless @invitation %> + <%= render "devise/shared/links" %> <% end %> diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 7b207ed3..2c9e31c7 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -265,7 +265,7 @@ Devise.setup do |config| # ==> OmniAuth # Add a new OmniAuth provider. Check the wiki for more information on setting # up on your models and hooks. - # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' + config.omniauth :github, ENV['GITHUB_OAUTH_CLIENT_ID'], ENV['GITHUB_OAUTH_CLIENT_SECRET'], scope: 'user' # ==> Warden configuration # If you want to use other strategies, that are not supported by Devise, or diff --git a/config/routes.rb b/config/routes.rb index d34aa775..16961f7e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -103,7 +103,8 @@ Rails.application.routes.draw do devise_for :users, controllers: { registrations: 'users/registrations', - sessions: 'users/sessions' + sessions: 'users/sessions', + omniauth_callbacks: 'users/omniauth_callbacks' } resources :metrics, only: [:index]