chore: add oauth flow

This commit is contained in:
Muhsin Keloth
2025-04-21 19:50:08 +05:30
parent b2a1e3282c
commit 1df0159f65
5 changed files with 147 additions and 2 deletions

View File

@@ -0,0 +1,71 @@
class Github::CallbacksController < ApplicationController
include Github::IntegrationHelper
def show
@response = oauth_client.auth_code.get_token(
params[:code],
redirect_uri: "#{base_url}/github/callback"
)
handle_response
rescue StandardError => e
Rails.logger.error("Github callback error: #{e.message}")
redirect_to github_redirect_uri
end
private
def oauth_client
app_id = GlobalConfigService.load('GITHUB_CLIENT_ID', nil)
app_secret = GlobalConfigService.load('GITHUB_CLIENT_SECRET', nil)
OAuth2::Client.new(
app_id,
app_secret,
{
site: 'https://github.com',
token_url: '/login/oauth/access_token',
authorize_url: '/login/oauth/authorize'
}
)
end
def handle_response
hook = account.hooks.new(
access_token: parsed_body['access_token'],
status: 'enabled',
app_id: 'github',
settings: {
token_type: parsed_body['token_type'],
scope: parsed_body['scope']
}
)
hook.save!
redirect_to github_redirect_uri
rescue StandardError => e
Rails.logger.error("Github callback error: #{e.message}")
redirect_to github_redirect_uri
end
def account
@account ||= Account.find(account_id)
end
def account_id
return unless params[:state]
verify_github_token(params[:state])
end
def github_redirect_uri
"#{ENV.fetch('FRONTEND_URL', nil)}/app/accounts/#{account.id}/settings/integrations/github"
end
def parsed_body
@parsed_body ||= @response.response.parsed
end
def base_url
ENV.fetch('FRONTEND_URL', 'http://localhost:3000')
end
end

View File

@@ -0,0 +1,47 @@
module Github::IntegrationHelper
# Generates a signed JWT token for Github integration
#
# @param account_id [Integer] The account ID to encode in the token
# @return [String, nil] The encoded JWT token or nil if client secret is missing
def generate_github_token(account_id)
return if client_secret.blank?
JWT.encode(token_payload(account_id), client_secret, 'HS256')
rescue StandardError => e
Rails.logger.error("Failed to generate Github token: #{e.message}")
nil
end
def token_payload(account_id)
{
sub: account_id,
iat: Time.current.to_i
}
end
# Verifies and decodes a Github JWT token
#
# @param token [String] The JWT token to verify
# @return [Integer, nil] The account ID from the token or nil if invalid
def verify_github_token(token)
return if token.blank? || client_secret.blank?
decode_token(token, client_secret)
end
private
def client_secret
@client_secret ||= GlobalConfigService.load('GITHUB_CLIENT_SECRET', nil)
end
def decode_token(token, secret)
JWT.decode(token, secret, true, {
algorithm: 'HS256',
verify_expiration: true
}).first['sub']
rescue StandardError => e
Rails.logger.error("Unexpected error verifying Github token: #{e.message}")
nil
end
end

View File

@@ -1,5 +1,6 @@
class Integrations::App
include Linear::IntegrationHelper
include Github::IntegrationHelper
attr_accessor :params
def initialize(params)
@@ -29,7 +30,12 @@ class Integrations::App
# There is no way to get the account_id from the linear callback
# so we are using the generate_linear_token method to generate a token and encode it in the state parameter
def encode_state
case params[:id]
when 'linear'
generate_linear_token(Current.account.id)
when 'github'
generate_github_token(Current.account.id)
end
end
def action
@@ -38,6 +44,8 @@ class Integrations::App
"#{params[:action]}&client_id=#{ENV.fetch('SLACK_CLIENT_ID', nil)}&redirect_uri=#{self.class.slack_integration_url}"
when 'linear'
build_linear_action
when 'github'
build_github_action
else
params[:action]
end
@@ -70,6 +78,17 @@ class Integrations::App
].join('&')
end
def build_github_action
app_id = GlobalConfigService.load('GITHUB_CLIENT_ID', nil)
[
"#{params[:action]}?response_type=code",
"client_id=#{app_id}",
"redirect_uri=#{self.class.github_integration_url}",
"state=#{encode_state}",
'scope=repo'
].join('&')
end
def enabled?(account)
case params[:id]
when 'webhook'
@@ -93,6 +112,10 @@ class Integrations::App
"#{ENV.fetch('FRONTEND_URL', nil)}/linear/callback"
end
def self.github_integration_url
"#{ENV.fetch('FRONTEND_URL', nil)}/github/callback"
end
class << self
def apps
Hashie::Mash.new(APPS_CONFIG)

View File

@@ -190,6 +190,6 @@ github:
id: github
logo: github.png
i18n_key: github
action: /github
action: https://github.com/login/oauth/authorize
hook_type: account
allow_multiple_hooks: false

View File

@@ -479,6 +479,10 @@ Rails.application.routes.draw do
resources :delivery_status, only: [:create]
end
namespace :github do
get :callback, to: 'callbacks#show'
end
get 'microsoft/callback', to: 'microsoft/callbacks#show'
get 'google/callback', to: 'google/callbacks#show'
get 'instagram/callback', to: 'instagram/callbacks#show'