aha_builder_core 1.0.1 → 1.0.4
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 +4 -4
- data/README.md +17 -27
- data/lib/aha/auth/client.rb +15 -18
- data/lib/aha/auth/configuration.rb +1 -1
- data/lib/aha/auth/sessions_resource.rb +2 -0
- data/lib/aha/auth/user.rb +2 -0
- data/lib/aha/auth/version.rb +1 -1
- data/lib/aha/auth.rb +46 -9
- metadata +16 -8
- data/lib/generators/aha_builder_core/authentication/USAGE +0 -21
- data/lib/generators/aha_builder_core/authentication/authentication_generator.rb +0 -57
- data/lib/generators/aha_builder_core/authentication/templates/authentication.rb.erb +0 -48
- data/lib/generators/aha_builder_core/authentication/templates/current.rb.erb +0 -4
- data/lib/generators/aha_builder_core/authentication/templates/sessions_controller.rb.erb +0 -44
- data/lib/generators/aha_builder_core/authentication/templates/user.rb.erb +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 67c88ef564275000006548bd40149fb9d8c9c6a28d7b9faf430a8dd6b338147c
|
|
4
|
+
data.tar.gz: 10401cac65cfa9f711640911038c12491acaf73b160f340851becdce92edfbb2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f8c526217a495e3e9ec239c4d169eb0848f9bcc6f876ba92f02f114a420f0220313e7960ef1a56b1271c0d270f2906480a5524510e988ecda939eca8491e3f10
|
|
7
|
+
data.tar.gz: 1eb5ccd35517e18df03ea87a2c93b4eadea3152d79023f440ee19c9930f600543085c6c6ab2e45fccbfb3ee0df1a1bd14b0223ca11315e8826e2d655f5df5880
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Aha Builder Core Client
|
|
2
2
|
|
|
3
|
-
Ruby client for Aha! Builder core authentication services.
|
|
3
|
+
Ruby client for Aha! Builder core authentication services which provides login and signup using email/password, social logins (Google, Github, Microsoft), SAML SSO and password reset.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -12,25 +12,14 @@ gem "aha_builder_core", path: "engines/builder_core/client"
|
|
|
12
12
|
|
|
13
13
|
## Configuration
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
```ruby
|
|
18
|
-
require "aha_builder_core"
|
|
19
|
-
|
|
20
|
-
Aha::Auth.configure do |config|
|
|
21
|
-
config.server_url = "https://secure.aha.io" # Auth server URL
|
|
22
|
-
config.client_id = "your-client-id" # ID of the builder application
|
|
23
|
-
config.api_key = "your-api-key" # For server-to-server auth
|
|
24
|
-
config.jwks_cache_ttl = 3600 # JWKS cache duration (default: 1 hour)
|
|
25
|
-
config.refresh_threshold = 120 # Seconds before expiry to refresh (default: 2 min)
|
|
26
|
-
config.timeout = 30 # HTTP timeout in seconds
|
|
27
|
-
end
|
|
28
|
-
```
|
|
15
|
+
No configuration is necessary and no environment variables are necessary.
|
|
29
16
|
|
|
30
17
|
## Authentication Flow
|
|
31
18
|
|
|
32
19
|
The authentication UI is provided completely by the core system. During authentication the user is redirected to the login page, and will return (via HTTP redirect) to the `/callback` URL when authentication is complete. Your application must implement a callback action at `/callback` to receive the code. Any value passed in as `state` is returned verbatim to the callback.
|
|
33
20
|
|
|
21
|
+
Protection against CSRF atacks is handled internally by the Aha::Auth library and there is no need to use a nonce in the state parameter.
|
|
22
|
+
|
|
34
23
|
### Generate Login URL
|
|
35
24
|
|
|
36
25
|
Redirect users to the authentication and signup UI:
|
|
@@ -38,11 +27,12 @@ Redirect users to the authentication and signup UI:
|
|
|
38
27
|
```ruby
|
|
39
28
|
url = Aha::Auth.login_url(
|
|
40
29
|
state: { return_to: "/" }.to_json,
|
|
41
|
-
redirect_uri: "#{ENV['APPLICATION_URL']}/callback"
|
|
42
30
|
)
|
|
43
31
|
redirect_to url
|
|
44
32
|
```
|
|
45
33
|
|
|
34
|
+
Redirecting to the login_url must be done with a full page load. It will not work using XHR.
|
|
35
|
+
|
|
46
36
|
### Exchange Authorization Code
|
|
47
37
|
|
|
48
38
|
In the `/callback` action callback, you must exchange the code for a session token and refresh token:
|
|
@@ -54,11 +44,11 @@ result = Aha::Auth.authenticate_with_code(code: params[:code])
|
|
|
54
44
|
# refresh_token: "...",
|
|
55
45
|
# expires_at: Time,
|
|
56
46
|
# user: {
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
47
|
+
# id: "user-uuid",
|
|
48
|
+
# first_name: "Jane",
|
|
49
|
+
# last_name: "Doe",
|
|
50
|
+
# email: "jane.doe@example.com",
|
|
51
|
+
# email_verified: true
|
|
62
52
|
# }
|
|
63
53
|
# }
|
|
64
54
|
```
|
|
@@ -68,8 +58,8 @@ result = Aha::Auth.authenticate_with_code(code: params[:code])
|
|
|
68
58
|
The `user` object returned contains the authenticated user's profile information from the Builder Core system:
|
|
69
59
|
|
|
70
60
|
- `id`: The unique identifier for the user in Builder Core
|
|
71
|
-
- `first_name`: User's first name
|
|
72
|
-
- `last_name`: User's last name
|
|
61
|
+
- `first_name`: User's first name. first_name is optional and may not be present.
|
|
62
|
+
- `last_name`: User's last name. last_name is optional and may not be present.
|
|
73
63
|
- `email`: User's email address
|
|
74
64
|
- `email_verified`: Boolean indicating if the email has been verified
|
|
75
65
|
|
|
@@ -86,10 +76,10 @@ local_user = User.find_or_initialize_by(auth_identifier: result[:user]["id"])
|
|
|
86
76
|
|
|
87
77
|
# Update local user attributes
|
|
88
78
|
local_user.update!(
|
|
89
|
-
email: result[:user][
|
|
90
|
-
first_name: result[:user][
|
|
91
|
-
last_name: result[:user][
|
|
92
|
-
email_verified: result[:user][
|
|
79
|
+
email: result[:user][:email],
|
|
80
|
+
first_name: result[:user][:first_name],
|
|
81
|
+
last_name: result[:user][:last_name],
|
|
82
|
+
email_verified: result[:user][:email_verified]
|
|
93
83
|
)
|
|
94
84
|
|
|
95
85
|
# Store tokens in session or database
|
data/lib/aha/auth/client.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support/core_ext/hash/indifferent_access"
|
|
4
|
+
|
|
3
5
|
module Aha
|
|
4
6
|
module Auth
|
|
5
7
|
# HTTP client for communicating with the BuilderCore auth server
|
|
@@ -47,14 +49,16 @@ module Aha
|
|
|
47
49
|
# @param refresh_token [String, nil] Optional refresh token for automatic refresh
|
|
48
50
|
# @return [Session] Session validation result
|
|
49
51
|
def validate_session(session_token, refresh_token: nil)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
begin
|
|
53
|
+
claims = decode_and_verify_token(session_token)
|
|
54
|
+
return Session.from_claims(claims) if claims
|
|
55
|
+
rescue ExpiredTokenError
|
|
56
|
+
# Token has expired, attempt refresh
|
|
57
|
+
end
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
exp = claims["exp"]
|
|
55
|
-
if exp && refresh_token && should_refresh?(exp)
|
|
59
|
+
if refresh_token
|
|
56
60
|
begin
|
|
57
|
-
tokens =
|
|
61
|
+
tokens = authenticate_with_refresh_token(refresh_token: refresh_token)
|
|
58
62
|
new_claims = decode_and_verify_token(tokens[:session_token])
|
|
59
63
|
|
|
60
64
|
return Session.from_claims(
|
|
@@ -64,13 +68,11 @@ module Aha
|
|
|
64
68
|
new_refresh_token: tokens[:refresh_token]
|
|
65
69
|
)
|
|
66
70
|
rescue Error
|
|
67
|
-
|
|
68
|
-
# Refresh failed, return session with original token if still valid
|
|
69
|
-
return Session.invalid if Time.at(exp).utc < Time.now.utc
|
|
71
|
+
return Session.invalid
|
|
70
72
|
end
|
|
71
73
|
end
|
|
72
74
|
|
|
73
|
-
Session.
|
|
75
|
+
Session.invalid
|
|
74
76
|
rescue InvalidTokenError, ExpiredTokenError
|
|
75
77
|
Session.invalid
|
|
76
78
|
end
|
|
@@ -80,7 +82,7 @@ module Aha
|
|
|
80
82
|
# @param session_token [String] The session token to revoke
|
|
81
83
|
# @return [Boolean] true if successful
|
|
82
84
|
def logout(session_token:)
|
|
83
|
-
get("/api/core/
|
|
85
|
+
get("/api/core/auth_ui/logout", headers: { "Authorization" => "Bearer #{session_token}" })
|
|
84
86
|
true
|
|
85
87
|
rescue ApiError
|
|
86
88
|
false
|
|
@@ -90,8 +92,7 @@ module Aha
|
|
|
90
92
|
#
|
|
91
93
|
# @return [Hash] The JWKS response
|
|
92
94
|
def fetch_jwks
|
|
93
|
-
|
|
94
|
-
handle_response(response)
|
|
95
|
+
get("/api/core/auth/jwks/#{@configuration.client_id}")
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
private
|
|
@@ -125,17 +126,13 @@ module Aha
|
|
|
125
126
|
raise InvalidTokenError, "Invalid token: #{e.message}"
|
|
126
127
|
end
|
|
127
128
|
|
|
128
|
-
def should_refresh?(exp)
|
|
129
|
-
Time.at(exp).utc - Time.now.utc <= @configuration.refresh_threshold
|
|
130
|
-
end
|
|
131
|
-
|
|
132
129
|
def parse_token_response(response)
|
|
133
130
|
{
|
|
134
131
|
session_token: response["session_token"],
|
|
135
132
|
refresh_token: response["refresh_token"],
|
|
136
133
|
expires_at: response["expires_at"] ? Time.iso8601(response["expires_at"]) : nil,
|
|
137
134
|
user: response["user"]
|
|
138
|
-
}
|
|
135
|
+
}.with_indifferent_access
|
|
139
136
|
end
|
|
140
137
|
|
|
141
138
|
def get(path, headers: {})
|
|
@@ -22,7 +22,7 @@ module Aha
|
|
|
22
22
|
attr_accessor :timeout
|
|
23
23
|
|
|
24
24
|
def initialize
|
|
25
|
-
@server_url = ENV.fetch("AHA_CORE_SERVER_URL",
|
|
25
|
+
@server_url = ENV.fetch("AHA_CORE_SERVER_URL", "https://secure.aha.io/api/core")
|
|
26
26
|
@api_key = ENV.fetch("AHA_CORE_API_KEY", nil)
|
|
27
27
|
@client_id = ENV.fetch("APPLICATION_ID", nil)
|
|
28
28
|
@jwks_cache_ttl = 3600 # 1 hour
|
data/lib/aha/auth/user.rb
CHANGED
data/lib/aha/auth/version.rb
CHANGED
data/lib/aha/auth.rb
CHANGED
|
@@ -4,6 +4,8 @@ require "faraday"
|
|
|
4
4
|
require "faraday/retry"
|
|
5
5
|
require "jwt"
|
|
6
6
|
require "concurrent"
|
|
7
|
+
require "active_support"
|
|
8
|
+
require "active_support/core_ext"
|
|
7
9
|
|
|
8
10
|
require_relative "auth/version"
|
|
9
11
|
require_relative "auth/errors"
|
|
@@ -40,24 +42,59 @@ module Aha
|
|
|
40
42
|
|
|
41
43
|
# Generate login URL for redirecting users to the auth server
|
|
42
44
|
#
|
|
45
|
+
# @param session [Hash] Session hash to store the nonce for CSRF verification
|
|
43
46
|
# @param state [String] Optional state parameter to pass through the auth flow
|
|
44
|
-
# @param redirect_uri [String] Optional redirect URI after authentication
|
|
45
47
|
# @return [String] The login URL
|
|
46
|
-
def login_url(state: nil
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
def login_url(session:, state: nil)
|
|
49
|
+
# Generate a nonce for CSRF protection
|
|
50
|
+
nonce = SecureRandom.hex(10)
|
|
51
|
+
|
|
52
|
+
# Store nonce in the client's session if provided
|
|
53
|
+
session[:auth_nonce] = nonce if session
|
|
54
|
+
|
|
55
|
+
# Encode the state with nonce
|
|
56
|
+
state_data = {
|
|
57
|
+
tunneled_state: state,
|
|
58
|
+
nonce: nonce,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
encoded_state = Base64.urlsafe_encode64(state_data.to_json, padding: false)
|
|
62
|
+
|
|
63
|
+
params = {
|
|
64
|
+
client_id: configuration.client_id,
|
|
65
|
+
state: encoded_state
|
|
66
|
+
}
|
|
50
67
|
|
|
51
68
|
query = URI.encode_www_form(params)
|
|
52
|
-
"#{configuration.server_url}/
|
|
69
|
+
"#{configuration.server_url}/auth_ui/start?#{query}"
|
|
53
70
|
end
|
|
54
71
|
|
|
55
72
|
# Exchange an authorization code for tokens
|
|
56
73
|
#
|
|
57
|
-
# @param code [String] The authorization code from the callback
|
|
74
|
+
# @param code [String] The authorization code from the callback (may include nonce)
|
|
75
|
+
# @param session [Hash] Session hash containing the nonce for CSRF verification
|
|
58
76
|
# @return [Hash] Token response with :session_token, :refresh_token, :expires_at, :user
|
|
59
|
-
def authenticate_with_code(code:)
|
|
60
|
-
|
|
77
|
+
def authenticate_with_code(code:, session:)
|
|
78
|
+
# Split the code and nonce if present
|
|
79
|
+
actual_code, nonce = code.split(".", 2)
|
|
80
|
+
|
|
81
|
+
# Verify CSRF protection if nonce is present
|
|
82
|
+
if nonce
|
|
83
|
+
session_nonce = session[:auth_nonce]
|
|
84
|
+
|
|
85
|
+
# Verify nonce matches
|
|
86
|
+
if session_nonce.blank? || session_nonce != nonce
|
|
87
|
+
raise "CSRF verification failed: nonce mismatch"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Clear the nonce from session after verification
|
|
91
|
+
session.delete(:auth_nonce)
|
|
92
|
+
else
|
|
93
|
+
# If we fon't have a none, we can't verify CSRF.
|
|
94
|
+
raise "CSRF verification failed: unable to verify nonce"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
client.authenticate_with_code(code: actual_code)
|
|
61
98
|
end
|
|
62
99
|
|
|
63
100
|
# Refresh tokens using a refresh token
|
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: aha_builder_core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aha! Labs Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: concurrent-ruby
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -111,12 +125,6 @@ files:
|
|
|
111
125
|
- lib/aha/auth/users_resource.rb
|
|
112
126
|
- lib/aha/auth/version.rb
|
|
113
127
|
- lib/aha_builder_core.rb
|
|
114
|
-
- lib/generators/aha_builder_core/authentication/USAGE
|
|
115
|
-
- lib/generators/aha_builder_core/authentication/authentication_generator.rb
|
|
116
|
-
- lib/generators/aha_builder_core/authentication/templates/authentication.rb.erb
|
|
117
|
-
- lib/generators/aha_builder_core/authentication/templates/current.rb.erb
|
|
118
|
-
- lib/generators/aha_builder_core/authentication/templates/sessions_controller.rb.erb
|
|
119
|
-
- lib/generators/aha_builder_core/authentication/templates/user.rb.erb
|
|
120
128
|
homepage: https://www.aha.io
|
|
121
129
|
licenses:
|
|
122
130
|
- MIT
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
Description:
|
|
2
|
-
Generates authentication setup for Aha! Builder Core client
|
|
3
|
-
|
|
4
|
-
This generator creates:
|
|
5
|
-
- SessionsController with new and callback actions for authentication flow
|
|
6
|
-
- User model for storing authenticated user information
|
|
7
|
-
- Current model for thread-isolated current user access
|
|
8
|
-
- Authentication concern to be included in ApplicationController
|
|
9
|
-
|
|
10
|
-
Example:
|
|
11
|
-
bin/rails generate aha_builder_core:authentication
|
|
12
|
-
|
|
13
|
-
This will create:
|
|
14
|
-
app/controllers/sessions_controller.rb
|
|
15
|
-
app/controllers/concerns/authentication.rb
|
|
16
|
-
app/models/user.rb
|
|
17
|
-
app/models/current.rb
|
|
18
|
-
|
|
19
|
-
And add routes:
|
|
20
|
-
get "login", to: "sessions#new", as: :new_session
|
|
21
|
-
get "callback", to: "sessions#callback", as: :session_callback
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "rails/generators"
|
|
4
|
-
|
|
5
|
-
module AhaBuilderCore
|
|
6
|
-
module Generators
|
|
7
|
-
class AuthenticationGenerator < Rails::Generators::Base
|
|
8
|
-
source_root File.expand_path("templates", __dir__)
|
|
9
|
-
|
|
10
|
-
def create_sessions_controller
|
|
11
|
-
template "sessions_controller.rb.erb", "app/controllers/sessions_controller.rb"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def create_user_model
|
|
15
|
-
template "user.rb.erb", "app/models/user.rb"
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def create_current_model
|
|
19
|
-
template "current.rb.erb", "app/models/current.rb"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def create_authentication_concern
|
|
23
|
-
template "authentication.rb.erb", "app/controllers/concerns/authentication.rb"
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def add_routes
|
|
27
|
-
route <<~RUBY
|
|
28
|
-
get "login", to: "sessions#new", as: :new_session
|
|
29
|
-
get "callback", to: "sessions#callback", as: :session_callback
|
|
30
|
-
RUBY
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def display_instructions
|
|
34
|
-
say "\nAuthentication setup complete!", :green
|
|
35
|
-
say "\nNext steps:"
|
|
36
|
-
say "1. Configure your environment variables:"
|
|
37
|
-
say " - APPLICATION_URL: Your application's base URL"
|
|
38
|
-
say " - AHA_AUTH_SERVER_URL: The authentication server URL"
|
|
39
|
-
say " - AHA_AUTH_CLIENT_ID: Your client ID"
|
|
40
|
-
say " - AHA_AUTH_API_KEY: Your API key (if using server-to-server operations)"
|
|
41
|
-
say "\n2. Run migrations to create the users table:"
|
|
42
|
-
say " rails generate migration CreateUsers auth_identifier:string:index email:string first_name:string last_name:string email_verified:boolean"
|
|
43
|
-
say " rails db:migrate"
|
|
44
|
-
say "\n3. Include authentication in ApplicationController:"
|
|
45
|
-
say " include Authentication"
|
|
46
|
-
say "\n4. Protect routes with:"
|
|
47
|
-
say " before_action :authenticate"
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
private
|
|
51
|
-
|
|
52
|
-
def application_name
|
|
53
|
-
Rails.application.class.module_parent.name
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
module Authentication
|
|
2
|
-
extend ActiveSupport::Concern
|
|
3
|
-
|
|
4
|
-
included do
|
|
5
|
-
before_action :authenticate
|
|
6
|
-
helper_method :current_user, :logged_in?
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
private
|
|
10
|
-
|
|
11
|
-
def authenticate
|
|
12
|
-
if session[:session_token].present?
|
|
13
|
-
session_result = Aha::Auth.validate_session(
|
|
14
|
-
session[:session_token],
|
|
15
|
-
refresh_token: session[:refresh_token]
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
if session_result.valid?
|
|
19
|
-
# If tokens were refreshed, update stored tokens
|
|
20
|
-
if session_result.refreshed?
|
|
21
|
-
session[:session_token] = session_result.new_session_token
|
|
22
|
-
session[:refresh_token] = session_result.new_refresh_token
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Set current user
|
|
26
|
-
@current_user = User.find_by(id: session[:user_id])
|
|
27
|
-
Current.user = @current_user
|
|
28
|
-
else
|
|
29
|
-
reset_session
|
|
30
|
-
redirect_to new_session_path, alert: "Your session has expired. Please log in again."
|
|
31
|
-
end
|
|
32
|
-
else
|
|
33
|
-
redirect_to new_session_path
|
|
34
|
-
end
|
|
35
|
-
rescue Aha::Auth::ApiError => e
|
|
36
|
-
Rails.logger.error "Session validation failed: #{e.message}"
|
|
37
|
-
reset_session
|
|
38
|
-
redirect_to new_session_path, alert: "Authentication error. Please log in again."
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def current_user
|
|
42
|
-
@current_user ||= Current.user
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def logged_in?
|
|
46
|
-
current_user.present?
|
|
47
|
-
end
|
|
48
|
-
end
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
class SessionsController < ApplicationController
|
|
2
|
-
skip_before_action :authenticate, only: [:new, :callback]
|
|
3
|
-
|
|
4
|
-
def new
|
|
5
|
-
# Start authentication by redirecting to the auth URL
|
|
6
|
-
redirect_to Aha::Auth.login_url(
|
|
7
|
-
state: { return_to: params[:return_to] || root_path }.to_json,
|
|
8
|
-
redirect_uri: "#{ENV['APPLICATION_URL']}/callback"
|
|
9
|
-
), allow_other_host: true
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def callback
|
|
13
|
-
# Handle the callback from the authentication server
|
|
14
|
-
if params[:code].present?
|
|
15
|
-
result = Aha::Auth.authenticate_with_code(code: params[:code])
|
|
16
|
-
|
|
17
|
-
# Find or create a local user record linked to Builder Core user
|
|
18
|
-
user = User.find_or_initialize_by(auth_identifier: result[:user]["id"])
|
|
19
|
-
|
|
20
|
-
# Update local user attributes
|
|
21
|
-
user.update!(
|
|
22
|
-
email: result[:user]["email"],
|
|
23
|
-
first_name: result[:user]["first_name"],
|
|
24
|
-
last_name: result[:user]["last_name"],
|
|
25
|
-
email_verified: result[:user]["email_verified"]
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
# Store tokens in session
|
|
29
|
-
session[:session_token] = result[:session_token]
|
|
30
|
-
session[:refresh_token] = result[:refresh_token]
|
|
31
|
-
session[:user_id] = user.id
|
|
32
|
-
|
|
33
|
-
# Parse state to get return_to path
|
|
34
|
-
state = JSON.parse(params[:state]) rescue {}
|
|
35
|
-
redirect_to state["return_to"] || root_path
|
|
36
|
-
else
|
|
37
|
-
# Authentication failed or was cancelled
|
|
38
|
-
redirect_to new_session_path, alert: "Authentication failed. Please try again."
|
|
39
|
-
end
|
|
40
|
-
rescue Aha::Auth::ApiError => e
|
|
41
|
-
Rails.logger.error "Authentication failed: #{e.message}"
|
|
42
|
-
redirect_to new_session_path, alert: "Authentication failed. Please try again."
|
|
43
|
-
end
|
|
44
|
-
end
|