Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Example of using OAuth authentication with JIRA in Go
/*
To the extent possible under law, Konstantin Olkhovskiy has waived
all copyright and related or neighboring rights to this snippet.
CC0 license: http://creativecommons.org/publicdomain/zero/1.0/
*/
package main
import (
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"net/http"
"net/url"
"os"
"os/user"
"path/filepath"
"strings"
"log"
"github.com/andygrunwald/go-jira"
"github.com/dghubble/oauth1"
"golang.org/x/net/context"
"gopkg.in/alecthomas/kingpin.v2"
)
var (
jiraURL = kingpin.Flag("jira-url", "JIRA instance to use").Default("<your jira here>").URL()
)
/*
$ openssl genrsa -out jira.pem 1024
$ openssl rsa -in jira.pem -pubout -out jira.pub
*/
const jiraPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
<your private key here>
-----END RSA PRIVATE KEY-----`
const jiraConsumerKey = '<your consumer key>'
func getJIRAHTTPClient(ctx context.Context, config *oauth1.Config) *http.Client {
cacheFile, err := jiraTokenCacheFile()
if err != nil {
log.Fatalf("Unable to get path to cached credential file. %v", err)
}
tok, err := jiraTokenFromFile(cacheFile)
if err != nil {
tok = getJIRATokenFromWeb(config)
saveJIRAToken(cacheFile, tok)
}
return config.Client(ctx, tok)
}
func getJIRATokenFromWeb(config *oauth1.Config) *oauth1.Token {
requestToken, requestSecret, err := config.RequestToken()
if err != nil {
log.Fatalf("Unable to get request token. %v", err)
}
authorizationURL, err := config.AuthorizationURL(requestToken)
if err != nil {
log.Fatalf("Unable to get authorization url. %v", err)
}
fmt.Printf("Go to the following link in your browser then type the "+
"authorization code: \n%v\n", authorizationURL.String())
var code string
if _, err := fmt.Scan(&code); err != nil {
log.Fatalf("Unable to read authorization code. %v", err)
}
accessToken, accessSecret, err := config.AccessToken(requestToken, requestSecret, code)
if err != nil {
log.Fatalf("Unable to get access token. %v", err)
}
return oauth1.NewToken(accessToken, accessSecret)
}
func jiraTokenCacheFile() (string, error) {
usr, err := user.Current()
if err != nil {
return "", err
}
tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
os.MkdirAll(tokenCacheDir, 0700)
return filepath.Join(tokenCacheDir,
url.QueryEscape((*jiraURL).Host+".json")), err
}
func jiraTokenFromFile(file string) (*oauth1.Token, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
t := &oauth1.Token{}
err = json.NewDecoder(f).Decode(t)
defer f.Close()
return t, err
}
func saveJIRAToken(file string, token *oauth1.Token) {
fmt.Printf("Saving credential file to: %s\n", file)
f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Fatalf("Unable to cache oauth token: %v", err)
}
defer f.Close()
json.NewEncoder(f).Encode(token)
}
func getJIRAClient() *jira.Client {
ctx := context.Background()
keyDERBlock, _ := pem.Decode([]byte(jiraPrivateKey))
if keyDERBlock == nil {
log.Fatal("unable to decode key PEM block")
}
if !(keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY")) {
log.Fatalf("unexpected key DER block type: %s", keyDERBlock.Type)
}
privateKey, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes)
if err != nil {
log.Fatalf("unable to parse PKCS1 private key. %v", err)
}
config := oauth1.Config{
ConsumerKey: jiraConsumerKey,
CallbackURL: "oob", /* for command line usage */
Endpoint: oauth1.Endpoint{
RequestTokenURL: (*jiraURL).String() + "plugins/servlet/oauth/request-token",
AuthorizeURL: (*jiraURL).String() + "plugins/servlet/oauth/authorize",
AccessTokenURL: (*jiraURL).String() + "plugins/servlet/oauth/access-token",
},
Signer: &oauth1.RSASigner{
PrivateKey: privateKey,
},
}
jiraClient, err := jira.NewClient(getJIRAHTTPClient(ctx, &config), (*jiraURL).String())
if err != nil {
log.Fatalf("unable to create new JIRA client. %v", err)
}
return jiraClient
}
@MindHunter86

This comment has been minimized.

Copy link

@MindHunter86 MindHunter86 commented May 23, 2018

godevel@gofedora jira-test $ go build main.go                                                                                          
# command-line-arguments
./main.go:50: undefined: log in log.Fatalf
./main.go:63: undefined: log in log.Fatalf
./main.go:67: undefined: log in log.Fatalf
./main.go:74: undefined: log in log.Fatalf
./main.go:79: undefined: log in log.Fatalf
./main.go:110: undefined: log in log.Fatalf
./main.go:120: undefined: log in log.Fatal
./main.go:123: undefined: log in log.Fatalf
./main.go:128: undefined: log in log.Fatalf
./main.go:144: undefined: log
./main.go:144: too many errors

Add "log" in imports %)

@Lupus

This comment has been minimized.

Copy link
Owner Author

@Lupus Lupus commented Mar 10, 2019

Add "log" in imports %)

Wow, added, thanks! :)

@alext234

This comment has been minimized.

Copy link

@alext234 alext234 commented Jun 21, 2019

Hi just wondering from where we can get the consumer key?

@Lupus

This comment has been minimized.

Copy link
Owner Author

@Lupus Lupus commented Jun 21, 2019

Hey @alext234, to be honest I don't remember the source of this key, I've played around with Jira API quite long time ago 😞

I recall I was following instructions on the internet on how to register my app in Jira admin UI, and I'm inclined to say that consumer key is assigned by Jira when you create an app. The one I have in my old codebase is a combination of 30 random symbols from [a-zA-Z0-9] alphabet.

@shdunning

This comment has been minimized.

Copy link

@shdunning shdunning commented Aug 28, 2019

the consumer key is part of the application link created to link the external app to jira:
Image 2019-08-28 at 1 26 57 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment