Class: CollavreLinear::OAuthTokenService
- Inherits:
-
Object
- Object
- CollavreLinear::OAuthTokenService
- Defined in:
- app/services/collavre_linear/o_auth_token_service.rb
Overview
Handles Linear OAuth 2.0 flows:
- authorize_url : build the URL that sends the user to Linear's OAuth screen
- exchange : trade an authorization code for tokens (authorization_code grant)
- refresh : silently refresh tokens when they are expiring soon (refresh_token grant)
Secret resolution order: DB Resolver > ENV > Rails.application.credentials (consistent with CollavreLinear::Client and other engine services in this codebase)
Defined Under Namespace
Classes: Error
Constant Summary collapse
- LINEAR_AUTH_ENDPOINT =
"https://linear.app/oauth/authorize"- LINEAR_TOKEN_ENDPOINT =
"https://api.linear.app/oauth/token"- OAUTH_SCOPES =
NOTE:
adminis intentionally NOT requested. Linear rejects it for app actors ("App users can't request admin scopes"), which is how we authorize (actor: "app").adminis only needed to auto-create webhooks via the API, so inbound sync is wired up MANUALLY instead — the integration modal shows a one-time setup guide (URL + events) after linking, and the admin pastes back the signing secret Linear generates for the webhook. "read,write,issues:create,comments:create"
Class Method Summary collapse
-
.authorize_url(state:, creative_id:) ⇒ String
Build the URL that redirects the user to Linear for authorization.
-
.exchange(code) ⇒ Hash
Exchange an authorization code for access + refresh tokens.
-
.missing_config ⇒ Object
Names of any OAuth secrets that are unset/blank.
-
.refresh(account) ⇒ CollavreLinear::Account
Refresh tokens if the account is expiring soon.
Class Method Details
.authorize_url(state:, creative_id:) ⇒ String
Build the URL that redirects the user to Linear for authorization.
48 49 50 51 52 53 54 55 56 57 58 |
# File 'app/services/collavre_linear/o_auth_token_service.rb', line 48 def (state:, creative_id:) params = { response_type: "code", client_id: client_id, redirect_uri: redirect_uri, scope: OAUTH_SCOPES, actor: "app", state: state } "#{LINEAR_AUTH_ENDPOINT}?#{URI.encode_www_form(params)}" end |
.exchange(code) ⇒ Hash
Exchange an authorization code for access + refresh tokens.
64 65 66 67 68 69 70 71 72 73 |
# File 'app/services/collavre_linear/o_auth_token_service.rb', line 64 def exchange(code) params = { grant_type: "authorization_code", code: code, redirect_uri: redirect_uri, client_id: client_id, client_secret: client_secret } post_token!(params) end |
.missing_config ⇒ Object
Names of any OAuth secrets that are unset/blank. When non-empty we must
NOT start the flow: a blank redirect_uri produces an authorize URL like
...&redirect_uri&scope=... that Linear accepts but can never redirect
back from, so the callback never fires and the account is never created
(Linear just shows the app as "already installed"). client_secret is
checked too: it isn't in the authorize URL, but a blank one would let the
user complete Linear authorization only to fail at token exchange in the
callback — surface the misconfiguration before leaving the app instead.
37 38 39 40 41 |
# File 'app/services/collavre_linear/o_auth_token_service.rb', line 37 def missing_config { LINEAR_CLIENT_ID: client_id, LINEAR_CLIENT_SECRET: client_secret, LINEAR_OAUTH_REDIRECT_URI: redirect_uri }.select { |_, v| v.blank? }.keys end |
.refresh(account) ⇒ CollavreLinear::Account
Refresh tokens if the account is expiring soon. Persists new token values to the account and returns it. No-ops (returns account unchanged) if not expiring soon.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'app/services/collavre_linear/o_auth_token_service.rb', line 81 def refresh(account) return account unless account.token_expiring_soon? params = { grant_type: "refresh_token", refresh_token: account.refresh_token, client_id: client_id, client_secret: client_secret } tokens = post_token!(params) attrs = { access_token: tokens[:access_token], token_expires_at: Time.current + tokens[:expires_in].to_i.seconds } attrs[:refresh_token] = tokens[:refresh_token] if tokens[:refresh_token].present? account.update!(attrs) account end |