-
Notifications
You must be signed in to change notification settings - Fork 69
Expand file tree
/
Copy pathlogin_oauth.go
More file actions
108 lines (92 loc) · 2.87 KB
/
login_oauth.go
File metadata and controls
108 lines (92 loc) · 2.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package main
import (
"context"
"fmt"
"io"
"os/exec"
"runtime"
"time"
"github.com/sourcegraph/src-cli/internal/api"
"github.com/sourcegraph/src-cli/internal/cmderrors"
"github.com/sourcegraph/src-cli/internal/oauth"
)
func runOAuthLogin(ctx context.Context, p loginParams) error {
endpointArg := cleanEndpoint(p.endpoint)
client, err := oauthLoginClient(ctx, p, endpointArg)
if err != nil {
printLoginProblem(p.out, fmt.Sprintf("OAuth Device flow authentication failed: %s", err))
fmt.Fprintln(p.out, loginAccessTokenMessage(endpointArg))
return cmderrors.ExitCode1
}
if err := validateCurrentUser(ctx, client, p.out, endpointArg); err != nil {
return err
}
fmt.Fprintln(p.out)
fmt.Fprint(p.out, "✔︎ Authenticated with OAuth credentials")
fmt.Fprintln(p.out)
return nil
}
func oauthLoginClient(ctx context.Context, p loginParams, endpoint string) (api.Client, error) {
token, err := runOAuthDeviceFlow(ctx, endpoint, p.out, p.oauthClient)
if err != nil {
return nil, err
}
if err := oauth.StoreToken(ctx, token); err != nil {
fmt.Fprintln(p.out)
fmt.Fprintf(p.out, "⚠️ Warning: Failed to store token in keyring store: %q. Continuing with this session only.\n", err)
}
return api.NewClient(api.ClientOpts{
Endpoint: p.cfg.Endpoint,
AdditionalHeaders: p.cfg.AdditionalHeaders,
Flags: p.apiFlags,
Out: p.out,
ProxyURL: p.cfg.ProxyURL,
ProxyPath: p.cfg.ProxyPath,
OAuthToken: token,
}), nil
}
func runOAuthDeviceFlow(ctx context.Context, endpoint string, out io.Writer, client oauth.Client) (*oauth.Token, error) {
authResp, err := client.Start(ctx, endpoint, nil)
if err != nil {
return nil, err
}
authURL := authResp.VerificationURIComplete
msg := fmt.Sprintf("If your browser did not open automatically, visit %s.", authURL)
if authURL == "" {
authURL = authResp.VerificationURI
msg = fmt.Sprintf("If your browser did not open automatically, visit %s and enter the user code %s", authURL, authResp.DeviceCode)
}
_ = openInBrowser(authURL)
fmt.Fprintln(out)
fmt.Fprint(out, msg)
fmt.Fprintln(out)
fmt.Fprint(out, "Waiting for authorization... ")
defer fmt.Fprintf(out, "DONE\n\n")
interval := time.Duration(authResp.Interval) * time.Second
if interval <= 0 {
interval = 5 * time.Second
}
resp, err := client.Poll(ctx, endpoint, authResp.DeviceCode, interval, authResp.ExpiresIn)
if err != nil {
return nil, err
}
token := resp.Token(endpoint)
token.ClientID = client.ClientID()
return token, nil
}
func openInBrowser(url string) error {
if url == "" {
return nil
}
var cmd *exec.Cmd
switch runtime.GOOS {
case "darwin":
cmd = exec.Command("open", url)
case "windows":
// "start" is a cmd.exe built-in; the empty string is the window title.
cmd = exec.Command("cmd", "/c", "start", "", url)
default:
cmd = exec.Command("xdg-open", url)
}
return cmd.Run()
}