Skip to content

feat: SSO support for Argo Workflows CLI #14471

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

dyatlov
Copy link

@dyatlov dyatlov commented May 15, 2025

Support authentication in Argo Workflows CLI via SSO and store / reuse the token in further CLI calls.

The PR also adds support for file-based configuration via Viper.

New command is as follows:

$ argo auth sso

You can also optionally pass --sso-port variable for a custom local server port.

Help for the cmd:

$ argo auth sso --help
Authenticate with SSO

Usage:
  argo auth sso [flags]

Flags:
  -h, --help       help for sso
      --sso-port int   Port to listen for the callback (default 8085)

A new global CLI option --config is added to load config from a custom file location. If omited - we use $HOME/.argo/config.yaml as the default config file location.

Motivation

In our company we have hundreds of users of Argo Workflows.
From time to time users use argo cli to test / update / trigger workflows simply because that's it's handier than UI and because you can script it however you need with predefined params.

The only downside to the approach is that users had to copy ARGO_TOKEN from workflows UI, manually store it somewhere then periodically update etc..
If we compare the workflow to ArgoCD CLI - we can clearly see advantages of using SSO within that CLI and automatically storing tokens.

This PR attempts to bring this functionality on par with argocd cli.

ArgoCD and Argo Workflows use slightly different mechanics underneath when it comes to SSO:

  • ArgoCD cli talks directly to dex and then uses the obtained token for ArgoCD requests
  • Argo Workflows further modifies dex token and wraps it into a custom encrypted JWT so our CLI needs to communicate with Argo Workflows in order to obtain the correct token.

CLI Flow

sequenceDiagram
CLI->>Argo Server: GET /oauth2/redirect?redirect=<localhost>&state=<state>
Argo Server->>Cookie: stores <redirect url>, cli=true, cli_state=<state>
Argo Server->>DEX: redirects to server-side SSO flow
DEX-->>Argo Server: redirects back to /oauth2/callback with a token
Argo Server->>Cookie: parse cookie and generate temp <JWT>
Argo Server-->>CLI: redirect to <localhost> with <JWT>
CLI->>Argo Server: POST /oauth2/cli/exchange with <JWT> and <state>
Argo Server-->>CLI: validates and return long-lived token
Loading

Modifications

In order to support CLI SSO Flow, I customized the state cookie within oauth flow to include besides redirect url some additional information:

  • cli flag to indicate that this is a CLI flow
  • cli_state variable to include state from CLI into temporary JWT token (jti) and validate it during JWT token exchange phase.

The above means that we need to encode the data to store it in a cookie.
For the purpose I encoded the data into json and then further encoded it into base64 to encode invalid cookie characters.

When we are in the CLI flow within /oauth2/callback handler - then instead of storing auth token within Cookies
CLI then calls POST /oauth2/cli/exchange with the code and cli state in json body to exchange the code (temporary cli jwt) for authentication token (actual long-living jwt).
The token is then stored within $HOME/.argo/config.yaml file.

