@@ -7,17 +7,19 @@ import (
77 "io"
88 "os"
99 "strings"
10+ "time"
1011
1112 "github.com/sourcegraph/src-cli/internal/api"
1213 "github.com/sourcegraph/src-cli/internal/cmderrors"
14+ "github.com/sourcegraph/src-cli/internal/oauthdevice"
1315)
1416
1517func init () {
1618 usage := `'src login' helps you authenticate 'src' to access a Sourcegraph instance with your user credentials.
1719
1820Usage:
1921
20- src login SOURCEGRAPH_URL
22+ src login [flags] SOURCEGRAPH_URL
2123
2224Examples:
2325
@@ -32,6 +34,11 @@ Examples:
3234 Use OAuth device flow to authenticate:
3335
3436 $ src login --device-flow https://sourcegraph.com
37+
38+
39+ Override the default client id used during device flow when authenticating:
40+
41+ $ src login --device-flow https://sourcegraph.com --client-id sgo_my_own_client_id
3542`
3643
3744 flagSet := flag .NewFlagSet ("login" , flag .ExitOnError )
@@ -41,7 +48,9 @@ Examples:
4148 }
4249
4350 var (
44- apiFlags = api .NewFlags (flagSet )
51+ apiFlags = api .NewFlags (flagSet )
52+ useDeviceFlow = flagSet .Bool ("device-flow" , false , "Use OAuth device flow to obtain an access token interactively" )
53+ OAuthClientID = flagSet .String ("client-id" , oauthdevice .DefaultClientID , "Client ID to use with OAuth device flow. Will use the predefined src cli client ID if not specified." )
4554 )
4655
4756 handler := func (args []string ) error {
@@ -56,9 +65,21 @@ Examples:
5665 return cmderrors .Usage ("expected exactly one argument: the Sourcegraph URL, or SRC_ENDPOINT to be set" )
5766 }
5867
68+ if * OAuthClientID == "" {
69+ return cmderrors .Usage ("no value specified for client-id" )
70+ }
71+
5972 client := cfg .apiClient (apiFlags , io .Discard )
6073
61- return loginCmd (context .Background (), cfg , client , endpoint , os .Stdout )
74+ return loginCmd (context .Background (), loginParams {
75+ cfg : cfg ,
76+ client : client ,
77+ endpoint : endpoint ,
78+ out : os .Stdout ,
79+ useDeviceFlow : * useDeviceFlow ,
80+ apiFlags : apiFlags ,
81+ deviceFlowClient : oauthdevice .NewClient (* OAuthClientID ),
82+ })
6283 }
6384
6485 commands = append (commands , & command {
@@ -68,8 +89,21 @@ Examples:
6889 })
6990}
7091
71- func loginCmd (ctx context.Context , cfg * config , client api.Client , endpointArg string , out io.Writer ) error {
72- endpointArg = cleanEndpoint (endpointArg )
92+ type loginParams struct {
93+ cfg * config
94+ client api.Client
95+ endpoint string
96+ out io.Writer
97+ useDeviceFlow bool
98+ apiFlags * api.Flags
99+ deviceFlowClient oauthdevice.Client
100+ }
101+
102+ func loginCmd (ctx context.Context , p loginParams ) error {
103+ endpointArg := cleanEndpoint (p .endpoint )
104+ cfg := p .cfg
105+ client := p .client
106+ out := p .out
73107
74108 printProblem := func (problem string ) {
75109 fmt .Fprintf (out , "❌ Problem: %s\n " , problem )
@@ -90,7 +124,19 @@ func loginCmd(ctx context.Context, cfg *config, client api.Client, endpointArg s
90124
91125 noToken := cfg .AccessToken == ""
92126 endpointConflict := endpointArg != cfg .Endpoint
93- if noToken || endpointConflict {
127+
128+ if p .useDeviceFlow {
129+ token , err := runDeviceFlow (ctx , endpointArg , out , p .deviceFlowClient )
130+ if err != nil {
131+ printProblem (fmt .Sprintf ("Device flow authentication failed: %s" , err ))
132+ fmt .Fprintln (out , createAccessTokenMessage )
133+ return cmderrors .ExitCode1
134+ }
135+
136+ cfg .AccessToken = token
137+ cfg .Endpoint = endpointArg
138+ client = cfg .apiClient (p .apiFlags , out )
139+ } else if noToken || endpointConflict {
94140 fmt .Fprintln (out )
95141 switch {
96142 case noToken :
0 commit comments