Skip to content

Commit 2e3cd13

Browse files
authored
feat: support authentication (zbirenbaum#47)
1 parent 3d3f6a3 commit 2e3cd13

File tree

5 files changed

+169
-54
lines changed

5 files changed

+169
-54
lines changed

README.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,9 @@ On its own, this plugin will do nothing. You must either use https://github.com/
1212

1313
## Install
1414

15-
### Preliminary Steps
15+
### Authentication
1616

17-
Currently, you must have had the original copilot.vim installed and set up at some point, as the authentication steps you do during its setup create files in ~/.config/github-copilot which copilot.lua must read from to function. Fairly soon, copilot.lua will be able to perform this authentication step on its own, but as the plugin is in early stages, this has not yet been fully implemented.
18-
19-
Install copilot.vim with `use {"github/copilot.vim"}`, `:PackerSync`, restart, and run `:Copilot` to be prompted for the necessary setup steps.
20-
21-
After the setup steps are complete for copilot.vim, ensure that ~/.config/github-copilot has files in it, and then you are free to uninstall copilot.vim and proceed to the following steps.
17+
Once copilot is started, run `:CopilotAuth` to start the authentication process.
2218

2319
### Setup
2420

lua/copilot/auth.lua

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
local util = require("copilot.util")
2+
3+
local M = {}
4+
5+
function M.setup(client)
6+
local function echo(message)
7+
vim.cmd('echom "[Copilot] ' .. message .. '"')
8+
end
9+
10+
local function copy_to_clipboard(str)
11+
vim.cmd(string.format(
12+
[[
13+
let @+ = "%s"
14+
let @* = "%s"
15+
]],
16+
str,
17+
str
18+
))
19+
end
20+
21+
local request = function(method, params)
22+
local co = coroutine.running()
23+
params.id = util.get_next_id()
24+
client.rpc.request(method, params, function(err, data)
25+
coroutine.resume(co, err, data)
26+
end)
27+
local err, data = coroutine.yield()
28+
if err then
29+
echo("Error: " .. err)
30+
error(err)
31+
end
32+
return data
33+
end
34+
35+
local function open_signin_popup(code, url)
36+
local lines = {
37+
" [Copilot] ",
38+
"",
39+
" First copy your one-time code: ",
40+
" " .. code .. " ",
41+
" In your browser, visit: ",
42+
" " .. url .. " ",
43+
"",
44+
" ...waiting, it might take a while and ",
45+
" this popup will auto close once done... ",
46+
}
47+
local height, width = #lines, math.max(unpack(vim.tbl_map(function(line)
48+
return #line
49+
end, lines)))
50+
51+
local bufnr = vim.api.nvim_create_buf(false, true)
52+
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
53+
local winid = vim.api.nvim_open_win(bufnr, true, {
54+
relative = "editor",
55+
style = "minimal",
56+
border = "single",
57+
row = (vim.o.lines - height) / 2,
58+
col = (vim.o.columns - width) / 2,
59+
height = height,
60+
width = width,
61+
})
62+
vim.api.nvim_win_set_option(winid, "winhighlight", "Normal:Normal")
63+
64+
return function()
65+
vim.api.nvim_win_close(winid, true)
66+
vim.api.nvim_buf_delete(bufnr, { force = true })
67+
end
68+
end
69+
70+
local initiate_setup = coroutine.wrap(function()
71+
local data = request("checkStatus", {})
72+
73+
if data.user then
74+
echo("Authenticated as GitHub user: " .. data.user)
75+
return
76+
end
77+
78+
local signin = request("signInInitiate", {})
79+
80+
if not signin.verificationUri then
81+
echo("Failed to setup")
82+
return
83+
end
84+
85+
copy_to_clipboard(signin.userCode)
86+
87+
local close_signin_popup = open_signin_popup(signin.userCode, signin.verificationUri)
88+
89+
local confirm = request("signInConfirm", { userCode = signin.userCode })
90+
91+
close_signin_popup()
92+
93+
if string.lower(confirm.status) ~= "ok" then
94+
echo("Authentication failure: " .. confirm.error.message)
95+
return
96+
end
97+
98+
echo("Authenticated as GitHub user: " .. confirm.user)
99+
end)
100+
101+
initiate_setup()
102+
end
103+
104+
local function find_config_path()
105+
local config = vim.fn.expand("$XDG_CONFIG_HOME")
106+
if config and vim.fn.isdirectory(config) > 0 then
107+
return config
108+
elseif vim.fn.has("win32") > 0 then
109+
config = vim.fn.expand("~/AppData/Local")
110+
if vim.fn.isdirectory(config) > 0 then
111+
return config
112+
end
113+
else
114+
config = vim.fn.expand("~/.config")
115+
if vim.fn.isdirectory(config) > 0 then
116+
return config
117+
else
118+
print("Error: could not find config path")
119+
end
120+
end
121+
end
122+
123+
local function json_body(response)
124+
if response.headers["content-type"] == "application/json" then
125+
return vim.json.decode(response.body)
126+
end
127+
end
128+
129+
local function oauth_user(token)
130+
return vim.fn.system('curl -s --header "Authorization: Bearer ' .. token .. '" https://api.github.com/user')
131+
end
132+
133+
local function oauth_save(oauth_token)
134+
local user_data = oauth_user(oauth_token)
135+
local github = { oauth_token = oauth_token, user = user_data.login }
136+
return github
137+
end
138+
139+
M.get_cred = function()
140+
local userdata = vim.json.decode(
141+
vim.api.nvim_eval("readfile('" .. find_config_path() .. "/github-copilot/hosts.json')")[1]
142+
)
143+
local token = userdata["github.com"].oauth_token
144+
local user = oauth_user(token)
145+
return { user = user, token = token }
146+
end
147+
148+
return M

lua/copilot/extensions/auth.lua

Lines changed: 0 additions & 48 deletions
This file was deleted.

lua/copilot/init.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ local create_cmds = function (_)
2929
panel.send_request()
3030
require("copilot.extensions.print_panel").create(panel.buf)
3131
end, {})
32+
33+
vim.api.nvim_create_user_command("CopilotAuth", function()
34+
require("copilot.util").auth()
35+
end, {})
3236
end
3337

3438
local config_handler = function(opts)

lua/copilot/util.lua

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
local M = {}
22

3+
local id = 0
4+
function M.get_next_id()
5+
id = id + 1
6+
return id
7+
end
8+
39
-- keep for debugging reasons
410
M.get_editor_info = function ()
511
local info = vim.empty_dict()
@@ -95,5 +101,14 @@ M.get_copilot_path = function(plugin_path)
95101
end
96102
end
97103

104+
M.auth = function ()
105+
local c = M.get_copilot_client()
106+
if not c then
107+
print("[Copilot] not running yet!")
108+
return
109+
end
110+
require("copilot.auth").setup(c)
111+
end
112+
98113

99114
return M

0 commit comments

Comments
 (0)