Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8689fd4
(build) Update Golang OAuth2 Library to latest
christianarty Jul 8, 2025
da912e5
(feat) Implement OAuth2 func. into jira-cli
christianarty Jul 5, 2025
56ab800
feat:(oauth) - Ability to get cloud id for clients
christianarty Jul 9, 2025
fd69ab0
(docs) Add note about OAuth weirdness
christianarty Jul 12, 2025
c17a87e
(refactor) Move oauth to separate module
christianarty Jul 12, 2025
c4e6ffb
(refactor) Remove unnecessary args from `verifyLoginDetails` and `con…
christianarty Jul 12, 2025
66b3890
(tests): Add tests for oauth
christianarty Jul 12, 2025
4a16853
(nit) Move getToken logic into separate func
christianarty Jul 13, 2025
57f34c8
(refactor) Organizing the oauth to break up the logic to other files
christianarty Jul 15, 2025
4950c0f
(feat) Add the ability to load the persisted oauth token from filesystem
christianarty Jul 15, 2025
3213b62
(test) More tests for oauth
christianarty Jul 15, 2025
0b13694
(feat) Enable auto refreshing access tokens through the oauth2 custom…
christianarty Jul 15, 2025
2d94903
(nit) remove dead code
christianarty Jul 15, 2025
fd85490
(fix) The constructed server urls were pointing to authorization serv…
christianarty Jul 17, 2025
5dbd309
(docs) update README with more instructions.
christianarty Jul 17, 2025
e9117ee
(fix/cleanup) Remove old/redundant code and fix the oauth tests
christianarty Jul 17, 2025
b55ca77
(nit) Remove old TODO
christianarty Jul 17, 2025
d395fe6
(lint) fix the lint issues
christianarty Jul 17, 2025
a1f4970
(ci/fix) Fix tests to allow for ci to pass quality checks
christianarty Jul 17, 2025
c3c4f6b
(ci) Fix some more issues found with DeepSource
christianarty Jul 17, 2025
0ebaa17
(docs) update README to account for discussion post
christianarty Jul 19, 2025
2ca15a5
(nit) add additional scopes for properly reading jira sprint
christianarty Oct 8, 2025
030adc0
need this scope for adding to a sprint
christianarty Oct 8, 2025
0708a1c
typos
christianarty Oct 13, 2025
3cd2c32
address the searching for epic details
christianarty Oct 13, 2025
d666c89
fix reason why the cloud isn't working
christianarty Oct 13, 2025
25cd230
linting
christianarty Oct 13, 2025
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
97 changes: 83 additions & 14 deletions README.md

Large diffs are not rendered by default.

83 changes: 69 additions & 14 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,75 @@ import (
"github.com/spf13/viper"
"github.com/zalando/go-keyring"

"github.com/ankitpokhrel/jira-cli/internal/cmdutil"
"github.com/ankitpokhrel/jira-cli/pkg/jira"
"github.com/ankitpokhrel/jira-cli/pkg/jira/filter"
"github.com/ankitpokhrel/jira-cli/pkg/netrc"
"github.com/ankitpokhrel/jira-cli/pkg/oauth"
)

const clientTimeout = 15 * time.Second

var jiraClient *jira.Client

// getAPIToken retrieves the API token from various sources in order of priority:
// 1. Viper configuration
// 2. OAuth access token (if available and valid)
// 3. Netrc file
// 4. Keyring.
func getAPIToken(config *jira.Config) string {
if config.APIToken != "" {
return config.APIToken
}

// Try viper config first
if token := viper.GetString("api_token"); token != "" {
return token
}

// Try OAuth access token if available and valid
// And should only do this assertion if the AuthType is oauth
isAuthTypeOAuth := config.AuthType != nil && *config.AuthType == jira.AuthTypeOAuth
if isAuthTypeOAuth && oauth.HasOAuthCredentials() {
tk, _ := oauth.LoadOAuth2TokenSource()
token, _ := tk.Token()
return token.AccessToken
}

// Try netrc file
if netrcConfig, _ := netrc.Read(config.Server, config.Login); netrcConfig != nil {
if netrcConfig.Password != "" {
return netrcConfig.Password
}
}

// Try keyring
if secret, _ := keyring.Get("jira-cli", config.Login); secret != "" {
return secret
}

return ""
}

// Client initializes and returns jira client.
func Client(config jira.Config) *jira.Client {
if jiraClient != nil {
return jiraClient
}

if config.Server == "" {
config.Server = viper.GetString("server")
apiServer := viper.GetString("api_server")
if apiServer != "" {
config.Server = apiServer
} else {
// Fallback to server URL if api_server is not set
cmdutil.Warn("api_server key is not set, falling back to server URL")
config.Server = viper.GetString("server")
}
}
if config.Login == "" {
config.Login = viper.GetString("login")
}
if config.APIToken == "" {
config.APIToken = viper.GetString("api_token")
}
if config.APIToken == "" {
netrcConfig, _ := netrc.Read(config.Server, config.Login)
if netrcConfig != nil {
config.APIToken = netrcConfig.Password
}
}
if config.APIToken == "" {
secret, _ := keyring.Get("jira-cli", config.Login)
config.APIToken = secret
}
if config.AuthType == nil {
authType := jira.AuthType(viper.GetString("auth_type"))
config.AuthType = &authType
Expand All @@ -49,6 +84,26 @@ func Client(config jira.Config) *jira.Client {
config.Insecure = &insecure
}

// Check if we have OAuth credentials and should use OAuth
if oauth.HasOAuthCredentials() && config.AuthType != nil && *config.AuthType == jira.AuthTypeOAuth {
// Try to create OAuth2 token source
tokenSource, err := oauth.LoadOAuth2TokenSource()
if err == nil {
// We have valid OAuth credentials, use OAuth authentication
// Pass the TokenSource to the client via a custom option
jiraClient = jira.NewClient(
config,
jira.WithTimeout(clientTimeout),
jira.WithInsecureTLS(*config.Insecure),
jira.WithOAuth2TokenSource(tokenSource),
)
return jiraClient
}
}

// Get API token from various sources (fallback for non-OAuth auth)
config.APIToken = getAPIToken(&config)

// MTLS

if config.MTLSConfig.CaCert == "" {
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ require (
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.10.0
github.com/zalando/go-keyring v0.2.6
golang.org/x/oauth2 v0.30.0
golang.org/x/term v0.30.0
)

Expand All @@ -39,6 +40,7 @@ require (
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/cli/browser v1.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/creack/pty v1.1.18 // indirect
github.com/danieljoos/wincred v1.2.2 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99k
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk=
github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00=
github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
Expand Down Expand Up @@ -187,6 +189,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down
5 changes: 5 additions & 0 deletions internal/cmd/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
jiraConfig "github.com/ankitpokhrel/jira-cli/internal/config"
"github.com/ankitpokhrel/jira-cli/pkg/jira"
"github.com/ankitpokhrel/jira-cli/pkg/netrc"
"github.com/ankitpokhrel/jira-cli/pkg/oauth"

"github.com/zalando/go-keyring"
)
Expand Down Expand Up @@ -156,6 +157,10 @@ func cmdRequireToken(cmd string) bool {
}

func checkForJiraToken(server string, login string) {
if oauth.HasOAuthCredentials() {
return
}

if os.Getenv("JIRA_API_TOKEN") != "" {
return
}
Expand Down
Loading