@@ -7,13 +7,11 @@ import (
77 "encoding/json"
88 "errors"
99 "fmt"
10- "net/http"
1110 "net/url"
1211 "os"
1312 "path"
1413 "path/filepath"
1514 "strings"
16- "time"
1715
1816 "connectrpc.com/connect"
1917 "github.com/google/uuid"
@@ -26,9 +24,6 @@ import (
2624 "golang.org/x/oauth2"
2725)
2826
29- const Auth0ClientId = "j3LylZtIosVPZtouKI8WuVHmE6Lluva1"
30- const Auth0Domain = "om-prod.eu.auth0.com"
31-
3227var logLevel string
3328
3429//go:generate sh -c "echo -n $(git describe --tags --long) > commit.txt"
@@ -147,6 +142,7 @@ func readLocalToken(homeDir string, expectedScopes []string) (string, []string,
147142 }
148143 }
149144
145+ log .Debugf ("Using local token from %v" , path )
150146 return token .AccessToken , currentScopes , nil
151147}
152148
@@ -204,92 +200,28 @@ func ensureToken(ctx context.Context, requiredScopes []string) (context.Context,
204200 // keep replacing it
205201 requestScopes := append (requiredScopes , localScopes ... )
206202
207- // Authenticate using the oauth resource owner password flow
203+ // Authenticate using the oauth device authorization flow
208204 config := oauth2.Config {
209- ClientID : Auth0ClientId ,
210- Scopes : requestScopes ,
205+ ClientID : viper .GetString ("cli-auth0-client-id" ),
211206 Endpoint : oauth2.Endpoint {
212- AuthURL : fmt .Sprintf ("https://%v/authorize" , Auth0Domain ),
213- TokenURL : fmt .Sprintf ("https://%v/oauth/token" , Auth0Domain ),
207+ AuthURL : fmt .Sprintf ("https://%v/authorize" , viper .GetString ("cli-auth0-domain" )),
208+ TokenURL : fmt .Sprintf ("https://%v/oauth/token" , viper .GetString ("cli-auth0-domain" )),
209+ DeviceAuthURL : fmt .Sprintf ("https://%v/oauth/device/code" , viper .GetString ("cli-auth0-domain" )),
214210 },
215- RedirectURL : "http://127.0.0.1:7837/oauth/callback" ,
211+ Scopes : requestScopes ,
216212 }
217213
218- tokenChan := make (chan * oauth2.Token , 1 )
219- // create a random token for this exchange
220- oAuthStateString := uuid .New ().String ()
221-
222- // Start the web server to listen for the callback
223- handler := func (w http.ResponseWriter , r * http.Request ) {
224- ctx := r .Context ()
225-
226- queryParts , err := url .ParseQuery (r .URL .RawQuery )
227- if err != nil {
228- log .WithContext (ctx ).WithError (err ).WithFields (log.Fields {
229- "url" : r .URL ,
230- }).Error ("Failed to parse url" )
231- }
232-
233- // Use the authorization code that is pushed to the redirect
234- // URL.
235- code := queryParts ["code" ][0 ]
236- log .WithContext (ctx ).Debugf ("Got code: %v" , code )
237-
238- state := queryParts ["state" ][0 ]
239- log .WithContext (ctx ).Debugf ("Got state: %v" , state )
240-
241- if state != oAuthStateString {
242- log .WithContext (ctx ).Errorf ("Invalid state, expected %v, got %v" , oAuthStateString , state )
243- return
244- }
245-
246- // Exchange will do the handshake to retrieve the initial access token.
247- log .WithContext (ctx ).Debug ("Exchanging code for token" )
248- tok , err := config .Exchange (ctx , code )
249- if err != nil {
250- log .WithContext (ctx ).Error (err )
251- return
252- }
253- log .WithContext (ctx ).Debug ("Got token" )
254-
255- tokenChan <- tok
256-
257- // show success page
258- msg := "<p><strong>Success!</strong></p>"
259- msg = msg + "<p>You are authenticated and can now return to the CLI.</p>"
260- fmt .Fprint (w , msg )
214+ deviceCode , err := config .DeviceAuth (ctx , oauth2 .SetAuthURLParam ("audience" , "https://api.overmind.tech" ))
215+ if err != nil {
216+ return ctx , fmt .Errorf ("error getting device code: %w" , err )
261217 }
262218
263- audienceOption := oauth2 .SetAuthURLParam ("audience" , "https://api.overmind.tech" )
264-
265- u := config .AuthCodeURL (oAuthStateString , oauth2 .AccessTypeOnline , audienceOption )
266- log .WithContext (ctx ).Infof ("Follow this link to authenticate: %v" , Underline .TextStyle (u ))
219+ fmt .Printf ("Go to %v and verify this code: %v\n " , deviceCode .VerificationURIComplete , deviceCode .UserCode )
267220
268- // Start the webserver
269- log .WithContext (ctx ).Trace ("Starting webserver to listen for callback, press Ctrl+C to cancel" )
270- srv := & http.Server {Addr : ":7837" , ReadHeaderTimeout : 30 * time .Second }
271- http .HandleFunc ("/oauth/callback" , handler )
272-
273- go func () {
274- if err := srv .ListenAndServe (); err != http .ErrServerClosed {
275- // unexpected error. port in use?
276- log .WithContext (ctx ).Errorf ("HTTP Server error: %v" , err )
277- }
278- }()
279-
280- // Wait for the token or cancel
281- var token * oauth2.Token
282- select {
283- case token = <- tokenChan :
284- // Keep working
285- case <- ctx .Done ():
286- return ctx , ctx .Err ()
287- }
288-
289- // Stop the server
290- err = srv .Shutdown (ctx )
221+ token , err := config .DeviceAccessToken (ctx , deviceCode )
291222 if err != nil {
292- log .WithContext (ctx ).WithError (err ).Warn ("failed to shutdown auth callback server, but continuing anyway" )
223+ fmt .Printf (": %v\n " , err )
224+ return ctx , fmt .Errorf ("Error exchanging Device Code for for access token: %w" , err )
293225 }
294226
295227 // Check that we actually got the claims we asked for. If you don't have
@@ -438,15 +370,17 @@ func init() {
438370 log .WithError (err ).Fatal ("could not bind api key to env" )
439371 }
440372
441- // tracing
373+ // internal configs
374+ rootCmd .PersistentFlags ().String ("cli-auth0-client-id" , "QMfjMww3x4QTpeXiuRtMV3JIQkx6mZa4" , "OAuth Client ID to use when connecting with auth0" )
375+ rootCmd .PersistentFlags ().String ("cli-auth0-domain" , "om-prod.eu.auth0.com" , "Auth0 domain to connect to" )
442376 rootCmd .PersistentFlags ().String ("honeycomb-api-key" , "" , "If specified, configures opentelemetry libraries to submit traces to honeycomb. This requires --otel to be set." )
443- // Mark this as hidden. This means that it will still be parsed of supplied,
377+
378+ // Mark these as hidden. This means that it will still be parsed of supplied,
444379 // and we will still look for it in the environment, but it won't be shown
445380 // in the help
446- err = rootCmd .PersistentFlags ().MarkHidden ("honeycomb-api-key" )
447- if err != nil {
448- log .WithError (err ).Fatal ("could not mark `honeycomb-api-key` flag as hidden" )
449- }
381+ must (rootCmd .PersistentFlags ().MarkHidden ("cli-auth0-client-id" ))
382+ must (rootCmd .PersistentFlags ().MarkHidden ("cli-auth0-domain" ))
383+ must (rootCmd .PersistentFlags ().MarkHidden ("honeycomb-api-key" ))
450384
451385 // Create groups
452386 rootCmd .AddGroup (& cobra.Group {
@@ -502,3 +436,11 @@ func initConfig() {
502436 viper .SetEnvKeyReplacer (replacer )
503437 viper .AutomaticEnv () // read in environment variables that match
504438}
439+
440+ // must panics if the passed in error is not nil
441+ // use this for init-time error checking of viper/cobra stuff that sometimes errors if the flag does not exist
442+ func must (err error ) {
443+ if err != nil {
444+ panic (fmt .Errorf ("error initialising: %w" , err ))
445+ }
446+ }
0 commit comments