Skip to content

Commit

Permalink
Merge pull request #447 from catskul/fix-rvl-auth-bearer-token
Browse files Browse the repository at this point in the history
Fix rvl auth bearer token branch
  • Loading branch information
mikepea authored Nov 28, 2021
2 parents a59fdc8 + b3723c7 commit 07bd89a
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 9 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,17 @@ The API Token authentication requires both the token and the email of the user.
If your Jira service still allows you to use the Session based authentication method then `jira` will prompt for a password automatically when get a response header from the Jira service that indicates you do not have an active session (ie the `X-Ausername` header is set to `anonymous`). Then after authentication we cache the `cloud.session.token` cookie returned by the service [session login api](https://docs.atlassian.com/jira/REST/cloud/#auth/1/session-login) and reuse that on subsequent requests. Typically this cookie will be valid for several hours (depending on the service configuration). To automatically securely store your password for easy reuse by jira You can enable a `password-source` via `.jira.d/config.yml` with possible values of `keyring`, `pass` or `gopass`.
Depending on how your private Jira service is configured, API tokens may require the "[Bearer][]" authentication scheme instead of the traditional "[Basic][]" [authentication scheme][scheme]. In this case, set the `authentication-method: bearer-token` property in your `$HOME/.jira.d/config.yml` file.
[scheme]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
[Bearer]: https://datatracker.ietf.org/doc/html/rfc6750
[Basic]: https://tools.ietf.org/html/rfc7617
| **API token [scheme][]** | `authentication-method` | **Example HTTP request header** |
|:------------------------:|-------------------------|-------------------------------------------------|
| [Basic][] | `api-token` | `Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQK` |
| [Bearer][] | `bearer-token` | `Authorization: Bearer MY_TOKEN` |
#### User vs Login
The Jira service has sometimes differing opinions about how a user is identified. In other words the ID you login with might not be ID that the jira system recognized you as. This matters when trying to identify a user via various Jira REST APIs (like issue assignment). This is especially relevant when trying to authenticate with an API Token where the authentication user is usually an email address, but within the Jira system the user is identified by a user name. To accommodate this `jira` now supports two different properties in the config file. So when authentication using the API Tokens you will likely want something like this in your `$HOME/.jira.d/config.yml` file:
```yaml
Expand Down
15 changes: 12 additions & 3 deletions jiracli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ const (
)

type GlobalOptions struct {
// AuthenticationMethod is the method we use to authenticate with the jira serivce. Possible values are "api-token" or "session".
// AuthenticationMethod is the method we use to authenticate with the jira serivce.
// Possible values are "api-token", "bearer-token" or "session".
// The default is "api-token" when the service endpoint ends with "atlassian.net", otherwise it "session". Session authentication
// will promt for user password and use the /auth/1/session-login endpoint.
AuthenticationMethod figtree.StringOption `yaml:"authentication-method,omitempty" json:"authentication-method,omitempty"`
Expand Down Expand Up @@ -154,6 +155,10 @@ func (o *GlobalOptions) AuthMethod() string {
return o.AuthenticationMethod.Value
}

func (o *GlobalOptions) AuthMethodIsToken() bool{
return o.AuthMethod() == "api-token" || o.AuthMethod() == "bearer-token";
}

func register(app *kingpin.Application, o *oreo.Client, fig *figtree.FigTree) {
globals := GlobalOptions{
User: figtree.NewStringOption(os.Getenv("USER")),
Expand All @@ -173,6 +178,10 @@ func register(app *kingpin.Application, o *oreo.Client, fig *figtree.FigTree) {
token := globals.GetPass()
authHeader := fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", globals.Login.Value, token))))
req.Header.Add("Authorization", authHeader)
} else if globals.AuthMethod() == "bearer-token" {
token := globals.GetPass()
authHeader := fmt.Sprintf("Bearer %s", token)
req.Header.Add("Authorization", authHeader)
}
return req, nil
})
Expand All @@ -194,7 +203,7 @@ func register(app *kingpin.Application, o *oreo.Client, fig *figtree.FigTree) {
// rerun the original request
return o.Do(req)
}
} else if globals.AuthMethod() == "api-token" && resp.StatusCode == 401 {
} else if globals.AuthMethodIsToken() && resp.StatusCode == 401 {
globals.SetPass("")
return o.Do(req)
}
Expand Down Expand Up @@ -232,7 +241,7 @@ func register(app *kingpin.Application, o *oreo.Client, fig *figtree.FigTree) {
} else if globals.SocksProxy.Value != "" {
o = o.WithTransport(socksProxy(globals.SocksProxy.Value))
}
if globals.AuthMethod() == "api-token" {
if globals.AuthMethodIsToken() {
o = o.WithCookieFile("")
}
if globals.Login.Value == "" {
Expand Down
6 changes: 3 additions & 3 deletions jiracli/password.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func (o *GlobalOptions) ProvideAuthParams() *jiradata.AuthParams {

func (o *GlobalOptions) keyName() string {
user := o.Login.Value
if o.AuthMethod() == "api-token" {
if o.AuthMethodIsToken() {
user = "api-token:" + user
}

Expand Down Expand Up @@ -133,14 +133,14 @@ func (o *GlobalOptions) GetPass() string {
return o.cachedPassword
}

if o.cachedPassword = os.Getenv("JIRA_API_TOKEN"); o.cachedPassword != "" && o.AuthMethod() == "api-token" {
if o.cachedPassword = os.Getenv("JIRA_API_TOKEN"); o.cachedPassword != "" && o.AuthMethodIsToken() {
return o.cachedPassword
}

prompt := fmt.Sprintf("Jira Password [%s]: ", o.Login)
help := ""

if o.AuthMethod() == "api-token" {
if o.AuthMethodIsToken() {
prompt = fmt.Sprintf("Jira API-Token [%s]: ", o.Login)
help = "API Tokens may be required by your Jira service endpoint: https://developer.atlassian.com/cloud/jira/platform/deprecation-notice-basic-auth-and-cookie-based-auth/"
}
Expand Down
4 changes: 4 additions & 0 deletions jiracmd/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ func CmdLogin(o *oreo.Client, globals *jiracli.GlobalOptions, opts *jiracli.Comm
log.Noticef("No need to login when using api-token authentication method")
return nil
}
if globals.AuthMethod() == "bearer-token" {
log.Noticef("No need to login when using bearer-token authentication method")
return nil
}

ua := o.WithoutRedirect().WithRetries(0).WithoutCallbacks().WithPostCallback(authCallback)
for {
Expand Down
6 changes: 3 additions & 3 deletions jiracmd/logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ func CmdLogoutRegistry() *jiracli.CommandRegistryEntry {

// CmdLogout will attempt to terminate an active Jira session
func CmdLogout(o *oreo.Client, globals *jiracli.GlobalOptions, opts *jiracli.CommonOptions) error {
if globals.AuthMethod() == "api-token" {
log.Noticef("No need to logout when using api-token authentication method")
if globals.AuthMethodIsToken() {
log.Noticef("No need to logout when using api-token or bearer-token authentication method")
if globals.GetPass() != "" && terminal.IsTerminal(int(os.Stdin.Fd())) && terminal.IsTerminal(int(os.Stdout.Fd())) {
delete := false
err := survey.AskOne(
&survey.Confirm{
Message: fmt.Sprintf("Delete api-token from password provider [%s]: ", globals.PasswordSource),
Message: fmt.Sprintf("Delete token from password provider [%s]: ", globals.PasswordSource),
Default: false,
},
&delete,
Expand Down

0 comments on commit 07bd89a

Please sign in to comment.