mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 17:21:38 -05:00
commit
88134a0a2b
19 changed files with 654 additions and 14 deletions
14
CHANGELOG.md
14
CHANGELOG.md
|
|
@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
# OIDC and KML support release
|
||||||
|
|
||||||
|
To configure your OIDC provider, set the following environment variables:
|
||||||
|
|
||||||
|
```
|
||||||
|
OIDC_CLIENT_ID=client_id_example
|
||||||
|
OIDC_CLIENT_SECRET=client_secret_example
|
||||||
|
OIDC_ISSUER=https://authentik.yourdomain.com/application/o/dawarich/
|
||||||
|
OIDC_REDIRECT_URI=https://your-dawarich-url.com/users/auth/openid_connect/callback
|
||||||
|
```
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
- Support for KML file uploads. #350
|
- Support for KML file uploads. #350
|
||||||
|
|
@ -18,6 +29,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
- Internal redis settings updated to implement support for connecting to Redis via unix socket. #1706
|
- Internal redis settings updated to implement support for connecting to Redis via unix socket. #1706
|
||||||
|
- Implemented authentication via GitHub and Google for Dawarich Cloud.
|
||||||
|
- Implemented OpenID Connect authentication for self-hosted Dawarich instances. #66
|
||||||
|
|
||||||
|
|
||||||
# [0.35.1] - 2025-11-09
|
# [0.35.1] - 2025-11-09
|
||||||
|
|
||||||
|
|
|
||||||
6
Gemfile
6
Gemfile
|
|
@ -5,7 +5,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
||||||
|
|
||||||
ruby File.read('.ruby-version').strip
|
ruby File.read('.ruby-version').strip
|
||||||
|
|
||||||
gem 'activerecord-postgis-adapter', '~> 11.0'
|
gem 'activerecord-postgis-adapter', '11.0'
|
||||||
# https://meta.discourse.org/t/cant-rebuild-due-to-aws-sdk-gem-bump-and-new-aws-data-integrity-protections/354217/40
|
# https://meta.discourse.org/t/cant-rebuild-due-to-aws-sdk-gem-bump-and-new-aws-data-integrity-protections/354217/40
|
||||||
gem 'aws-sdk-core', '~> 3.215.1', require: false
|
gem 'aws-sdk-core', '~> 3.215.1', require: false
|
||||||
gem 'aws-sdk-kms', '~> 1.96.0', require: false
|
gem 'aws-sdk-kms', '~> 1.96.0', require: false
|
||||||
|
|
@ -24,6 +24,10 @@ gem 'jwt', '~> 2.8'
|
||||||
gem 'kaminari'
|
gem 'kaminari'
|
||||||
gem 'lograge'
|
gem 'lograge'
|
||||||
gem 'oj'
|
gem 'oj'
|
||||||
|
gem 'omniauth-github', '~> 2.0.0'
|
||||||
|
gem 'omniauth-google-oauth2'
|
||||||
|
gem 'omniauth_openid_connect'
|
||||||
|
gem 'omniauth-rails_csrf_protection'
|
||||||
gem 'parallel'
|
gem 'parallel'
|
||||||
gem 'pg'
|
gem 'pg'
|
||||||
gem 'prometheus_exporter'
|
gem 'prometheus_exporter'
|
||||||
|
|
|
||||||
99
Gemfile.lock
99
Gemfile.lock
|
|
@ -86,8 +86,10 @@ GEM
|
||||||
uri (>= 0.13.1)
|
uri (>= 0.13.1)
|
||||||
addressable (2.8.7)
|
addressable (2.8.7)
|
||||||
public_suffix (>= 2.0.2, < 7.0)
|
public_suffix (>= 2.0.2, < 7.0)
|
||||||
|
aes_key_wrap (1.1.0)
|
||||||
ast (2.4.3)
|
ast (2.4.3)
|
||||||
attr_extras (7.1.0)
|
attr_extras (7.1.0)
|
||||||
|
attr_required (1.0.2)
|
||||||
aws-eventstream (1.3.2)
|
aws-eventstream (1.3.2)
|
||||||
aws-partitions (1.1072.0)
|
aws-partitions (1.1072.0)
|
||||||
aws-sdk-core (3.215.1)
|
aws-sdk-core (3.215.1)
|
||||||
|
|
@ -108,6 +110,7 @@ GEM
|
||||||
bcrypt (3.1.20)
|
bcrypt (3.1.20)
|
||||||
benchmark (0.4.1)
|
benchmark (0.4.1)
|
||||||
bigdecimal (3.3.1)
|
bigdecimal (3.3.1)
|
||||||
|
bindata (2.5.1)
|
||||||
bootsnap (1.18.6)
|
bootsnap (1.18.6)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
brakeman (7.1.0)
|
brakeman (7.1.0)
|
||||||
|
|
@ -161,6 +164,8 @@ GEM
|
||||||
dotenv (= 3.1.8)
|
dotenv (= 3.1.8)
|
||||||
railties (>= 6.1)
|
railties (>= 6.1)
|
||||||
drb (2.2.3)
|
drb (2.2.3)
|
||||||
|
email_validator (2.2.4)
|
||||||
|
activemodel
|
||||||
erb (5.1.3)
|
erb (5.1.3)
|
||||||
erubi (1.13.1)
|
erubi (1.13.1)
|
||||||
et-orbi (1.4.0)
|
et-orbi (1.4.0)
|
||||||
|
|
@ -171,6 +176,14 @@ GEM
|
||||||
factory_bot (~> 6.5)
|
factory_bot (~> 6.5)
|
||||||
railties (>= 6.1.0)
|
railties (>= 6.1.0)
|
||||||
fakeredis (0.1.4)
|
fakeredis (0.1.4)
|
||||||
|
faraday (2.14.0)
|
||||||
|
faraday-net_http (>= 2.0, < 3.5)
|
||||||
|
json
|
||||||
|
logger
|
||||||
|
faraday-follow_redirects (0.4.0)
|
||||||
|
faraday (>= 1, < 3)
|
||||||
|
faraday-net_http (3.4.1)
|
||||||
|
net-http (>= 0.5.0)
|
||||||
ffaker (2.25.0)
|
ffaker (2.25.0)
|
||||||
ffi (1.17.2-aarch64-linux-gnu)
|
ffi (1.17.2-aarch64-linux-gnu)
|
||||||
ffi (1.17.2-arm-linux-gnu)
|
ffi (1.17.2-arm-linux-gnu)
|
||||||
|
|
@ -196,6 +209,7 @@ GEM
|
||||||
rgeo-geojson (~> 2.1)
|
rgeo-geojson (~> 2.1)
|
||||||
zeitwerk (~> 2.5)
|
zeitwerk (~> 2.5)
|
||||||
hashdiff (1.1.2)
|
hashdiff (1.1.2)
|
||||||
|
hashie (5.0.0)
|
||||||
httparty (0.23.1)
|
httparty (0.23.1)
|
||||||
csv
|
csv
|
||||||
mini_mime (>= 1.0.0)
|
mini_mime (>= 1.0.0)
|
||||||
|
|
@ -213,6 +227,13 @@ GEM
|
||||||
reline (>= 0.4.2)
|
reline (>= 0.4.2)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.15.0)
|
json (2.15.0)
|
||||||
|
json-jwt (1.17.0)
|
||||||
|
activesupport (>= 4.2)
|
||||||
|
aes_key_wrap
|
||||||
|
base64
|
||||||
|
bindata
|
||||||
|
faraday (~> 2.0)
|
||||||
|
faraday-follow_redirects
|
||||||
json-schema (5.0.1)
|
json-schema (5.0.1)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
jwt (2.10.1)
|
jwt (2.10.1)
|
||||||
|
|
@ -256,6 +277,8 @@ GEM
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multi_xml (0.7.1)
|
multi_xml (0.7.1)
|
||||||
bigdecimal (~> 3.1)
|
bigdecimal (~> 3.1)
|
||||||
|
net-http (0.6.0)
|
||||||
|
uri
|
||||||
net-imap (0.5.12)
|
net-imap (0.5.12)
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
|
|
@ -279,9 +302,52 @@ GEM
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.18.10-x86_64-linux-gnu)
|
nokogiri (1.18.10-x86_64-linux-gnu)
|
||||||
racc (~> 1.4)
|
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)
|
oj (3.16.11)
|
||||||
bigdecimal (>= 3.0)
|
bigdecimal (>= 3.0)
|
||||||
ostruct (>= 0.2)
|
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)
|
||||||
|
omniauth_openid_connect (0.8.0)
|
||||||
|
omniauth (>= 1.9, < 3)
|
||||||
|
openid_connect (~> 2.2)
|
||||||
|
openid_connect (2.3.1)
|
||||||
|
activemodel
|
||||||
|
attr_required (>= 1.0.0)
|
||||||
|
email_validator
|
||||||
|
faraday (~> 2.0)
|
||||||
|
faraday-follow_redirects
|
||||||
|
json-jwt (>= 1.16)
|
||||||
|
mail
|
||||||
|
rack-oauth2 (~> 2.2)
|
||||||
|
swd (~> 2.0)
|
||||||
|
tzinfo
|
||||||
|
validate_url
|
||||||
|
webfinger (~> 2.0)
|
||||||
optimist (3.2.1)
|
optimist (3.2.1)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
ostruct (0.6.1)
|
ostruct (0.6.1)
|
||||||
|
|
@ -321,6 +387,17 @@ GEM
|
||||||
raabro (1.4.0)
|
raabro (1.4.0)
|
||||||
racc (1.8.1)
|
racc (1.8.1)
|
||||||
rack (3.2.3)
|
rack (3.2.3)
|
||||||
|
rack-oauth2 (2.3.0)
|
||||||
|
activesupport
|
||||||
|
attr_required
|
||||||
|
faraday (~> 2.0)
|
||||||
|
faraday-follow_redirects
|
||||||
|
json-jwt (>= 1.11.0)
|
||||||
|
rack (>= 2.1.0)
|
||||||
|
rack-protection (4.2.1)
|
||||||
|
base64 (>= 0.1.0)
|
||||||
|
logger (>= 1.6.0)
|
||||||
|
rack (>= 3.0.0, < 4)
|
||||||
rack-session (2.1.1)
|
rack-session (2.1.1)
|
||||||
base64 (>= 0.1.0)
|
base64 (>= 0.1.0)
|
||||||
rack (>= 3.0.0)
|
rack (>= 3.0.0)
|
||||||
|
|
@ -475,6 +552,9 @@ GEM
|
||||||
simplecov_json_formatter (~> 0.1)
|
simplecov_json_formatter (~> 0.1)
|
||||||
simplecov-html (0.13.1)
|
simplecov-html (0.13.1)
|
||||||
simplecov_json_formatter (0.1.4)
|
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)
|
sprockets (4.2.1)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
rack (>= 2.2.4, < 4)
|
rack (>= 2.2.4, < 4)
|
||||||
|
|
@ -492,6 +572,11 @@ GEM
|
||||||
attr_extras (>= 6.2.4)
|
attr_extras (>= 6.2.4)
|
||||||
diff-lcs
|
diff-lcs
|
||||||
patience_diff
|
patience_diff
|
||||||
|
swd (2.0.3)
|
||||||
|
activesupport (>= 3)
|
||||||
|
attr_required (>= 0.0.5)
|
||||||
|
faraday (~> 2.0)
|
||||||
|
faraday-follow_redirects
|
||||||
tailwindcss-rails (3.3.2)
|
tailwindcss-rails (3.3.2)
|
||||||
railties (>= 7.0.0)
|
railties (>= 7.0.0)
|
||||||
tailwindcss-ruby (~> 3.0)
|
tailwindcss-ruby (~> 3.0)
|
||||||
|
|
@ -515,8 +600,16 @@ GEM
|
||||||
unicode-emoji (4.1.0)
|
unicode-emoji (4.1.0)
|
||||||
uri (1.0.4)
|
uri (1.0.4)
|
||||||
useragent (0.16.11)
|
useragent (0.16.11)
|
||||||
|
validate_url (1.0.15)
|
||||||
|
activemodel (>= 3.0.0)
|
||||||
|
public_suffix
|
||||||
|
version_gem (1.1.9)
|
||||||
warden (1.2.9)
|
warden (1.2.9)
|
||||||
rack (>= 2.0.9)
|
rack (>= 2.0.9)
|
||||||
|
webfinger (2.1.3)
|
||||||
|
activesupport
|
||||||
|
faraday (~> 2.0)
|
||||||
|
faraday-follow_redirects
|
||||||
webmock (3.25.1)
|
webmock (3.25.1)
|
||||||
addressable (>= 2.8.0)
|
addressable (>= 2.8.0)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
|
|
@ -540,7 +633,7 @@ PLATFORMS
|
||||||
x86_64-linux
|
x86_64-linux
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
activerecord-postgis-adapter (~> 11.0)
|
activerecord-postgis-adapter (= 11.0)
|
||||||
aws-sdk-core (~> 3.215.1)
|
aws-sdk-core (~> 3.215.1)
|
||||||
aws-sdk-kms (~> 1.96.0)
|
aws-sdk-kms (~> 1.96.0)
|
||||||
aws-sdk-s3 (~> 1.177.0)
|
aws-sdk-s3 (~> 1.177.0)
|
||||||
|
|
@ -568,6 +661,10 @@ DEPENDENCIES
|
||||||
kaminari
|
kaminari
|
||||||
lograge
|
lograge
|
||||||
oj
|
oj
|
||||||
|
omniauth-github (~> 2.0.0)
|
||||||
|
omniauth-google-oauth2
|
||||||
|
omniauth-rails_csrf_protection
|
||||||
|
omniauth_openid_connect
|
||||||
parallel
|
parallel
|
||||||
pg
|
pg
|
||||||
prometheus_exporter
|
prometheus_exporter
|
||||||
|
|
|
||||||
54
app/controllers/users/omniauth_callbacks_controller.rb
Normal file
54
app/controllers/users/omniauth_callbacks_controller.rb
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||||
|
def github
|
||||||
|
handle_auth('GitHub')
|
||||||
|
end
|
||||||
|
|
||||||
|
def google_oauth2
|
||||||
|
handle_auth('Google')
|
||||||
|
end
|
||||||
|
|
||||||
|
def openid_connect
|
||||||
|
handle_auth('OpenID Connect')
|
||||||
|
end
|
||||||
|
|
||||||
|
def failure
|
||||||
|
error_type = request.env['omniauth.error.type']
|
||||||
|
error = request.env['omniauth.error']
|
||||||
|
|
||||||
|
# Provide user-friendly error messages
|
||||||
|
error_message =
|
||||||
|
case error_type
|
||||||
|
when :invalid_credentials
|
||||||
|
'Invalid credentials. Please check your username and password.'
|
||||||
|
when :timeout
|
||||||
|
'Connection timeout. Please try again.'
|
||||||
|
when :csrf_detected
|
||||||
|
'Security error detected. Please try again.'
|
||||||
|
else
|
||||||
|
if error&.message&.include?('Discovery')
|
||||||
|
'Unable to connect to authentication provider. Please contact your administrator.'
|
||||||
|
elsif error&.message&.include?('Issuer mismatch')
|
||||||
|
'Authentication provider configuration error. Please contact your administrator.'
|
||||||
|
else
|
||||||
|
"Authentication failed: #{params[:message] || error&.message || 'Unknown error'}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to root_path, alert: error_message
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def handle_auth(provider)
|
||||||
|
@user = User.from_omniauth(request.env['omniauth.auth'])
|
||||||
|
|
||||||
|
if @user.persisted?
|
||||||
|
flash[:notice] = I18n.t 'devise.omniauth_callbacks.success', kind: provider
|
||||||
|
sign_in_and_redirect @user, event: :authentication
|
||||||
|
else
|
||||||
|
redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
37
app/models/concerns/omniauthable.rb
Normal file
37
app/models/concerns/omniauthable.rb
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Omniauthable
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
class_methods do
|
||||||
|
def from_omniauth(access_token)
|
||||||
|
data = access_token.info
|
||||||
|
provider = access_token.provider
|
||||||
|
uid = access_token.uid
|
||||||
|
|
||||||
|
# First, try to find user by provider and uid (for linked accounts)
|
||||||
|
user = find_by(provider: provider, uid: uid)
|
||||||
|
|
||||||
|
return user if user
|
||||||
|
|
||||||
|
# If not found, try to find by email
|
||||||
|
user = find_by(email: data['email'])
|
||||||
|
|
||||||
|
if user
|
||||||
|
# Update provider and uid for existing user (first-time linking)
|
||||||
|
user.update(provider: provider, uid: uid)
|
||||||
|
return user
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create new user if not found
|
||||||
|
user = create(
|
||||||
|
email: data['email'],
|
||||||
|
password: Devise.friendly_token[0, 20],
|
||||||
|
provider: provider,
|
||||||
|
uid: uid
|
||||||
|
)
|
||||||
|
|
||||||
|
user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -2,8 +2,11 @@
|
||||||
|
|
||||||
class User < ApplicationRecord # rubocop:disable Metrics/ClassLength
|
class User < ApplicationRecord # rubocop:disable Metrics/ClassLength
|
||||||
include UserFamily
|
include UserFamily
|
||||||
|
include Omniauthable
|
||||||
|
|
||||||
devise :database_authenticatable, :registerable,
|
devise :database_authenticatable, :registerable,
|
||||||
:recoverable, :rememberable, :validatable, :trackable
|
:recoverable, :rememberable, :validatable, :trackable,
|
||||||
|
:omniauthable, omniauth_providers: ::OMNIAUTH_PROVIDERS
|
||||||
|
|
||||||
has_many :points, dependent: :destroy
|
has_many :points, dependent: :destroy
|
||||||
has_many :imports, dependent: :destroy
|
has_many :imports, dependent: :destroy
|
||||||
|
|
|
||||||
|
|
@ -64,10 +64,10 @@
|
||||||
<div class="form-control mt-6">
|
<div class="form-control mt-6">
|
||||||
<%= f.submit "Update", class: 'btn btn-primary' %>
|
<%= f.submit "Update", class: 'btn btn-primary' %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= render "devise/shared/links" %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<%= render "devise/shared/links" %>
|
||||||
|
|
||||||
<p class='mt-3'>Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), data: { turbo_confirm: "Are you sure?", turbo_method: :delete }, method: :delete, class: 'btn' %></p>
|
<p class='mt-3'>Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), data: { turbo_confirm: "Are you sure?", turbo_method: :delete }, method: :delete, class: 'btn' %></p>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<p class='mt-3 flex flex-col gap-2'>
|
<p class='mt-3 flex flex-col gap-2'>
|
||||||
|
|
|
||||||
|
|
@ -74,10 +74,10 @@
|
||||||
<%= f.submit (@invitation ? "Create Account & Join Family" : "Sign up"),
|
<%= f.submit (@invitation ? "Create Account & Join Family" : "Sign up"),
|
||||||
class: 'btn btn-primary' %>
|
class: 'btn btn-primary' %>
|
||||||
</div>
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<% unless @invitation %>
|
<% unless @invitation %>
|
||||||
<%= render "devise/shared/links" %>
|
<%= render "devise/shared/links" %>
|
||||||
<% end %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,10 @@
|
||||||
<div class="form-control mt-6">
|
<div class="form-control mt-6">
|
||||||
<%= f.submit (@invitation ? "Sign in & Accept Invitation" : "Log in"), class: 'btn btn-primary' %>
|
<%= f.submit (@invitation ? "Sign in & Accept Invitation" : "Log in"), class: 'btn btn-primary' %>
|
||||||
</div>
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<% unless @invitation %>
|
<% unless @invitation %>
|
||||||
<%= render "devise/shared/links" %>
|
<%= render "devise/shared/links" %>
|
||||||
<% end %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<div class='my-5'>
|
<div class='mt-5'>
|
||||||
<% if !signed_in? %>
|
<% if !signed_in? %>
|
||||||
<div class='my-2'>
|
<div class='my-2'>
|
||||||
<%= link_to "Log in", new_session_path(resource_name) %>
|
<%= link_to "Log in", new_session_path(resource_name) %>
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,20 @@
|
||||||
</div> %>
|
</div> %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<% unless DawarichSettings.self_hosted? %>
|
||||||
|
<div>
|
||||||
|
<h2 class="text-2xl font-bold mb-4 flex items-center">
|
||||||
|
<%= icon 'link', class: "text-primary mr-1" %> Connected Accounts
|
||||||
|
</h2>
|
||||||
|
<div class="bg-base-100 p-5 rounded-lg shadow-sm space-y-4">
|
||||||
|
<p class="text-sm text-base-content/70">
|
||||||
|
You've connected your account using the following OAuth provider:
|
||||||
|
<strong><%= current_user.provider.capitalize %></strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions justify-end mt-6">
|
<div class="card-actions justify-end mt-6">
|
||||||
<%= f.submit "Save changes", class: "btn btn-primary" %>
|
<%= f.submit "Save changes", class: "btn btn-primary" %>
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,17 @@ MANAGER_URL = SELF_HOSTED ? nil : ENV.fetch('MANAGER_URL', nil)
|
||||||
METRICS_USERNAME = ENV.fetch('METRICS_USERNAME', 'prometheus')
|
METRICS_USERNAME = ENV.fetch('METRICS_USERNAME', 'prometheus')
|
||||||
METRICS_PASSWORD = ENV.fetch('METRICS_PASSWORD', 'prometheus')
|
METRICS_PASSWORD = ENV.fetch('METRICS_PASSWORD', 'prometheus')
|
||||||
# /Prometheus metrics
|
# /Prometheus metrics
|
||||||
|
|
||||||
|
# Configure OAuth providers based on environment
|
||||||
|
# Self-hosted: only OpenID Connect, Cloud: only GitHub and Google
|
||||||
|
OMNIAUTH_PROVIDERS =
|
||||||
|
if SELF_HOSTED
|
||||||
|
# Self-hosted: only OpenID Connect
|
||||||
|
ENV['OIDC_CLIENT_ID'].present? ? %i[openid_connect] : []
|
||||||
|
else
|
||||||
|
# Cloud: only GitHub and Google
|
||||||
|
providers = []
|
||||||
|
providers << :github if ENV['GITHUB_OAUTH_CLIENT_ID'].present?
|
||||||
|
providers << :google_oauth2 if ENV['GOOGLE_OAUTH_CLIENT_ID'].present?
|
||||||
|
providers
|
||||||
|
end
|
||||||
|
|
|
||||||
|
|
@ -265,7 +265,63 @@ Devise.setup do |config|
|
||||||
# ==> OmniAuth
|
# ==> OmniAuth
|
||||||
# Add a new OmniAuth provider. Check the wiki for more information on setting
|
# Add a new OmniAuth provider. Check the wiki for more information on setting
|
||||||
# up on your models and hooks.
|
# up on your models and hooks.
|
||||||
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
|
|
||||||
|
# Cloud version: only GitHub, Google (when env vars present)
|
||||||
|
if !SELF_HOSTED
|
||||||
|
if ENV['GITHUB_OAUTH_CLIENT_ID'].present? && ENV['GITHUB_OAUTH_CLIENT_SECRET'].present?
|
||||||
|
config.omniauth :github, ENV['GITHUB_OAUTH_CLIENT_ID'], ENV['GITHUB_OAUTH_CLIENT_SECRET'], scope: 'user:email'
|
||||||
|
Rails.logger.info 'OAuth: GitHub configured'
|
||||||
|
end
|
||||||
|
|
||||||
|
if ENV['GOOGLE_OAUTH_CLIENT_ID'].present? && ENV['GOOGLE_OAUTH_CLIENT_SECRET'].present?
|
||||||
|
config.omniauth :google_oauth2, ENV['GOOGLE_OAUTH_CLIENT_ID'], ENV['GOOGLE_OAUTH_CLIENT_SECRET'],
|
||||||
|
scope: 'userinfo.email,userinfo.profile'
|
||||||
|
Rails.logger.info 'OAuth: Google configured'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Self-hosted version: only OpenID Connect (when env vars present)
|
||||||
|
# Generic OpenID Connect provider (Authelia, Authentik, Keycloak, etc.)
|
||||||
|
# Supports both discovery mode (preferred) and manual endpoint configuration
|
||||||
|
if SELF_HOSTED && ENV['OIDC_CLIENT_ID'].present? && ENV['OIDC_CLIENT_SECRET'].present?
|
||||||
|
oidc_config = {
|
||||||
|
name: :openid_connect,
|
||||||
|
scope: %i[openid email profile],
|
||||||
|
response_type: :code,
|
||||||
|
client_options: {
|
||||||
|
identifier: ENV['OIDC_CLIENT_ID'],
|
||||||
|
secret: ENV['OIDC_CLIENT_SECRET'],
|
||||||
|
redirect_uri: ENV.fetch('OIDC_REDIRECT_URI', "#{ENV.fetch('APPLICATION_URL', 'http://localhost:3000')}/users/auth/openid_connect/callback")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use OIDC discovery if issuer is provided (recommended for Authelia, Authentik, Keycloak)
|
||||||
|
if ENV['OIDC_ISSUER'].present?
|
||||||
|
oidc_config[:issuer] = ENV['OIDC_ISSUER']
|
||||||
|
oidc_config[:discovery] = true
|
||||||
|
Rails.logger.info "OIDC: Discovery mode enabled with issuer: #{ENV['OIDC_ISSUER']}"
|
||||||
|
# Otherwise use manual endpoint configuration
|
||||||
|
elsif ENV['OIDC_HOST'].present?
|
||||||
|
oidc_config[:client_options].merge!(
|
||||||
|
{
|
||||||
|
host: ENV['OIDC_HOST'],
|
||||||
|
scheme: ENV.fetch('OIDC_SCHEME', 'https'),
|
||||||
|
port: ENV.fetch('OIDC_PORT', 443).to_i,
|
||||||
|
authorization_endpoint: ENV.fetch('OIDC_AUTHORIZATION_ENDPOINT', '/authorize'),
|
||||||
|
token_endpoint: ENV.fetch('OIDC_TOKEN_ENDPOINT', '/token'),
|
||||||
|
userinfo_endpoint: ENV.fetch('OIDC_USERINFO_ENDPOINT', '/userinfo')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Rails.logger.info "OIDC: Manual mode enabled with host: #{ENV['OIDC_SCHEME']}://#{ENV['OIDC_HOST']}:#{ENV.fetch(
|
||||||
|
'OIDC_PORT', 443
|
||||||
|
)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
Rails.logger.info "OIDC: Client ID: #{ENV['OIDC_CLIENT_ID']}, Redirect URI: #{oidc_config[:client_options][:redirect_uri]}"
|
||||||
|
config.omniauth :openid_connect, oidc_config
|
||||||
|
else
|
||||||
|
Rails.logger.warn 'OIDC: Not configured (missing OIDC_CLIENT_ID or OIDC_CLIENT_SECRET)'
|
||||||
|
end
|
||||||
|
|
||||||
# ==> Warden configuration
|
# ==> Warden configuration
|
||||||
# If you want to use other strategies, that are not supported by Devise, or
|
# If you want to use other strategies, that are not supported by Devise, or
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,8 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
devise_for :users, controllers: {
|
devise_for :users, controllers: {
|
||||||
registrations: 'users/registrations',
|
registrations: 'users/registrations',
|
||||||
sessions: 'users/sessions'
|
sessions: 'users/sessions',
|
||||||
|
omniauth_callbacks: 'users/omniauth_callbacks'
|
||||||
}
|
}
|
||||||
|
|
||||||
resources :metrics, only: [:index]
|
resources :metrics, only: [:index]
|
||||||
|
|
|
||||||
6
db/migrate/20251028130433_add_omniauth_to_users.rb
Normal file
6
db/migrate/20251028130433_add_omniauth_to_users.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
class AddOmniauthToUsers < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_column :users, :provider, :string
|
||||||
|
add_column :users, :uid, :string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -139,3 +139,49 @@ APP_MEMORY_LIMIT=4G
|
||||||
# SECRET_KEY_BASE=your-generated-secret-key
|
# SECRET_KEY_BASE=your-generated-secret-key
|
||||||
# SELF_HOSTED=true
|
# SELF_HOSTED=true
|
||||||
# PROMETHEUS_EXPORTER_ENABLED=true
|
# PROMETHEUS_EXPORTER_ENABLED=true
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Example of configuration for OpenID Connect (OIDC) authentication
|
||||||
|
#
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Generic OpenID Connect (for Authelia, Authentik, Keycloak, etc.)
|
||||||
|
# Option 1: Using OIDC Discovery (Recommended)
|
||||||
|
# Set OIDC_ISSUER to your provider's issuer URL (e.g., https://auth.example.com)
|
||||||
|
# The provider must support OpenID Connect Discovery (.well-known/openid-configuration)
|
||||||
|
OIDC_CLIENT_ID=client_id_example
|
||||||
|
OIDC_CLIENT_SECRET=client_secret_example
|
||||||
|
OIDC_ISSUER=https://authentik.yourdomain.com/application/o/dawarich/
|
||||||
|
OIDC_REDIRECT_URI=https://your-dawarich-url.com/users/auth/openid_connect/callback
|
||||||
|
|
||||||
|
# Option 2: Manual Endpoint Configuration (if discovery is not supported)
|
||||||
|
# Use this if your provider doesn't support OIDC discovery
|
||||||
|
# OIDC_CLIENT_ID=
|
||||||
|
# OIDC_CLIENT_SECRET=
|
||||||
|
# OIDC_HOST=auth.example.com
|
||||||
|
# OIDC_SCHEME=https
|
||||||
|
# OIDC_PORT=443
|
||||||
|
# OIDC_AUTHORIZATION_ENDPOINT=/authorize
|
||||||
|
# OIDC_TOKEN_ENDPOINT=/token
|
||||||
|
# OIDC_USERINFO_ENDPOINT=/userinfo
|
||||||
|
# OIDC_REDIRECT_URI=https://yourdomain.com/users/auth/openid_connect/callback
|
||||||
|
|
||||||
|
# Example configurations:
|
||||||
|
#
|
||||||
|
# Authelia:
|
||||||
|
# OIDC_ISSUER=https://auth.example.com
|
||||||
|
# OIDC_CLIENT_ID=your-client-id
|
||||||
|
# OIDC_CLIENT_SECRET=your-client-secret
|
||||||
|
# OIDC_REDIRECT_URI=https://dawarich.example.com/users/auth/openid_connect/callback
|
||||||
|
#
|
||||||
|
# Authentik:
|
||||||
|
# OIDC_ISSUER=https://authentik.example.com/application/o/dawarich/
|
||||||
|
# OIDC_CLIENT_ID=your-client-id
|
||||||
|
# OIDC_CLIENT_SECRET=your-client-secret
|
||||||
|
# OIDC_REDIRECT_URI=https://dawarich.example.com/users/auth/openid_connect/callback
|
||||||
|
#
|
||||||
|
# Keycloak:
|
||||||
|
# OIDC_ISSUER=https://keycloak.example.com/realms/your-realm
|
||||||
|
# OIDC_CLIENT_ID=dawarich
|
||||||
|
# OIDC_CLIENT_SECRET=your-client-secret
|
||||||
|
# OIDC_REDIRECT_URI=https://dawarich.example.com/users/auth/openid_connect/callback
|
||||||
|
|
|
||||||
|
|
@ -336,4 +336,97 @@ RSpec.describe User, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.from_omniauth' do
|
||||||
|
let(:auth_hash) do
|
||||||
|
OmniAuth::AuthHash.new({
|
||||||
|
provider: 'github',
|
||||||
|
uid: '123545',
|
||||||
|
info: {
|
||||||
|
email: email,
|
||||||
|
name: 'Test User'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user exists with the same email' do
|
||||||
|
let(:email) { 'existing@example.com' }
|
||||||
|
let!(:existing_user) { create(:user, email: email) }
|
||||||
|
|
||||||
|
it 'returns the existing user' do
|
||||||
|
user = described_class.from_omniauth(auth_hash)
|
||||||
|
expect(user).to eq(existing_user)
|
||||||
|
expect(user.persisted?).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create a new user' do
|
||||||
|
expect do
|
||||||
|
described_class.from_omniauth(auth_hash)
|
||||||
|
end.not_to change(User, :count)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user does not exist' do
|
||||||
|
let(:email) { 'new@example.com' }
|
||||||
|
|
||||||
|
it 'creates a new user with the OAuth email' do
|
||||||
|
expect do
|
||||||
|
described_class.from_omniauth(auth_hash)
|
||||||
|
end.to change(User, :count).by(1)
|
||||||
|
|
||||||
|
user = User.last
|
||||||
|
expect(user.email).to eq(email)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'generates a random password for the new user' do
|
||||||
|
user = described_class.from_omniauth(auth_hash)
|
||||||
|
expect(user.encrypted_password).to be_present
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a persisted user' do
|
||||||
|
user = described_class.from_omniauth(auth_hash)
|
||||||
|
expect(user.persisted?).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when OAuth provider is Google' do
|
||||||
|
let(:email) { 'google@example.com' }
|
||||||
|
let(:auth_hash) do
|
||||||
|
OmniAuth::AuthHash.new({
|
||||||
|
provider: 'google_oauth2',
|
||||||
|
uid: '123545',
|
||||||
|
info: {
|
||||||
|
email: email,
|
||||||
|
name: 'Google User'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a user from Google OAuth data' do
|
||||||
|
user = described_class.from_omniauth(auth_hash)
|
||||||
|
expect(user.email).to eq(email)
|
||||||
|
expect(user.persisted?).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when email is nil' do
|
||||||
|
let(:email) { nil }
|
||||||
|
|
||||||
|
it 'attempts to create a user but fails validation' do
|
||||||
|
user = described_class.from_omniauth(auth_hash)
|
||||||
|
expect(user.persisted?).to be false
|
||||||
|
expect(user.errors[:email]).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when email is blank' do
|
||||||
|
let(:email) { '' }
|
||||||
|
|
||||||
|
it 'attempts to create a user but fails validation' do
|
||||||
|
user = described_class.from_omniauth(auth_hash)
|
||||||
|
expect(user.persisted?).to be false
|
||||||
|
expect(user.errors[:email]).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
96
spec/requests/users/omniauth_callbacks_spec.rb
Normal file
96
spec/requests/users/omniauth_callbacks_spec.rb
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'Users::OmniauthCallbacks', type: :request do
|
||||||
|
let(:email) { 'oauth_user@example.com' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Rails.application.env_config['devise.mapping'] = Devise.mappings[:user]
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'successful OAuth authentication' do |provider, provider_name|
|
||||||
|
context "when user doesn't exist" do
|
||||||
|
it 'creates a new user and signs them in' do
|
||||||
|
expect do
|
||||||
|
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider]
|
||||||
|
get "/users/auth/#{provider}/callback"
|
||||||
|
end.to change(User, :count).by(1)
|
||||||
|
|
||||||
|
expect(response).to redirect_to(root_path)
|
||||||
|
|
||||||
|
user = User.find_by(email: email)
|
||||||
|
expect(user).to be_present
|
||||||
|
expect(user.encrypted_password).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user already exists' do
|
||||||
|
let!(:existing_user) { create(:user, email: email) }
|
||||||
|
|
||||||
|
it 'signs in the existing user without creating a new one' do
|
||||||
|
expect do
|
||||||
|
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider]
|
||||||
|
get "/users/auth/#{provider}/callback"
|
||||||
|
end.not_to change(User, :count)
|
||||||
|
|
||||||
|
expect(response).to redirect_to(root_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user creation fails' do
|
||||||
|
before do
|
||||||
|
allow(User).to receive(:create).and_return(
|
||||||
|
User.new(email: email).tap do |u|
|
||||||
|
u.errors.add(:email, 'is invalid')
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'redirects to registration with error message' do
|
||||||
|
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider]
|
||||||
|
get "/users/auth/#{provider}/callback"
|
||||||
|
|
||||||
|
expect(response).to redirect_to(new_user_registration_url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Self-hosted configuration (SELF_HOSTED=true) uses OpenID Connect
|
||||||
|
describe 'GET /users/auth/openid_connect/callback' do
|
||||||
|
before do
|
||||||
|
mock_openid_connect_auth(email: email)
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples 'successful OAuth authentication', :openid_connect, 'OpenID Connect'
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'OAuth flow integration with OpenID Connect' do
|
||||||
|
context 'with OpenID Connect (Authelia/Authentik/Keycloak)' do
|
||||||
|
before { mock_openid_connect_auth(email: 'oidc@example.com') }
|
||||||
|
|
||||||
|
it 'completes the full OAuth flow' do
|
||||||
|
expect do
|
||||||
|
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect]
|
||||||
|
get '/users/auth/openid_connect/callback'
|
||||||
|
end.to change(User, :count).by(1)
|
||||||
|
|
||||||
|
user = User.find_by(email: 'oidc@example.com')
|
||||||
|
expect(user).to be_present
|
||||||
|
expect(user.email).to eq('oidc@example.com')
|
||||||
|
expect(response).to redirect_to(root_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'CSRF protection' do
|
||||||
|
it 'does not raise CSRF error for OpenID Connect callback' do
|
||||||
|
mock_openid_connect_auth(email: email)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect]
|
||||||
|
get '/users/auth/openid_connect/callback'
|
||||||
|
end.not_to raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
105
spec/support/omniauth.rb
Normal file
105
spec/support/omniauth.rb
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
OmniAuth.config.test_mode = true
|
||||||
|
|
||||||
|
module OmniauthHelpers
|
||||||
|
def mock_github_auth(email: 'test@github.com')
|
||||||
|
OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new({
|
||||||
|
provider: 'github',
|
||||||
|
uid: '123545',
|
||||||
|
info: {
|
||||||
|
email: email,
|
||||||
|
name: 'Test User',
|
||||||
|
image: 'https://avatars.githubusercontent.com/u/123545'
|
||||||
|
},
|
||||||
|
credentials: {
|
||||||
|
token: 'mock_token',
|
||||||
|
expires_at: Time.now + 1.week
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
raw_info: {
|
||||||
|
login: 'testuser',
|
||||||
|
avatar_url: 'https://avatars.githubusercontent.com/u/123545',
|
||||||
|
name: 'Test User',
|
||||||
|
email: email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def mock_google_auth(email: 'test@gmail.com')
|
||||||
|
OmniAuth.config.mock_auth[:google_oauth2] = OmniAuth::AuthHash.new({
|
||||||
|
provider: 'google_oauth2',
|
||||||
|
uid: '123545',
|
||||||
|
info: {
|
||||||
|
email: email,
|
||||||
|
name: 'Test User',
|
||||||
|
image: 'https://lh3.googleusercontent.com/a/test'
|
||||||
|
},
|
||||||
|
credentials: {
|
||||||
|
token: 'mock_token',
|
||||||
|
refresh_token: 'mock_refresh_token',
|
||||||
|
expires_at: Time.now + 1.hour
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
raw_info: {
|
||||||
|
email: email,
|
||||||
|
email_verified: true,
|
||||||
|
name: 'Test User',
|
||||||
|
given_name: 'Test',
|
||||||
|
family_name: 'User',
|
||||||
|
picture: 'https://lh3.googleusercontent.com/a/test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def mock_openid_connect_auth(email: 'test@oidc.com', provider_name: 'Authelia')
|
||||||
|
OmniAuth.config.mock_auth[:openid_connect] = OmniAuth::AuthHash.new({
|
||||||
|
provider: 'openid_connect',
|
||||||
|
uid: '123545',
|
||||||
|
info: {
|
||||||
|
email: email,
|
||||||
|
name: 'Test User',
|
||||||
|
image: 'https://example.com/avatar.jpg'
|
||||||
|
},
|
||||||
|
credentials: {
|
||||||
|
token: 'mock_token',
|
||||||
|
refresh_token: 'mock_refresh_token',
|
||||||
|
expires_at: Time.now + 1.hour,
|
||||||
|
id_token: 'mock_id_token'
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
raw_info: {
|
||||||
|
sub: '123545',
|
||||||
|
email: email,
|
||||||
|
email_verified: true,
|
||||||
|
name: 'Test User',
|
||||||
|
preferred_username: 'testuser',
|
||||||
|
given_name: 'Test',
|
||||||
|
family_name: 'User',
|
||||||
|
picture: 'https://example.com/avatar.jpg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def mock_oauth_failure(provider)
|
||||||
|
OmniAuth.config.mock_auth[provider] = :invalid_credentials
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.include OmniauthHelpers, type: :request
|
||||||
|
config.include OmniauthHelpers, type: :system
|
||||||
|
|
||||||
|
config.before do
|
||||||
|
OmniAuth.config.test_mode = true
|
||||||
|
end
|
||||||
|
|
||||||
|
config.after do
|
||||||
|
OmniAuth.config.mock_auth[:github] = nil
|
||||||
|
OmniAuth.config.mock_auth[:google_oauth2] = nil
|
||||||
|
OmniAuth.config.mock_auth[:openid_connect] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in a new issue