diff --git a/lua/pending/sync/gcal.lua b/lua/pending/sync/gcal.lua index 4669b89..12264dc 100644 --- a/lua/pending/sync/gcal.lua +++ b/lua/pending/sync/gcal.lua @@ -169,8 +169,20 @@ local function fmt_counts(parts) return table.concat(items, ' | ') end +---@param callback fun(access_token: string): nil +local function with_token(callback) + oauth.async(function() + local token = oauth.google_client:get_access_token() + if not token then + log.warn('not authenticated — run :Pending auth') + return + end + callback(token) + end) +end + function M.push() - oauth.with_token(oauth.google_client, 'gcal', function(access_token) + with_token(function(access_token) local calendars, cal_err = get_all_calendars(access_token) if cal_err or not calendars then log.error(cal_err or 'failed to fetch calendars') diff --git a/lua/pending/sync/gtasks.lua b/lua/pending/sync/gtasks.lua index 6a5e570..a2a6da0 100644 --- a/lua/pending/sync/gtasks.lua +++ b/lua/pending/sync/gtasks.lua @@ -436,9 +436,20 @@ local function sync_setup(access_token) return tasklists, s, now_ts end +---@param callback fun(access_token: string): nil +local function with_token(callback) + oauth.async(function() + local token = oauth.google_client:get_access_token() + if not token then + log.warn('not authenticated — run :Pending auth') + return + end + callback(token) + end) +end function M.push() - oauth.with_token(oauth.google_client, 'gtasks', function(access_token) + with_token(function(access_token) local tasklists, s, now_ts = sync_setup(access_token) if not tasklists then return @@ -464,7 +475,7 @@ function M.push() end function M.pull() - oauth.with_token(oauth.google_client, 'gtasks', function(access_token) + with_token(function(access_token) local tasklists, s, now_ts = sync_setup(access_token) if not tasklists then return @@ -491,7 +502,7 @@ function M.pull() end function M.sync() - oauth.with_token(oauth.google_client, 'gtasks', function(access_token) + with_token(function(access_token) local tasklists, s, now_ts = sync_setup(access_token) if not tasklists then return diff --git a/lua/pending/sync/oauth.lua b/lua/pending/sync/oauth.lua index 8c30268..8539ea6 100644 --- a/lua/pending/sync/oauth.lua +++ b/lua/pending/sync/oauth.lua @@ -25,9 +25,6 @@ local BUNDLED_CLIENT_SECRET = 'PLACEHOLDER' local OAuthClient = {} OAuthClient.__index = OAuthClient -local _active_close = nil -local _sync_in_flight = false - ---@class pending.oauth local M = {} @@ -52,30 +49,6 @@ function M.async(fn) coroutine.resume(coroutine.create(fn)) end ----@param client pending.OAuthClient ----@param name string ----@param callback fun(access_token: string): nil -function M.with_token(client, name, callback) - if _sync_in_flight then - require('pending.log').warn(name .. ': sync operation in progress — please wait') - return - end - _sync_in_flight = true - M.async(function() - local token = client:get_access_token() - if not token then - _sync_in_flight = false - require('pending.log').warn(name .. ': not authenticated — run :Pending auth') - return - end - local ok, err = pcall(callback, token) - _sync_in_flight = false - if not ok then - require('pending.log').error(name .. ': ' .. tostring(err)) - end - end) -end - ---@param str string ---@return string function M.url_encode(str) @@ -375,11 +348,6 @@ end ---@param on_complete? fun(): nil ---@return nil function OAuthClient:auth(on_complete) - if _active_close then - _active_close() - _active_close = nil - end - local creds = self:resolve_credentials() if creds.client_id == BUNDLED_CLIENT_ID then log.error(self.name .. ': no credentials configured — run :Pending auth') @@ -411,11 +379,14 @@ function OAuthClient:auth(on_complete) .. '&scope=' .. M.url_encode(self.scope) .. '&access_type=offline' - .. '&prompt=select_account%20consent' + .. '&prompt=consent' .. '&code_challenge=' .. M.url_encode(code_challenge) .. '&code_challenge_method=S256' + vim.ui.open(auth_url) + log.info('Opening browser for Google authorization...') + local server = vim.uv.new_tcp() local server_closed = false local function close_server() @@ -423,20 +394,10 @@ function OAuthClient:auth(on_complete) return end server_closed = true - if _active_close == close_server then - _active_close = nil - end server:close() end - _active_close = close_server - - local bind_ok, bind_err = pcall(server.bind, server, '127.0.0.1', port) - if not bind_ok or bind_err == nil then - close_server() - log.error(self.name .. ': port ' .. port .. ' already in use — try again in a moment') - return - end + server:bind('127.0.0.1', port) server:listen(1, function(err) if err then return @@ -469,9 +430,6 @@ function OAuthClient:auth(on_complete) end) end) - vim.ui.open(auth_url) - log.info('Opening browser for Google authorization...') - vim.defer_fn(function() if not server_closed then close_server() @@ -512,14 +470,14 @@ function OAuthClient:_exchange_code(creds, code, code_verifier, port, on_complet }, { text = true }) if result.code ~= 0 then - self:clear_tokens() + self:_wipe() log.error('Token exchange failed.') return end local ok, decoded = pcall(vim.json.decode, result.stdout or '') if not ok or not decoded.access_token then - self:clear_tokens() + self:_wipe() log.error('Invalid token response.') return end @@ -540,10 +498,6 @@ end ---@return nil function OAuthClient:clear_tokens() - if _active_close then - _active_close() - _active_close = nil - end os.remove(self:token_path()) end diff --git a/spec/oauth_spec.lua b/spec/oauth_spec.lua index a4a6f1d..520227d 100644 --- a/spec/oauth_spec.lua +++ b/spec/oauth_spec.lua @@ -142,13 +142,8 @@ describe('oauth', function() it('falls back to bundled credentials', function() config.reset() vim.g.pending = { data_path = tmpdir .. '/tasks.json' } - local orig_load = oauth.load_json_file - oauth.load_json_file = function() - return nil - end local c = oauth.new({ name = 'gtasks', scope = 'x', port = 0, config_key = 'gtasks' }) local creds = c:resolve_credentials() - oauth.load_json_file = orig_load assert.equals(oauth._BUNDLED_CLIENT_ID, creds.client_id) assert.equals(oauth._BUNDLED_CLIENT_SECRET, creds.client_secret) end)