defer a.mutex.Unlock()
var code ChatGPTDeviceCode
+
err := a.postJSON(ctx, "/api/accounts/deviceauth/usercode", map[string]string{
"client_id": chatgptClientID,
}, &code)
_, err = fmt.Fprintf(out, "Open %s/codex/device on any device and enter this code:\n\n %s\n\nWaiting for the approval...\n", a.issuer, userCode)
if err != nil {
- return err
+ return fmt.Errorf("write device code: %w", err)
}
grant, err := a.poll(ctx, code.DeviceAuthID, userCode, chatgptPollInterval(code.Interval))
}
_, err = fmt.Fprintln(out, "Logged in.")
+ if err != nil {
+ return fmt.Errorf("write login result: %w", err)
+ }
- return err
+ return nil
}
// read reads the token file.
}
var tokens ChatGPTTokenFile
+
err = json.Unmarshal(data, &tokens)
if err == nil && (tokens.AccessToken == "" || tokens.RefreshToken == "" || tokens.AccountID == "") {
err = errors.New("a token or the account ID is missing")
// It keeps the old refresh token and ID token when the answer has none.
func (a *ChatGPTAuth) refresh(ctx context.Context, tokens ChatGPTTokenFile) (ChatGPTTokenFile, error) {
var response ChatGPTTokenResponse
+
err := a.postJSON(ctx, "/oauth/token", map[string]string{
"client_id": chatgptClientID,
"grant_type": "refresh_token",
for {
var grant ChatGPTDeviceGrant
+
status, err := a.post(ctx, "/api/accounts/deviceauth/token", "application/json", body, &grant)
if err == nil && (grant.AuthorizationCode == "" || grant.CodeVerifier == "") {
err = errors.New("device grant has no authorization code or code verifier")
select {
case <-ctx.Done():
timer.Stop()
+
return ChatGPTDeviceGrant{}, fmt.Errorf("wait for approval: %w", ctx.Err())
case <-timer.C:
}
}
var response ChatGPTTokenResponse
+
_, err := a.post(ctx, "/oauth/token", "application/x-www-form-urlencoded", []byte(form.Encode()), &response)
if err != nil {
return ChatGPTTokenFile{}, fmt.Errorf("exchange code for tokens: %w", err)
}
request.Header.Set("Content-Type", contentType)
+
response, err := a.http.Do(request)
if err != nil {
return 0, fmt.Errorf("send request: %w", err)
// none.
func chatgptAccountID(token string) string {
var claims map[string]json.RawMessage
+
if !chatgptDecodeClaims(token, &claims) {
return ""
}