77require "base64"
88require "digest"
99require "uri"
10+ require "monitor"
1011
1112module Lara
1213 # This class is used to interact with Lara via the REST API.
@@ -31,7 +32,7 @@ def initialize(auth_method, base_url: DEFAULT_BASE_URL, connection_timeout: nil,
3132 @connection_timeout = connection_timeout
3233 @read_timeout = read_timeout
3334 @extra_headers = { }
34- @auth_mutex = Mutex . new
35+ @auth_mutex = Monitor . new
3536
3637 @connection = build_connection
3738 end
@@ -93,23 +94,37 @@ def request(method, path, body: nil, files: nil, headers: nil, params: nil, raw_
9394 make_request ( method , path , body : body , files : files , headers : headers , params : params ,
9495 raw_response : raw_response , &callback )
9596 rescue LaraApiError => e
96- # Auto-refresh token on 401 with jwt expired
97- raise unless e . status_code == 401 && e . message . include? ( "jwt expired" )
97+ raise unless e . status_code == 401
9898
99- refresh_token
100- # Retry once with new token
99+ @auth_mutex . synchronize { refresh_or_reauthenticate }
101100 make_request ( method , path , body : body , files : files , headers : headers , params : params ,
102101 raw_response : raw_response , &callback )
103102 end
104103
105104 def ensure_valid_token
106105 @auth_mutex . synchronize do
107- return if @auth_token
106+ return if @auth_token && ! @auth_token . token_expired?
108107
109- raise LaraError , "No authentication method available" unless @credentials
108+ refresh_or_reauthenticate
109+ end
110+ end
111+
112+ def refresh_or_reauthenticate
113+ if @auth_token &.refresh_token && !@auth_token . refresh_token . empty?
114+ begin
115+ do_refresh
116+ return
117+ rescue StandardError
118+ raise unless @credentials
119+ end
120+ end
110121
122+ if @credentials
111123 @auth_token = authenticate
124+ return
112125 end
126+
127+ raise LaraError , "No authentication method available for token renewal"
113128 end
114129
115130 def authenticate
@@ -143,47 +158,37 @@ def authenticate
143158 raise LaraApiError . from_response ( response ) unless response . success?
144159
145160 data = JSON . parse ( response . body )
146- refresh_token = response . headers [ "x-lara-refresh-token" ]
161+ refresh_token_value = response . headers [ "x-lara-refresh-token" ]
147162
148- raise LaraError , "Missing refresh token in authentication response" unless refresh_token
163+ raise LaraError , "Missing refresh token in authentication response" unless refresh_token_value
149164
150- AuthToken . new ( data [ "token" ] , refresh_token )
165+ AuthToken . new ( data [ "token" ] , refresh_token_value )
151166 end
152167
153- def refresh_token
154- @auth_mutex . synchronize do
155- return unless @auth_token
168+ def do_refresh
169+ raise LaraError , "No refresh token available" unless @auth_token &.refresh_token
156170
157- conn = Faraday . new ( url : @base_url ) do |c |
158- c . adapter Faraday . default_adapter
159- end
160-
161- response = conn . post ( "/v2/auth/refresh" ) do |req |
162- req . headers = {
163- "Date" => Time . now . utc . strftime ( "%a, %d %b %Y %H:%M:%S GMT" ) ,
164- "X-Lara-SDK-Name" => "lara-ruby" ,
165- "X-Lara-SDK-Version" => Lara ::VERSION ,
166- "Authorization" => "Bearer #{ @auth_token &.refresh_token } "
167- }
168- end
171+ conn = Faraday . new ( url : @base_url ) do |c |
172+ c . adapter Faraday . default_adapter
173+ end
169174
170- if response . success?
171- data = JSON . parse ( response . body )
172- refresh_token = response . headers [ "x-lara-refresh-token" ]
173-
174- if refresh_token
175- @auth_token = AuthToken . new ( data [ "token" ] , refresh_token )
176- else
177- # Refresh failed, force re-authentication
178- @auth_token = nil
179- ensure_valid_token
180- end
181- else
182- # Refresh failed, force re-authentication
183- @auth_token = nil
184- ensure_valid_token
185- end
175+ response = conn . post ( "/v2/auth/refresh" ) do |req |
176+ req . headers = {
177+ "Date" => Time . now . utc . strftime ( "%a, %d %b %Y %H:%M:%S GMT" ) ,
178+ "X-Lara-SDK-Name" => "lara-ruby" ,
179+ "X-Lara-SDK-Version" => Lara ::VERSION ,
180+ "Authorization" => "Bearer #{ @auth_token . refresh_token } "
181+ }
186182 end
183+
184+ raise LaraApiError . from_response ( response ) unless response . success?
185+
186+ data = JSON . parse ( response . body )
187+ refresh_token_value = response . headers [ "x-lara-refresh-token" ]
188+
189+ raise LaraError , "Missing refresh token in refresh response" unless refresh_token_value
190+
191+ @auth_token = AuthToken . new ( data [ "token" ] , refresh_token_value )
187192 end
188193
189194 def make_request ( method , path , body : nil , files : nil , headers : nil , params : nil , raw_response : false , &callback )
0 commit comments