Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ go 1.23.0
toolchain go1.24.1

require golang.org/x/oauth2 v0.30.0

require rsc.io/qr v0.2.0
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
45 changes: 43 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package main

import (
"bytes"
"context"
"flag"
"fmt"
Expand All @@ -31,6 +32,7 @@ import (

"golang.org/x/oauth2"
"golang.org/x/oauth2/endpoints"
"rsc.io/qr"
)

// configByHost lists default config for several public hosts.
Expand Down Expand Up @@ -494,15 +496,54 @@ func getToken(ctx context.Context, c oauth2.Config, authURLSuffix string) (*oaut
func getDeviceToken(ctx context.Context, c oauth2.Config) (*oauth2.Token, error) {
deviceAuth, err := c.DeviceAuth(ctx)
if err != nil {
log.Fatalln(err)
return nil, err
}
if verbose {
fmt.Fprintf(os.Stderr, "%+v\n", deviceAuth)
}
fmt.Fprintf(os.Stderr, "Please enter code %s at %s\n", deviceAuth.UserCode, deviceAuth.VerificationURI)
if deviceAuth.VerificationURIComplete != "" {
fmt.Fprintf(os.Stderr, "Please scan the QR code or enter code %s at %s\n", deviceAuth.UserCode, deviceAuth.VerificationURI)
fmt.Fprintln(os.Stderr)
writeQRCode(os.Stderr, deviceAuth.VerificationURIComplete)
} else {
fmt.Fprintf(os.Stderr, "Please enter code %s at %s\n", deviceAuth.UserCode, deviceAuth.VerificationURI)
}
return c.DeviceAccessToken(ctx, deviceAuth)
}

func writeQRCode(w io.Writer, data string) error {
// use low redundancy to generate small QR codes for terminal output.
// we assume the user is sitting in front of their screen so image quality shouldn't be an issue
code, err := qr.Encode(data, qr.L)
if err != nil {
return err
}

var (
black = "\033[40m \033[0m"
white = "\033[107m \033[0m"
whiteLine = strings.Repeat(white, code.Size+2) + "\n"
buf bytes.Buffer
)
buf.WriteString(whiteLine)
for y := range code.Size {
buf.WriteString(white)
for x := range code.Size {
if code.Black(x, y) {
buf.WriteString(black)
} else {
buf.WriteString(white)
}
}
buf.WriteString(white)
buf.WriteRune('\n')
}
buf.WriteString(whiteLine)

_, err = buf.WriteTo(w)
return err
}

func replaceHost(e oauth2.Endpoint, host string) oauth2.Endpoint {
e.AuthURL = replaceHostInURL(e.AuthURL, host)
e.TokenURL = replaceHostInURL(e.TokenURL, host)
Expand Down
11 changes: 11 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"os"
"strings"
"testing"
)
Expand All @@ -22,6 +23,16 @@ func TestConfig(t *testing.T) {
}
}

func TestQR(t *testing.T) {
msg := os.Getenv("QR_MSG")
if msg == "" {
t.Skip("no QR_MSG set, skipping")
}
if err := writeQRCode(os.Stdout, msg); err != nil {
t.Fatal(err)
}
}

func FuzzParse(f *testing.F) {
f.Add("key=value")
f.Add("key=")
Expand Down