Skip to content

Conversation

@marcus-lundgren
Copy link

@marcus-lundgren marcus-lundgren commented Sep 3, 2025

Removed the file based persistent storage of the tokens, as they were stored in clear text. They will only be stored in variables.

The initial value of the github-token can now be set by providing a function that provides it. This enables this token to be securely stored outside of Emacs.

@marcus-lundgren marcus-lundgren marked this pull request as draft September 3, 2025 20:56
@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch from 68b1fc9 to 76a1010 Compare September 9, 2025 21:16
@marcus-lundgren
Copy link
Author

@karthink - Do you have any input in how you imagine the workflow to be for this API regarding the GitHub token?

With the changes made just recently to present the login function, is it reasonable to have a workflow to perform that in order to get the token that can then be stored elsewhere?

@karthink
Copy link
Owner

karthink commented Sep 9, 2025 via email

@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch from b31c107 to 7fa9017 Compare September 15, 2025 11:51
@marcus-lundgren
Copy link
Author

@kiennq - A friendly reminder about this PR :)

@kiennq
Copy link
Contributor

kiennq commented Sep 23, 2025

Sorry for the late reply. Can you add a default/example for github-token-init? At least it should be defaulted to the current workflow where the token is saved/restored from a file, else we're looking at reauth everytime gptel is reloaded

@marcus-lundgren
Copy link
Author

Thanks for your reply, @kiennq!

This is what I am currently using:

(use-package gptel
  :defer t
  :config (setq
           gptel-backend (gptel-make-gh-copilot "Copilot"
                           :github-token-init (lambda () (read-passwd "Copilot token: "))))
  )

I store my token in a password manager, so I use read-passwd in order to enter it when calling gptel. I will only be prompted once per Emacs session for this, which is a very reasonable workflow for me as I rarely restart Emacs. As it takes just a function, it is trivial to make it read from a file instead.

With the current implementation in this PR, the login function is not able to persistently store the newly acquired token. The new token is only stored in a variable and I take the it from the message log, which is not an ideal scenario and hence my request for feedback earlier. Another approach could be to introduce load and save functions that can be given as parameters to gptel-make-gh-copilot. It would then allow the user to choose how it should be stored and saved.

Before making the changes in gptel itself, I used the code below as a patch to control how the token was stored. This also has the behavior of only requesting the token once. As you may notice, the logic for the chat message token was kept, but this was before me realising that that is also a thing to keep secret.

(use-package gptel
  :config
  (progn
    (defvar my/gh-token nil "Store the Copilot token for GPTel commands.")
    (defalias 'original-gptel--gh-save (symbol-function 'gptel--gh-save))
    (defalias 'original-gptel--gh-restore (symbol-function 'gptel--gh-restore))
    (defun gptel--gh-restore (file)
      (if (equal file gptel-gh-github-token-file)
          (if my/gh-token
              my/gh-token
            (progn 
              (setq my/gh-token
                    (let ((token (read-passwd "Copilot token: ")))
                      (if (string= "" token) nil token)))
              my/gh-token))
        (original-gptel--gh-restore file)))
    (defun gptel--gh-save (file obj)
      (message "New GH token: %s" obj)
      (if (equal file gptel-gh-github-token-file)
          (setq my/gh-token obj)
        (original-gptel--gh-save file obj)))))

@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch from 7fa9017 to c293cbf Compare October 4, 2025 19:40
@marcus-lundgren
Copy link
Author

@kiennq - Any thoughts? :)

@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch from c293cbf to 63c1bec Compare October 11, 2025 20:11
@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch from 63c1bec to 83b7a84 Compare October 22, 2025 18:33
@marcus-lundgren
Copy link
Author

marcus-lundgren commented Oct 22, 2025

@kiennq - I've now changed the implementation so that it defaults to use the file as before for the github-token.

What has been additionally added is the ability to create a custom save function for the github-token. This solves the question that I raised before, as it is now up to the one redefining the save function to decide how to handle an update.

A sample use-package snippet to override it:

(use-package gptel
  :defer t
  :config
  (defvar my/gh-token nil "Store the Copilot token for GPTel commands.")
  (setq
   gptel-gh-github-token-load (lambda ()
                                (if my/gh-token
                                    my/gh-token
                                  (progn 
                                    (setq my/gh-token
                                          (let ((token (read-passwd "Copilot token: ")))
                                            (if (string= "" token) nil token)))
                                    my/gh-token)))
   gptel-gh-github-token-save (lambda (token)
                                (setq my/gh-token token))
   gptel-backend (gptel-make-gh-copilot "Copilot"))
  )

@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch from c0727e8 to 0bc293c Compare October 27, 2025 21:59
@marcus-lundgren
Copy link
Author

@kiennq - a friendly reminder concerning this. I think the current solution in this MR strikes a good balance between ease of use and choice of security. If you agree, then I can start working on changes to the README.

@karthink - do you have any thoughts regarding the latest proposal?

@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch 4 times, most recently from 0ed7be3 to 51ba56e Compare November 5, 2025 21:01
@marcus-lundgren
Copy link
Author

@kiennq, @karthink - another reminder.

@karthink
Copy link
Owner

@marcus-lundgren just dropping a note to say this PR remains on my radar -- I just need a longer chunk of time to review it than other PRs since I'm not the author of the token management code. I'll do a general code review but I trust @kiennq's judgment on the design.

@marcus-lundgren
Copy link
Author

@karthink - thank you for the status update! As you may have noticed, I am rebasing the branch continuously to fix any conflicts (as happened a few days ago).

@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch from 51ba56e to c67ae91 Compare November 16, 2025 21:26
@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch from c67ae91 to fdfee2a Compare November 18, 2025 19:51
@marcus-lundgren
Copy link
Author

