Search code examples
goaws-sdk-go

Go: bufio.NewScanner works on MacOS but is skipped on Windows


I have this piece of go code, mostly taken from here:

fmt.Println("Please enter your role: ")
fmt.Scanf("%s", &roleName)

flag.StringVar(&startURL, "start-url", "", "AWS SSO Start URL")
flag.StringVar(&accountID, "account-id", "", "AWS Account ID to fetch credentials for")
flag.Parse()
if startURL == "" || accountID == "" || roleName == "" {
    flag.Usage()
    os.Exit(1)
}

cfg := aws.Config{Region: "eu-west-1"}

// create sso oidc client to trigger login flow
ssooidcClient := ssooidc.NewFromConfig(cfg)

// register your client which is triggering the login flow
register, err := ssooidcClient.RegisterClient(context.TODO(), &ssooidc.RegisterClientInput{
    ClientName: aws.String("sample-client-name"),
    ClientType: aws.String("public"),
    Scopes:     []string{"sso-portal:*"},
})
if err != nil {
    fmt.Println(err)
}
// authorize your device using the client registration response
deviceAuth, err := ssooidcClient.StartDeviceAuthorization(context.TODO(), &ssooidc.StartDeviceAuthorizationInput{
    ClientId:     register.ClientId,
    ClientSecret: register.ClientSecret,
    StartUrl:     aws.String(startURL),
})
if err != nil {
    fmt.Println(err)
}
// trigger OIDC login. open browser to login. close tab once login is done. press enter to continue
url := aws.ToString(deviceAuth.VerificationUriComplete)
fmt.Printf("If browser is not opened automatically, please open link:\n%v\n", url)
err = browser.OpenURL(url)
if err != nil {
    fmt.Println(err)
}

fmt.Println("Press ENTER key once login is done")
// These lines get skipped on Windows
// also tried bufio.NewReader(os.Stdin).ReadBytes('\n')
// and fmt.Scanf("%s", &test)
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
if scanner.Err() != nil {
    fmt.Println()
}

token, err := ssooidcClient.CreateToken(context.TODO(), &ssooidc.CreateTokenInput{
    ClientId:     register.ClientId,
    ClientSecret: register.ClientSecret,
    DeviceCode:   deviceAuth.DeviceCode,
    GrantType:    aws.String("urn:ietf:params:oauth:grant-type:device_code"),
})

While on MacOS, the program waits for user input and hence the SSO login works perfectly, on Windows it gets skipped and the user does not have time to accept the login on AWS side, so the program fails. Moreover, the first prompt that asks for the users' role works correctly, so I really don't understand the second one just gets skipped ? I use these commands to build the binary, from a MacOS machine:

GOOS=darwin go build
GOOS=windows go build

Solution

  • The call fmt.Scanf("%s", &roleName) returns after reading the first whitespace character after the token.

    The line terminator on Windows is \r\n. The fmt.Scanf call returns after reading the \r. The \n remains in stdin. The later call to scanner.Scan() reads the remaining \n in stdin and returns immediately.

    The line terminator on other systems is \n. The call fmt.Scanf returns after reading the entire line terminator. The call to scanner.Scan() waits for the user to type another line terminator.

    One fix is to use the scanner for all input:

    scanner := bufio.NewScanner(os.Stdin)
    fmt.Println("Please enter your role: ")
    if !scanner.Scan() {
       // handle EOF
    }
    roleName = strings.TrimSpace(scanner.Text())
    …
    fmt.Println("Press ENTER key once login is done")
    scanner.Scan()
    …