mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-30 18:47:51 +00:00 
			
		
		
		
	feat: Add support for customizing expiry of widget token (#12446)
This PR is part of https://github.com/chatwoot/chatwoot/pull/12259. It adds a default expiry of 180 days for tokens issued on the widget. The expiry can be customized based on customer requests and internal security requirements. Co-authored-by: Balasaheb Dubale <bdubale@entrata.com>
This commit is contained in:
		| @@ -1,8 +1,10 @@ | |||||||
| class Widget::TokenService | class Widget::TokenService | ||||||
|  |   DEFAULT_EXPIRY_DAYS = 180 | ||||||
|  |  | ||||||
|   pattr_initialize [:payload, :token] |   pattr_initialize [:payload, :token] | ||||||
|  |  | ||||||
|   def generate_token |   def generate_token | ||||||
|     JWT.encode payload, secret_key, 'HS256' |     JWT.encode payload_with_expiry, secret_key, 'HS256' | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def decode_token |   def decode_token | ||||||
| @@ -15,6 +17,24 @@ class Widget::TokenService | |||||||
|  |  | ||||||
|   private |   private | ||||||
|  |  | ||||||
|  |   def payload_with_expiry | ||||||
|  |     payload.merge(exp: exp, iat: iat) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def iat | ||||||
|  |     Time.zone.now.to_i | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def exp | ||||||
|  |     iat + expire_in.days.to_i | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def expire_in | ||||||
|  |     # Value is stored in days, defaulting to 6 months (180 days) | ||||||
|  |     token_expiry_value = InstallationConfig.find_by(name: 'WIDGET_TOKEN_EXPIRY')&.value | ||||||
|  |     (token_expiry_value.presence || DEFAULT_EXPIRY_DAYS).to_i | ||||||
|  |   end | ||||||
|  |  | ||||||
|   def secret_key |   def secret_key | ||||||
|     Rails.application.secret_key_base |     Rails.application.secret_key_base | ||||||
|   end |   end | ||||||
|   | |||||||
| @@ -439,3 +439,11 @@ | |||||||
|   locked: false |   locked: false | ||||||
|   description: 'Zone ID for the Cloudflare domain' |   description: 'Zone ID for the Cloudflare domain' | ||||||
| ## ------ End of Configs added for Cloudflare ------ ## | ## ------ End of Configs added for Cloudflare ------ ## | ||||||
|  |  | ||||||
|  | ## ------ Customizations for Customers ------ ## | ||||||
|  | - name: WIDGET_TOKEN_EXPIRY | ||||||
|  |   display_title: 'Widget Token Expiry' | ||||||
|  |   value: 180 | ||||||
|  |   locked: false | ||||||
|  |   description: 'Token expiry in days' | ||||||
|  | ## ------ End of Customizations for Customers ------ ## | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								spec/services/widget/token_service_expiry_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								spec/services/widget/token_service_expiry_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | require 'rails_helper' | ||||||
|  |  | ||||||
|  | RSpec.describe Widget::TokenService, type: :service do | ||||||
|  |   describe 'token expiry configuration' do | ||||||
|  |     let(:service) { described_class.new(payload: {}) } | ||||||
|  |  | ||||||
|  |     before do | ||||||
|  |       # Clear any existing configs to ensure test isolation | ||||||
|  |       InstallationConfig.where(name: 'WIDGET_TOKEN_EXPIRY').destroy_all | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'with valid configuration' do | ||||||
|  |       before do | ||||||
|  |         create(:installation_config, name: 'WIDGET_TOKEN_EXPIRY', value: '30') | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       it 'uses the configured value for token expiry' do | ||||||
|  |         freeze_time do | ||||||
|  |           token = service.generate_token | ||||||
|  |           decoded = JWT.decode(token, Rails.application.secret_key_base, true, algorithm: 'HS256').first | ||||||
|  |           expect(decoded['iat']).to eq(Time.now.to_i) | ||||||
|  |           expect(decoded['exp']).to eq(30.days.from_now.to_i) | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |  | ||||||
|  |     context 'with empty configuration' do | ||||||
|  |       before do | ||||||
|  |         create(:installation_config, name: 'WIDGET_TOKEN_EXPIRY', value: '') | ||||||
|  |       end | ||||||
|  |  | ||||||
|  |       it 'uses the default expiry' do | ||||||
|  |         freeze_time do | ||||||
|  |           token = service.generate_token | ||||||
|  |           decoded = JWT.decode(token, Rails.application.secret_key_base, true, algorithm: 'HS256').first | ||||||
|  |           expect(decoded['iat']).to eq(Time.now.to_i) | ||||||
|  |           expect(decoded['exp']).to eq(180.days.from_now.to_i) | ||||||
|  |         end | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
		Reference in New Issue
	
	Block a user
	 Pranav
					Pranav