github.com/taubyte/tau-cli@v0.1.13-0.20240326000942-487f0d57edfc/prompts/login/token_web.go (about)

     1  package loginPrompts
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"path"
     9  	"strings"
    10  
    11  	"github.com/pterm/pterm"
    12  	http "github.com/taubyte/http"
    13  	basicHttp "github.com/taubyte/http/basic"
    14  	"github.com/taubyte/http/options"
    15  	"github.com/urfave/cli/v2"
    16  )
    17  
    18  type SessionData struct {
    19  	Expiry       float64  `json:"exp"`
    20  	Provider     string   `json:"provider"`
    21  	Repositories []string `json:"repositories"`
    22  	Token        string   `json:"token"`
    23  }
    24  
    25  func extractTokenFromSession(session string) (data SessionData, err error) {
    26  	sessionSplit := strings.Split(session, ".")
    27  	if len(sessionSplit) < 1 {
    28  		err = fmt.Errorf("invalid session: `%s`", session)
    29  		return
    30  	}
    31  
    32  	base64Decoded, err := base64.RawStdEncoding.DecodeString(sessionSplit[1])
    33  	if err != nil {
    34  		err = fmt.Errorf("decoding session `%s` failed with: %s", session, err)
    35  		return
    36  	}
    37  
    38  	err = json.Unmarshal(base64Decoded, &data)
    39  	if err != nil {
    40  		return
    41  	}
    42  
    43  	return
    44  }
    45  
    46  func getTokenConsoleURL(provider string, origin string) string {
    47  	consoleURL := "console.taubyte.com/"
    48  	consoleURL = "https://" + path.Join(consoleURL, fmt.Sprintf("oauth/%s/login", provider))
    49  	return consoleURL + fmt.Sprintf("?origin=%s", origin)
    50  }
    51  
    52  // Token from web gives a link to github with a hook back to here
    53  func TokenFromWeb(ctx *cli.Context, provider string) (token string, err error) {
    54  	tokenCh := make(chan string)
    55  	defer close(tokenCh)
    56  
    57  	errCh := make(chan error)
    58  	defer close(errCh)
    59  
    60  	// Open an http server to listen for the token
    61  	srv, err := basicHttp.New(ctx.Context, options.Listen(":"+githubLoginListenPort))
    62  	if err != nil {
    63  		err = fmt.Errorf(StartingHttpFailedWith, githubLoginListenPort, err)
    64  		return
    65  	}
    66  
    67  	srv.GET(&http.RouteDefinition{
    68  		Path: "/",
    69  		Handler: func(ctx http.Context) (iface interface{}, err error) {
    70  			session := ctx.Request().URL.Query().Get("session")
    71  			if len(session) == 0 {
    72  				errCh <- errors.New(NoSessionProvided)
    73  				return
    74  			}
    75  
    76  			sessionData, err := extractTokenFromSession(session)
    77  			if err != nil {
    78  				errCh <- err
    79  				return
    80  			}
    81  
    82  			// TODO track expiration of token
    83  			tokenCh <- sessionData.Token
    84  
    85  			return SuccessCheckBackAtYourTerminal, nil
    86  		},
    87  	})
    88  
    89  	// Start the http server listen
    90  	srv.Start()
    91  
    92  	origin := fmt.Sprintf("http://127.0.0.1:%s", githubLoginListenPort)
    93  	pterm.Info.Printfln(OpenURLToLogin, provider, getTokenConsoleURL(provider, origin))
    94  
    95  	select {
    96  	case token = <-tokenCh:
    97  		break
    98  	case err = <-errCh:
    99  		break
   100  	}
   101  
   102  	// Stop the http server
   103  	srv.Stop()
   104  	if srv.Error() != nil {
   105  		// Only display error, as we got the token or an error
   106  		pterm.Warning.Printfln(ShuttingDownHttpFailedWith, githubLoginListenPort, srv.Error())
   107  	}
   108  
   109  	return
   110  }