model_driven_api 3.4.0 → 3.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ecd0e3a3b7a279fb9a68b6ac3fe513ada2aa9875807cc672fa5a8f4d690fb1d
4
- data.tar.gz: 926640daa632265835dc91e24d4062ed3678c91972aa5d6de53c2c8939511c4d
3
+ metadata.gz: cc68530c0b5f4519866a975cea9ea23207cec6dc83aad73cd6a61da041760fee
4
+ data.tar.gz: 5789f8c2a515b3385a6c53da9392fbc21fdb599aa247a9968b5a8a2f6b03f326
5
5
  SHA512:
6
- metadata.gz: aa235f9c25f6abcb48d724f68f9a726bf38c1dd90bd7aab6c292fdf78ecab4df142f806337b6782595ba19484481d230020880e2376ed44f62d207f736026acd
7
- data.tar.gz: 984146f876ba9b9267855f9099a316641efbd65131191e680e933ccbeb013d3bac51ec287c41e9a886a815f312866f579fcf0a8d3cb795e7b1b6538960b5e058
6
+ metadata.gz: b58dc845ec1c3d228228e4f0527d8dcb0c82ebe9bf92cd1df86615cd9137a4c93539b076d1c8bb673feb8b6bc68daa7e4b332357995aabc11bb4c297b17dd372
7
+ data.tar.gz: cbbabc3bb858b9d83d10bc01617f147c1cb9402a6ce65db269b9a41639ea8fedb16ccaed7f5c86dbde3c7a7c98c19f50d552804f6538d0dd33aa3b96a77cf38b
data/README.md CHANGED
@@ -1 +1,66 @@
1
1
  This is part of Thecore framework: https://github.com/gabrieletassoni/thecore/tree/release/3
2
+
3
+ ## Authorization
4
+
5
+ It's enabled to LDAP (Active Directory and othe LDAP Based local service) and to oauth2 (Google Workspace and Microsoft Entra ID) services.
6
+
7
+ ### 📘 Register OAuth Apps
8
+
9
+ To use OAuth2 for authentication, you need to register your application with the OAuth provider (Google or Microsoft). Follow these steps, which are specific to each provider and aimed at a demo frontend application running on `http://localhost:5173`.
10
+
11
+ #### ✅ Google OAuth Client Setup
12
+
13
+ Go to: https://console.cloud.google.com/apis/credentials
14
+ Create OAuth 2.0 Client ID
15
+ Choose Web Application
16
+ Add to “Authorized JavaScript Origins”:
17
+ http://localhost:5173
18
+ Add to “Authorized redirect URIs” (used by Google, not backend):
19
+ http://localhost:5173 (for frontend access only)
20
+ Save your Client ID
21
+
22
+
23
+ #### ✅ Microsoft Entra ID OAuth Setup
24
+
25
+ Go to https://portal.azure.com
26
+ Microsoft Entra ID → App registrations → New registration
27
+ Set redirect URI to http://localhost:5173 (type: SPA)
28
+ Note the:
29
+ Application (client) ID
30
+ Directory (tenant) ID
31
+ Under Authentication tab:
32
+ Add platform: “Single-page application”
33
+ Add http://localhost:5173 to Redirect URIs
34
+
35
+ ### Save your Client ID and Tenant ID
36
+ You will need these IDs to configure the backend.
37
+
38
+ ### 📘 Configure OAuth in Thecore
39
+ To configure OAuth in Thecore backend, you need to set the following environment variables in your `.env` file or `environmet:` configuration in docker-compose.yml:
40
+
41
+ ```plaintext
42
+ # OAuth Configuration
43
+ # Microsoft OAuth
44
+ ENTRA_CLIENT_ID: "your-client-id"
45
+ ENTRA_CLIENT_SECRET: "your-client-secret"
46
+ ENTRA_TENANT_ID: "your-tenant-id"
47
+
48
+ # Google OAuth
49
+ GOOGLE_CLIENT_ID: "your-client-id.apps.googleusercontent.com"
50
+ GOOGLE_CLIENT_SECRET: "your-client-secret"
51
+ ```
52
+
53
+ In the Frontend applciation, you will need to set the following environment variables in your `.env` file:
54
+
55
+ ```plaintext
56
+ # OAuth Configuration
57
+ # Google OAuth
58
+ VITE_GOOGLE_CLIENT_ID=your-client-id
59
+
60
+ # Microsoft OAuth
61
+ VITE_AZURE_CLIENT_ID=your-client-id
62
+ VITE_AZURE_TENANT_ID=your-tenant-id
63
+
64
+ # OAuth Callback URLs
65
+ VITE_API_URL=http://yourdomain/api/v2/auth/google_oauth2/callback
66
+ ```
@@ -0,0 +1,71 @@
1
+ module Api::V2::Auth
2
+ class OauthController < ActionController::API
3
+ def callback
4
+ email = params['email']
5
+
6
+ user = User.find_or_create_by(email: email) do |u|
7
+ u.name = params['given_name']
8
+ u.surname = params['family_name']
9
+ u.password = u.password_confirmation = ThecoreAuthCommons.generate_secure_password
10
+ u.auth_source = params['provider'] # 'google' or 'microsoft'
11
+ u.admin = true
12
+ end
13
+ unless user
14
+ render json: { error: "User not registered" }, status: :unauthorized
15
+ return
16
+ end
17
+
18
+ token = JsonWebToken.encode(user_id: user.id)
19
+
20
+ if ENV["ALLOW_MULTISESSIONS"] == "false"
21
+ UsedToken.where(user_id: user.id).update_all(is_valid: false)
22
+ UsedToken.create!(token: token, user_id: user.id)
23
+ end
24
+
25
+ # redirect_url = "#{ENV['FRONTEND_URL']}?token=#{token}"
26
+ # redirect_to redirect_url
27
+ response.set_header("Token", JsonWebToken.encode(user_id: user.id))
28
+ render json: user, status: :ok
29
+ end
30
+
31
+ def failure
32
+ render json: { error: "OAuth authentication failed" }, status: :unauthorized
33
+ end
34
+
35
+ def exchange_token
36
+ provider_token = params[:provider_token]
37
+ provider = params[:provider] # 'google' or 'microsoft'
38
+
39
+ user_info = case provider
40
+ when 'google'
41
+ uri = URI("https://www.googleapis.com/oauth2/v3/userinfo")
42
+ res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
43
+ req = Net::HTTP::Get.new(uri)
44
+ req["Authorization"] = "Bearer #{provider_token}"
45
+ http.request(req)
46
+ end
47
+ JSON.parse(res.body)
48
+ when 'microsoft'
49
+ uri = URI("https://graph.microsoft.com/v1.0/me")
50
+ res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
51
+ req = Net::HTTP::Get.new(uri)
52
+ req["Authorization"] = "Bearer #{provider_token}"
53
+ http.request(req)
54
+ end
55
+ JSON.parse(res.body)
56
+ else
57
+ return render json: { error: "Unknown provider" }, status: :unprocessable_entity
58
+ end
59
+
60
+ email = user_info["mail"] || user_info["email"] || user_info["userPrincipalName"]
61
+ user = User.find_by(email: email)
62
+
63
+ if user.nil?
64
+ return render json: { error: "User not registered" }, status: :unauthorized
65
+ end
66
+
67
+ response.set_header("Token", JsonWebToken.encode(user_id: user.id))
68
+ render json: user, status: :ok
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,20 @@
1
+ Rails.application.config.middleware.use OmniAuth::Builder do
2
+ provider(
3
+ :entra_id,
4
+ {
5
+ client_id: ENV['ENTRA_CLIENT_ID'],
6
+ client_secret: ENV['ENTRA_CLIENT_SECRET'],
7
+ tenant_id: ENV['ENTRA_TENANT_ID'], # Needed for Microsoft
8
+ scope: 'User.Read',
9
+ response_type: 'code'
10
+ }
11
+ )
12
+ provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], {
13
+ scope: 'email,profile',
14
+ prompt: 'select_account',
15
+ access_type: 'online'
16
+ }
17
+ end
18
+
19
+ OmniAuth.config.allowed_request_methods = [:get, :post]
20
+ OmniAuth.config.silence_get_warning = true
data/config/routes.rb CHANGED
@@ -1,11 +1,26 @@
1
1
  # require 'ransack'
