From 60b1bf832b28424c963a585223c50b1af2ccab3f Mon Sep 17 00:00:00 2001 From: Albert Stefanov Date: Tue, 5 May 2026 09:21:32 +0300 Subject: [PATCH 1/2] oidc: add gems --- Gemfile | 3 +- Gemfile.lock | 9 +- .../initializers/doorkeeper_openid_connect.rb | 118 ++++++++++++++++++ .../locales/doorkeeper_openid_connect.en.yml | 23 ++++ config/routes.rb | 1 + ...create_doorkeeper_openid_connect_tables.rb | 15 +++ db/schema.rb | 9 +- 7 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 config/initializers/doorkeeper_openid_connect.rb create mode 100644 config/locales/doorkeeper_openid_connect.en.yml create mode 100644 db/migrate/20260429165133_create_doorkeeper_openid_connect_tables.rb diff --git a/Gemfile b/Gemfile index 6572b4a..ef42374 100644 --- a/Gemfile +++ b/Gemfile @@ -77,8 +77,9 @@ gem 'snmp' gem 'yaml_db' # OAuth -gem 'doorkeeper', '> 1.0beta' +gem 'doorkeeper' gem 'doorkeeper-i18n' +gem 'doorkeeper-openid_connect' gem 'matrix' gem 'net-smtp', require: false diff --git a/Gemfile.lock b/Gemfile.lock index be6b509..4006264 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -151,6 +151,10 @@ GEM railties (>= 5) doorkeeper-i18n (5.2.8) doorkeeper (>= 5.2) + doorkeeper-openid_connect (1.9.0) + doorkeeper (>= 5.5, < 6.0) + jwt (>= 2.5) + ostruct (>= 0.5) drb (2.2.3) erb (6.0.4) erubi (1.13.1) @@ -187,6 +191,8 @@ GEM railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (2.19.4) + jwt (3.1.2) + base64 kaminari (1.2.2) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.2) @@ -455,8 +461,9 @@ DEPENDENCIES delayed_job_active_record devise devise-i18n - doorkeeper (> 1.0beta) + doorkeeper doorkeeper-i18n + doorkeeper-openid_connect factory_bot_rails fakefs faker diff --git a/config/initializers/doorkeeper_openid_connect.rb b/config/initializers/doorkeeper_openid_connect.rb new file mode 100644 index 0000000..a290613 --- /dev/null +++ b/config/initializers/doorkeeper_openid_connect.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +Doorkeeper::OpenidConnect.configure do + issuer do |resource_owner, application| + Rails.application.credentials.root_url + end + + signing_key Rails.application.credentials.oidc_signing_key + # signing_key <<~KEY + # -----BEGIN RSA PRIVATE KEY----- + # .... + # -----END RSA PRIVATE KEY----- + # KEY + + subject_types_supported [:public] + + resource_owner_from_access_token do |access_token| + # Example implementation: + User.find_by(id: access_token.resource_owner_id) + end + + auth_time_from_resource_owner do |resource_owner| + # Example implementation: + resource_owner.current_sign_in_at + end + + reauthenticate_resource_owner do |resource_owner, return_to| + # Example implementation: + store_location_for resource_owner, return_to + sign_out resource_owner + redirect_to new_user_session_url + end + + select_account_for_resource_owner do |resource_owner, return_to| + # Example implementation: + store_location_for resource_owner, return_to + redirect_to account_select_url + end + + subject do |resource_owner, application| + # Example implementation: + resource_owner.id + + # or if you need pairwise subject identifier, implement like below: + # Digest::SHA256.hexdigest("#{resource_owner.id}#{URI.parse(application.redirect_uri).host}#{'your_secret_salt'}") + end + + # Protocol to use when generating URIs for the discovery endpoint, + # for example if you also use HTTPS in development + # protocol do + # :https + # end + + # Expiration time on or after which the ID Token MUST NOT be accepted for processing. (default 120 seconds). + # expiration 600 + + # Enable dynamic client registration (default false) + # dynamic_client_registration true + + # You can use your own model class if you need to extend (or even override) the default + # Doorkeeper::OpenidConnect::Request model (e.g. to use a different database connection). + # + # By default Doorkeeper OpenID Connect uses: + # + # open_id_request_class "Doorkeeper::OpenidConnect::Request" + # + # Don't forget to include the OpenID Connect ORM mixin into your custom model: + # + # * ::Doorkeeper::OpenidConnect::Orm::ActiveRecord::Mixins::OpenidRequest + # + # For example: + # + # open_id_request_class "MyOpenidRequest" + # + # class MyOpenidRequest < ApplicationRecord + # include ::Doorkeeper::OpenidConnect::Orm::ActiveRecord::Mixins::OpenidRequest + # end + + # Example claims: + # claims do + # normal_claim :_foo_ do |resource_owner| + # resource_owner.foo + # end + + # normal_claim :_bar_ do |resource_owner| + # resource_owner.bar + # end + # end + claims do + claim :email do |resource_owner| + resource_owner.email + end + + claim :name do |resource_owner| + "#{resource_owner.first_name} #{resource_owner.last_name}" + end + + claim :family_name do |resource_owner| + resource_owner.last_name + end + + claim :given_name do |resource_owner| + resource_owner.first_name + end + + claim :picture do |resource_owner| + Gravatar.new(resource_owner.email).image_url ssl: true, s: 128, d: 'retro' + end + + claim :username do |resource_owner| + resource_owner.username + end + + claim :roles do |resource_owner| + resource_owner.roles.map(&:name) + end + end +end \ No newline at end of file diff --git a/config/locales/doorkeeper_openid_connect.en.yml b/config/locales/doorkeeper_openid_connect.en.yml new file mode 100644 index 0000000..1bed506 --- /dev/null +++ b/config/locales/doorkeeper_openid_connect.en.yml @@ -0,0 +1,23 @@ +en: + doorkeeper: + scopes: + openid: 'Authenticate your account' + profile: 'View your profile information' + email: 'View your email address' + address: 'View your physical address' + phone: 'View your phone number' + errors: + messages: + login_required: 'The authorization server requires end-user authentication' + consent_required: 'The authorization server requires end-user consent' + interaction_required: 'The authorization server requires end-user interaction' + account_selection_required: 'The authorization server requires end-user account selection' + openid_connect: + errors: + messages: + # Configuration error messages + resource_owner_from_access_token_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.resource_owner_from_access_token missing configuration.' + auth_time_from_resource_owner_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.auth_time_from_resource_owner missing configuration.' + reauthenticate_resource_owner_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.reauthenticate_resource_owner missing configuration.' + select_account_for_resource_owner_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.select_account_for_resource_owner missing configuration.' + subject_not_configured: 'ID Token generation failed due to Doorkeeper::OpenidConnect.configure.subject missing configuration.' diff --git a/config/routes.rb b/config/routes.rb index cbc8ae5..b907798 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true Rails.application.routes.draw do + use_doorkeeper_openid_connect use_doorkeeper do controllers applications: 'oauth/applications' end diff --git a/db/migrate/20260429165133_create_doorkeeper_openid_connect_tables.rb b/db/migrate/20260429165133_create_doorkeeper_openid_connect_tables.rb new file mode 100644 index 0000000..b0f8240 --- /dev/null +++ b/db/migrate/20260429165133_create_doorkeeper_openid_connect_tables.rb @@ -0,0 +1,15 @@ +class CreateDoorkeeperOpenidConnectTables < ActiveRecord::Migration[8.1] + def change + create_table :oauth_openid_requests do |t| + t.references :access_grant, null: false, index: true + t.string :nonce, null: false + end + + add_foreign_key( + :oauth_openid_requests, + :oauth_access_grants, + column: :access_grant_id, + on_delete: :cascade + ) + end +end diff --git a/db/schema.rb b/db/schema.rb index 50cf2c8..510ed47 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_01_11_102636) do +ActiveRecord::Schema[8.1].define(version: 2026_04_29_165133) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -119,6 +119,12 @@ t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true end + create_table "oauth_openid_requests", force: :cascade do |t| + t.bigint "access_grant_id", null: false + t.string "nonce", null: false + t.index ["access_grant_id"], name: "index_oauth_openid_requests_on_access_grant_id" + end + create_table "phone_numbers", force: :cascade do |t| t.datetime "created_at", precision: nil, null: false t.integer "owner_id", null: false @@ -186,4 +192,5 @@ end add_foreign_key "audit_log_entries", "users" + add_foreign_key "oauth_openid_requests", "oauth_access_grants", column: "access_grant_id", on_delete: :cascade end From c87db130efc94dbdeacd963739bc96156617c236 Mon Sep 17 00:00:00 2001 From: Albert Stefanov Date: Tue, 5 May 2026 09:52:18 +0300 Subject: [PATCH 2/2] oidc: add required scopes --- config/initializers/doorkeeper.rb | 2 +- config/initializers/doorkeeper_openid_connect.rb | 2 +- config/locales/doorkeeper_openid_connect.bg.yml | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 config/locales/doorkeeper_openid_connect.bg.yml diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 783c062..5aeb136 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -153,7 +153,7 @@ # https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes # default_scopes :public - optional_scopes :account_data_read + optional_scopes :account_data_read, :openid, :profile, :email, :roles # Define scopes_by_grant_type to restrict only certain scopes for grant_type # By default, all the scopes will be available for all the grant types. diff --git a/config/initializers/doorkeeper_openid_connect.rb b/config/initializers/doorkeeper_openid_connect.rb index a290613..9da3f31 100644 --- a/config/initializers/doorkeeper_openid_connect.rb +++ b/config/initializers/doorkeeper_openid_connect.rb @@ -111,7 +111,7 @@ resource_owner.username end - claim :roles do |resource_owner| + claim :roles, scope: :roles do |resource_owner| resource_owner.roles.map(&:name) end end diff --git a/config/locales/doorkeeper_openid_connect.bg.yml b/config/locales/doorkeeper_openid_connect.bg.yml new file mode 100644 index 0000000..9d1054e --- /dev/null +++ b/config/locales/doorkeeper_openid_connect.bg.yml @@ -0,0 +1,8 @@ +bg: + doorkeeper: + scopes: + openid: Вход чрез init Lab Fauna + profile: Профил в init Lab Fauna + email: Имейл адрес за init Lab Fauna + address: Адрес + phone: Телефонен номер за init Lab Fauna \ No newline at end of file