Class: Zoom::OauthService

Inherits:
Object
  • Object
show all
Defined in:
app/services/zoom/oauth_service.rb

Overview

Handles Zoom OAuth 2.0 token storage and refresh via OauthCredential.

Zoom uses standard OAuth 2.0 Authorization Code flow:

  1. User authorizes via OmniAuth (Devise)
  2. Callback stores tokens in OauthCredential
  3. When token expires (1 hour), refresh_token is used to get a new pair
  4. Refresh tokens expire after 90 days of non-use

Reference: https://developers.zoom.us/docs/integrations/oauth/

Defined Under Namespace

Classes: TokenRefreshError

Constant Summary collapse

PROVIDER =

Provider.

'zoom'
TOKEN_URL =

URL for token.

'https://zoom.us/oauth/token'
API_BASE =

Api base.

'https://api.zoom.us/v2'

Instance Method Summary collapse

Constructor Details

#initialize(account:) ⇒ OauthService

Returns a new instance of OauthService.

Parameters:

  • account (Account)

    the CRM account whose Zoom credential to manage



27
28
29
30
31
# File 'app/services/zoom/oauth_service.rb', line 27

def initialize(account:)
  @account       = 
  @client_id     = Heatwave::Configuration.fetch(:zoom, :client_id)
  @client_secret = Heatwave::Configuration.fetch(:zoom, :client_secret)
end

Instance Method Details

#access_token!String

Get a valid access token, refreshing if expired.

Returns:

  • (String)

    the access_token

Raises:



78
79
80
81
82
83
84
# File 'app/services/zoom/oauth_service.rb', line 78

def access_token!
  credential = OauthCredential.for(PROVIDER, account: @account)
  raise TokenRefreshError, 'No Zoom credential found' unless credential

  refresh! if credential.token_expired?
  credential.reload.access_token
end

#connected?Boolean

Whether this account has a stored Zoom credential.

Returns:

  • (Boolean)


88
89
90
# File 'app/services/zoom/oauth_service.rb', line 88

def connected?
  OauthCredential.for(PROVIDER, account: @account).present?
end

#disconnect!Object

Remove the stored credential (disconnect).



93
94
95
# File 'app/services/zoom/oauth_service.rb', line 93

def disconnect!
  OauthCredential.destroy_by(provider: PROVIDER, account_id: @account.id)
end

#healthy?Boolean

Verify the current token is valid by calling Zoom API.
Attempts refresh first if token is expired.

Returns:

  • (Boolean)


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'app/services/zoom/oauth_service.rb', line 100

def healthy?
  credential = OauthCredential.for(PROVIDER, account: @account)
  return false if credential&.access_token.blank?

  refresh! if credential.token_expired?
  credential.reload

  response = connection.get("#{API_BASE}/users/me") do |req|
    req.headers['Authorization'] = "Bearer #{credential.access_token}"
  end
  response.status == 200
rescue TokenRefreshError, StandardError => e
  Rails.logger.error("[Zoom::OauthService] Health check failed for account #{@account.id}: #{e.message}")
  false
end

#refresh!OauthCredential

Refresh the stored access token.

Returns:

Raises:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'app/services/zoom/oauth_service.rb', line 49

def refresh!
  credential = OauthCredential.for(PROVIDER, account: @account)
  raise TokenRefreshError, 'No Zoom credential found' unless credential
  raise TokenRefreshError, 'No refresh token available' if credential.refresh_token.blank?

  encoded = Base64.strict_encode64("#{@client_id}:#{@client_secret}")

  response = connection.post(TOKEN_URL) do |req|
    req.headers['Authorization'] = "Basic #{encoded}"
    req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
    req.body = URI.encode_www_form(
      grant_type: 'refresh_token',
      refresh_token: credential.refresh_token
    )
  end

  raise TokenRefreshError, "Token refresh failed (#{response.status}): #{response.body}" unless response.status == 200

  data = JSON.parse(response.body)
  credential.update!(
    access_token: data['access_token'],
    refresh_token: data['refresh_token'].presence || credential.refresh_token,
    expires_at: data['expires_in'] ? data['expires_in'].to_i.seconds.from_now : 1.hour.from_now
  )
  credential
end

#store_from_omniauth!(credentials) ⇒ OauthCredential

Store tokens from OmniAuth callback.

Parameters:

  • credentials (Hash)

    omniauth['credentials'] hash

Returns:



36
37
38
39
40
41
42
43
44
# File 'app/services/zoom/oauth_service.rb', line 36

def store_from_omniauth!(credentials)
  cred = OauthCredential.find_or_initialize_by(provider: PROVIDER, account_id: @account.id)
  cred.access_token  = credentials['token']
  cred.refresh_token = credentials['refresh_token'] if credentials['refresh_token'].present?
  cred.expires_at    = credentials['expires_at'] ? Time.zone.at(credentials['expires_at']) : 1.hour.from_now
  cred.token_type    = 'Bearer'
  cred.save!
  cred
end