2
2
 
3
3
  Rails.application.routes.draw do
4
- # REST API (Stateless)
5
-
4
+ oauth_test = (ENV['ENTRA_CLIENT_ID'].present? && ENV['ENTRA_CLIENT_SECRET'].present? && ENV['ENTRA_TENANT_ID'].present?) || (ENV['GOOGLE_CLIENT_ID'].present? && ENV['GOOGLE_CLIENT_SECRET'].present?)
6
5
  scope ENV.fetch("RAILS_RELATIVE_URL_ROOT", "/") do
6
+ if oauth_test
7
+ # OmniAuth callbacks need these top-level paths:
8
+ match '/auth/:provider/callback', to: redirect('/api/v2/auth/%{provider}/callback'), via: [:get, :post]
9
+ match '/auth/failure', to: redirect('/api/v2/auth/failure'), via: [:get, :post]
10
+ end
7
11
  namespace :api, constraints: { format: :json } do
8
12
  namespace :v2 do
13
+ # Authentication via Oauth2 only if the environment variable is set
14
+ if oauth_test
15
+ namespace :auth do
16
+ # Omniauth routes for OAuth2 authentication
17
+ match ':provider/callback', to: 'oauth#callback', via: [:get, :post]
18
+ get :failure, to: 'oauth#failure'
19
+ get ':provider', to: redirect('/auth/%{provider}') # triggers OmniAuth middleware
20
+ post :jwt, to: 'oauth#exchange_token' # Optional endpoint to allow frontends to send the OAuth token
21
+ end
22
+ end
23
+
9
24
  resources :users
10
25
 
11
26
  namespace :info do
@@ -1,3 +1,3 @@
1
1
  module ModelDrivenApi
2
- VERSION = "3.4.0".freeze
2
+ VERSION = "3.4.2".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: model_driven_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.0
4
+ version: 3.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriele Tassoni
@@ -121,6 +121,7 @@ files:
121
121
  - app/commands/authenticate_user.rb
122
122
  - app/commands/authorize_api_request.rb
123
123
  - app/controllers/api/v2/application_controller.rb
124
+ - app/controllers/api/v2/auth/oauth_controller.rb
124
125
  - app/controllers/api/v2/authentication_controller.rb
125
126
  - app/controllers/api/v2/info_controller.rb
126
127
  - app/controllers/api/v2/raw_controller.rb
@@ -131,6 +132,7 @@ files:
131
132
  - config/initializers/after_initialize_for_model_driven_api.rb
132
133
  - config/initializers/cors_api_thecore.rb
133
134
  - config/initializers/knock.rb
135
+ - config/initializers/omniauth.rb
134
136
  - config/initializers/time_with_zone.rb
135
137
  - config/initializers/wrap_parameters.rb
136
138
  - config/routes.rb