On server side to split token types I used JWT Audiences.

  • The temporary JWT token is issued with cli-auth audience and is not valid for anything besides cli exchange flow (the token is valid for just 10 seconds to minimise any potential security risks).
  • Long-living token is issued with argo-session audience and is used for normal operations (beforehand audience wasn't set at all).

In order to support token preloading from config file I utilised viper.ReadInConfig() method.
Since you already using viper in your command parsing code it was logic choice.
I also moved viper configuration and flags override into cobra.OnInitialize() method since that is the correct way to update flags only after they were fully parsed.

And lastly, I allowed localhost in redirect value for /oauth2/redirect handler as otherwise we can't redirect to a local CLI server.

➜  argo-workflows git:(cli-sso-support) ls -l ~/.argo/
ls: /Users/vitali.deatlov/.argo/: No such file or directory
➜  argo-workflows git:(cli-sso-support) dist/argo auth sso -s https://localhost:8080
Opening browser for SSO login: https://localhost:8080/oauth2/redirect?redirect=http%3A%2F%2Flocalhost%3A8085%2Foauth%2Fcallback&cli=true&cli_state=b8b649353f
Listening on localhost:8085 for OAuth2 callback...
🔐 Received SSO callback
Code: eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwiemlwIjoiREVGIn0.NioWA9PXj3VY856d1CtNOqDFbKMSXuvUlxHVjXYWX6QWC5M_P7-mgzMV-AKQRe5FmXT_QmpNQOPEVBhB_MB4t_v14nSyd-4yE66kDgYrmfmLKeeLueByvPfgIiDf17-H0PAYFPoRfVVXJHXdneBBx3WaCwaaNPyCQrfVggW828V5YRcNVwZi2loz0k2iRxBe80a1vPFdCctWffNWlOsYjdU34Rf8L8VNHGLCp5xLhnb7S3t4YqozHnARQ-rU0RCmNpRnYK7gyvdhklNatBQGF5fAZebgLZduMMNXodIfiYCP0ncJX79MJgpY-FbILUuB8LscBl-JdRLVZueL4wmzEg.G3tBxgwnMAlzEVb9.0xFCrRwoq1rdQ_YQf8Alqoy3P4nn8RquzcaM66E_FJZW74zgxOdzR3Tn6reVs6-r-vMtUbIymZX_8-mRiCGnO_lkth64bg3hc_tcEXmATxx1HIaPmnJfoq7fq7Woe9iV-Z8Q7R7fyadkP91dMuTZpM3u6lowq0onFhDnF5OmDxQxfpCDGCy-tv2g41SE4dTVy3cZe4IxgNHudspSduSFu7Cj.avZ6q4uedktWaSKPGB_jKA
✅ Code exchanged and saved successfully
✅ Authentication successful
➜  argo-workflows git:(cli-sso-support) cat ~/.argo/config.yaml
token: Bearer v2:eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwiemlwIjoiREVGIn0.e-X_aYwYg1J01CQJF964fkDA9DhEpy0v_hPoGSOb0SRnFXh6c6SsDL2a7TScvQDHixCZOUkmyU_gsZU5zGXqjQ5b7Y86MwBhqmbWqBSljm1JDeSw4MOacqHlweqM41xwT0aEXpYzSKLdE_dhNv5d9qtVLZL7o2AJLEZKIu9LHr2fc-XgOs9uktLEC8QfPshd7fgTV-uz2ROTlOYRNoL7TdankNQEVlTgA6lHt0v-cOZ3cFAxfj8QuHtZmA0TPoDixudNdPq1QYac6RazevCRpIrsfRgtEDKHp6m8Pxm6p_bD_GBo4hdJn0nWGFbkkZ3WVgvUhzau3mUB6UQj-h-UMw.1JeCcTijyIsWUX-B.meAvrK564bY3j4VHuHrWaVML7XUJa6rt5wWJ6VWs70THpzvD5Tgh_3ZPzBUv684NvQaf04iOmXJJG060vusZTEbY0xH6YZQeK-p4qxFHoIJ9JHaRUJHFFfIBQ_lInoDGnVmemQ7RiCE6z9USCAEXJmEkTrRVmvIUnuyVPvgG8IlChNI32JEs.BoyBGsFBSWMgEJlV436z4Q
➜  argo-workflows git:(cli-sso-support) dist/argo auth token
Bearer v2:eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwiemlwIjoiREVGIn0.e-X_aYwYg1J01CQJF964fkDA9DhEpy0v_hPoGSOb0SRnFXh6c6SsDL2a7TScvQDHixCZOUkmyU_gsZU5zGXqjQ5b7Y86MwBhqmbWqBSljm1JDeSw4MOacqHlweqM41xwT0aEXpYzSKLdE_dhNv5d9qtVLZL7o2AJLEZKIu9LHr2fc-XgOs9uktLEC8QfPshd7fgTV-uz2ROTlOYRNoL7TdankNQEVlTgA6lHt0v-cOZ3cFAxfj8QuHtZmA0TPoDixudNdPq1QYac6RazevCRpIrsfRgtEDKHp6m8Pxm6p_bD_GBo4hdJn0nWGFbkkZ3WVgvUhzau3mUB6UQj-h-UMw.1JeCcTijyIsWUX-B.meAvrK564bY3j4VHuHrWaVML7XUJa6rt5wWJ6VWs70THpzvD5Tgh_3ZPzBUv684NvQaf04iOmXJJG060vusZTEbY0xH6YZQeK-p4qxFHoIJ9JHaRUJHFFfIBQ_lInoDGnVmemQ7RiCE6z9USCAEXJmEkTrRVmvIUnuyVPvgG8IlChNI32JEs.BoyBGsFBSWMgEJlV436z4Q

Screenshot 2025-05-20 at 16 16 04

Screenshot 2025-05-20 at 16 16 13

Screenshot 2025-05-20 at 16 16 21

Verification

Changes were successfully tested locally using Dev Containers.

Documentation

Documentation was updated automatically when I ran make pre-commit -B
I find the generated documentation sufficient.
Users can discover the new feature simply by using argo --help or argo auth --help.

@dyatlov dyatlov force-pushed the cli-sso-support branch 7 times, most recently from 9dc5f7f to ea696df Compare May 16, 2025 15:46
@dyatlov dyatlov marked this pull request as ready for review May 16, 2025 16:18
@dyatlov
Copy link
Author

dyatlov commented May 16, 2025

Some tests have failed but I dont see how it's connected to my changes

@dyatlov dyatlov force-pushed the cli-sso-support branch 3 times, most recently from 070153a to 6098928 Compare May 20, 2025 13:42
Signed-off-by: Vitali Deatlov <vitali.deatlov@mongodb.com>
@dyatlov dyatlov force-pushed the cli-sso-support branch from 6098928 to 64b7251 Compare May 20, 2025 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant