Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ bin/

# direnv
.envrc
.buildtemp
.buildtemp

# compiled binary
/saml2aws
13 changes: 13 additions & 0 deletions NMD_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@
4. You may then use it as normal `saml2aws`, ensure you preface with
`./saml2aws login`

### Debugging login issues

If authentication fails unexpectedly, use the `--verbose` and `--debug-idp` flags together to diagnose the problem:

```bash
./saml2aws --verbose login --debug-idp
```

- `--verbose` enables debug-level logging, showing HTTP requests/responses, page headings, and form actions from the IDP.
- `--debug-idp` writes the full IDP response HTML to a temp file (path printed in the output) so you can open it in a browser and see exactly what page the IDP returned.

This is especially useful when Google introduces intermediate pages (password resets, account recovery, new terms of service) that saml2aws doesn't handle automatically.

### Legacy Instructions
5. Find the AWS role and account you want cli creds for.
- Go into console and find account number and role name (IAM should have the arn)
Expand Down
5 changes: 5 additions & 0 deletions cmd/saml2aws/commands/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ func buildIdpAccount(loginFlags *flags.LoginExecFlags) (*cfg.IDPAccount, error)
// update username and hostname if supplied
flags.ApplyFlagOverrides(loginFlags.CommonFlags, account)

// pass through debug flag (not part of CommonFlags)
if loginFlags.DebugIDP {
account.DebugIDP = true
}

err = account.Validate()
if err != nil {
return nil, errors.Wrap(err, "Failed to validate account.")
Expand Down
1 change: 1 addition & 0 deletions cmd/saml2aws/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ func main() {
cmdLogin.Flag("disable-sessions", "Do not use Okta sessions. Uses Okta sessions by default. (env: SAML2AWS_OKTA_DISABLE_SESSIONS)").Envar("SAML2AWS_OKTA_DISABLE_SESSIONS").BoolVar(&commonFlags.DisableSessions)
cmdLogin.Flag("disable-remember-device", "Do not remember Okta MFA device. Remembers MFA device by default. (env: SAML2AWS_OKTA_DISABLE_REMEMBER_DEVICE)").Envar("SAML2AWS_OKTA_DISABLE_REMEMBER_DEVICE").BoolVar(&commonFlags.DisableRememberDevice)
cmdLogin.Flag("try-no-prompt", "Only prompt for credentials if they cannot be read from keyring").BoolVar(&loginFlags.TryNoPrompt)
cmdLogin.Flag("debug-idp", "Dump the IDP response HTML to a temp file when authentication fails. Useful for diagnosing login issues.").BoolVar(&loginFlags.DebugIDP)

// `exec` command and settings
cmdExec := app.Command("exec", "Exec the supplied command with env vars from STS token.")
Expand Down
1 change: 1 addition & 0 deletions pkg/cfg/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type IDPAccount struct {
KCAuthErrorMessage string `ini:"kc_auth_error_message,omitempty"` // used by KeyCloak; hide from user if not set
KCAuthErrorElement string `ini:"kc_auth_error_element,omitempty"` // used by KeyCloak; hide from user if not set
KCBroker string `ini:"kc_broker"` // used by KeyCloak;
DebugIDP bool `ini:"-"` // dump IDP response HTML on auth failure (not persisted)
}

func (ia IDPAccount) String() string {
Expand Down
1 change: 1 addition & 0 deletions pkg/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type LoginExecFlags struct {
ExecProfile string
CredentialProcess bool
TryNoPrompt bool
DebugIDP bool
}

type ConsoleFlags struct {
Expand Down
40 changes: 38 additions & 2 deletions pkg/provider/googleapps/googleapps.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ var logger = logrus.WithField("provider", "googleapps")
type Client struct {
provider.ValidateBase

client *provider.HTTPClient
client *provider.HTTPClient
debugIDP bool
}

// New create a new Google Apps Client
Expand All @@ -42,7 +43,8 @@ func New(idpAccount *cfg.IDPAccount) (*Client, error) {
}

return &Client{
client: client,
client: client,
debugIDP: idpAccount.DebugIDP,
}, nil
}

Expand Down Expand Up @@ -163,10 +165,44 @@ func (kc *Client) Authenticate(loginDetails *creds.LoginDetails) (string, error)

samlAssertion := mustFindInputByName(responseDoc, "SAMLResponse")
if samlAssertion == "" {
// Log targeted debug info when SAML assertion is missing
logger.Debug("No SAMLResponse found in response page")

pageTitle := responseDoc.Find("title").Text()
logger.Debugf("Page title: %q", pageTitle)

responseDoc.Find("h1, h2, h3").Each(func(i int, s *goquery.Selection) {
logger.Debugf("Heading [%s]: %q", goquery.NodeName(s), strings.TrimSpace(s.Text()))
})

responseDoc.Find("form").Each(func(i int, s *goquery.Selection) {
action, _ := s.Attr("action")
id, _ := s.Attr("id")
logger.Debugf("Form id=%q action=%q", id, action)
})

// Dump full response HTML to a temp file when --debug-idp is set
if kc.debugIDP {
htmlBody, dumpErr := responseDoc.Html()
if dumpErr == nil {
tmpFile, tmpErr := os.CreateTemp("", "saml2aws-debug-*.html")
if tmpErr == nil {
tmpFile.WriteString(htmlBody)
tmpFile.Close()
log.Printf("IDP response page written to: %s", tmpFile.Name())
}
}
}

if responseDoc.Selection.Find("#passwordError").Text() != "" {
return "", errors.New("Password error")
}

// Check for forced password change page
if strings.Contains(strings.ToLower(responseDoc.Find("h2").Text()), "create a strong password") {
return "", errors.New("Google is requiring a password change for your account. Please log in at https://accounts.google.com in a browser to set a new password, then try again")
}

if err := isMissing2StepSetup(responseDoc); err != nil {
return "", err
}
Expand Down