feat(linear): Support refresh tokens and migrate legacy OAuth tokens (#13721)

Linear is deprecating long-lived OAuth2 access tokens (valid for 10
years) in favor of short-lived access tokens with refresh tokens.
Starting October 1, 2025, all new OAuth2 apps will default to refresh
tokens. Linear will no longer issue long-lived access tokens. Please
read more details
[here](https://linear.app/developers/oauth-2-0-authentication#migrate-to-using-refresh-tokens)
We currently use long-lived tokens in our Linear integration (valid for
up to 10 years). To remain compatible, this PR ensures compatibility by
supporting refresh-token-based auth and migrating existing legacy
tokens.

Fixes
https://linear.app/chatwoot/issue/CW-5541/migrate-linear-oauth2-integration-to-support-refresh-tokens
This commit is contained in:
Muhsin Keloth
2026-03-17 13:09:03 +04:00
committed by GitHub
parent 2a90652f05
commit a8d53a6df4
8 changed files with 487 additions and 22 deletions
+7 -2
View File
@@ -3,8 +3,9 @@ class Linear
REVOKE_URL = 'https://api.linear.app/oauth/revoke'.freeze
PRIORITY_LEVELS = (0..4).to_a
def initialize(access_token)
def initialize(access_token, refresh_token: nil)
@access_token = access_token
@refresh_token = refresh_token
raise ArgumentError, 'Missing Credentials' if access_token.blank?
end
@@ -79,9 +80,13 @@ class Linear
end
def revoke_token
token = @refresh_token.presence || @access_token
token_type_hint = @refresh_token.present? ? 'refresh_token' : 'access_token'
response = HTTParty.post(
REVOKE_URL,
headers: { 'Authorization' => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
headers: { 'Content-Type' => 'application/x-www-form-urlencoded' },
body: { token: token, token_type_hint: token_type_hint }
)
response.success?
end