Updated config example with the builtin cache functionality:

(use-package gptel
  :defer t
  :config
  (setq
   gptel-gh-github-token-load-function (lambda () (read-passwd "Copilot token: "))
   gptel-gh-github-token-save-function (lambda (token) (message "Token saved: %s" token))
   gptel-backend (gptel-make-gh-copilot "Copilot"))
  )

@kiennq
Copy link
Contributor

kiennq commented Nov 20, 2025

I left one comment, else it LGTM

@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch 2 times, most recently from 7332d9a to 684e145 Compare November 20, 2025 21:53
@marcus-lundgren
Copy link
Author

@kiennq, @karthink - I resolved the review comments you had after fixing them. If the workflow is that the reviewer should do that, then please let me know.

I've now also updated the README file with information about the customizable variables.

Some suitable information for the NEWS file can be found below. Do you want me to add it?

  • Breaking change: Removed the customizable variable gptel-gh-token-file, as the chat token is no longer persistently stored.
  • Added the customizable variables gptel-gh-github-token-load-function and gptel-gh-github-token-save-function. This enables the GitHub OAuth token to be loaded from and saved to arbitrary places.

@marcus-lundgren marcus-lundgren marked this pull request as ready for review November 20, 2025 22:09
@karthink
Copy link
Owner

If I understand the changes correctly, you can no longer have more than one Github copilot backend defined, since the authentication is now shared across all gh backends? Is this correct?

@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch from 684e145 to 0bca814 Compare November 23, 2025 09:52
@marcus-lundgren
Copy link
Author

@karthink - Yes, you are correct, but I would argue that while it was technically true before, it wasn't properly implemented as such. Simply due to the fact that one could only persistently store one token and there was no official way to set the GitHub token manually.

Calling the login function will overwrite the token stored in the file:

https://github.com/karthink/gptel/blob/master/gptel-gh.el#L267

It would also fall back to the persistently stored token on failure to renew the chat token:

https://github.com/karthink/gptel/blob/master/gptel-gh.el#L287 followed by
https://github.com/karthink/gptel/blob/master/gptel-gh.el#L298

With the changes I've made as a basis, one could:

  • Reintroduce the tokens to the backend to be used as a cache variable
  • Add the load and save functions as parameters to the gptel-make-gh-copilot function and add them as references to the backend. This would enable several backends to use the same overloaded functions.

But I think that might be suitable for a follow up PR to make it proper.

Any thoughts, @kiennq? Do you agree with my understanding of the logic and that it is suitable for another PR?

@kiennq
Copy link
Contributor

kiennq commented Nov 24, 2025

Any thoughts, @kiennq? Do you agree with my understanding of the logic and that it is suitable for another PR?

Although we only store one token to file, we can have multiple backends and each backend can ask for a github token by using the login function. So, we can still use multiple github accounts/backends.

gptel-gh.el Outdated
(defun gptel--gh-restore (file)
"Restore saved object from FILE."
(when (file-exists-p file)
(defun gptel--gh-restore-from-file ()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you keep this function the same signature as before, i.e., taking a file param.
I'm working on a change to automatically fetch the model list and will use this to store the cached model list as well

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the file param to the function again. Do you need a similar change to the equivalent save function?

Copy link
Contributor

@kiennq kiennq Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Can we remove the -from-file suffix as well? It takes a parameter of file so it's quite obvious that this is restoring from file

(float-time) will implicitly use (current-time) if the argument is not specified.
Removed the file based persistent storage of the chat token.

Introduced overloadable function variables that determines how a token
is loaded and saved.
@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch from 0bca814 to 5cbf148 Compare November 24, 2025 20:03
@marcus-lundgren
Copy link
Author

marcus-lundgren commented Nov 24, 2025

Any thoughts, @kiennq? Do you agree with my understanding of the logic and that it is suitable for another PR?

Although we only store one token to file, we can have multiple backends and each backend can ask for a github token by using the login function. So, we can still use multiple github accounts/backends.

As @karthink identified in his comment, that will no longer work in the current implementation in this PR.

As the GitHub account used for a backend can change behind the scenes as described earlier, I would like to suggest adding proper support for token handling. I am thinking something along the lines of adding a parameter to the gptel-make-gh-copilot function which contains the desired GitHub username. That would allow the user to explicitly state which account to use, which in turn makes it possible to store the token in account specific files (e.g. github_token_karthink). Then we will avoid the following scenario that I fall into:

  1. Have one backend for a personal account using free tier and one backend for a work account using a paid subscription
  2. The currently stored token in the file is for the personal account as that was logged in last
  3. We receive an error in the following logic, triggering the work account to read and make use of the personal account's token from the file:
It would also fall back to the persistently stored token on failure to renew the chat token:

https://github.com/karthink/gptel/blob/master/gptel-gh.el#L287 followed by
https://github.com/karthink/gptel/blob/master/gptel-gh.el#L298
  1. The backend meant for the work account will now burn through the free tier's limited usage limit.

gptel-gh-github-token-load-function and gptel-gh-github-token-save-function should then take the GitHub username as a parameter, so that the implementation can deduce how to save/load the account specific token. With this, it would probably be best to replace the gptel-gh-github-token-file custom variable with a variable that points to a directory, as the filename will have the username in it. What do you think, @kiennq?

@marcus-lundgren marcus-lundgren force-pushed the gh-change-token-management branch from 67b5059 to 198ec46 Compare November 24, 2025 20:49
@kiennq
Copy link
Contributor

kiennq commented Nov 24, 2025

How about making the gptel-gh-github-token-save/load-function as part of the gptel--gh backend? Same with the chat token cache. That would allow the user to be able to create multiple backends that target different github copilot backends.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants