June 22, 2023

AutoHotkey Basic Authentification for REST API e.g. Jira

I have stumbled upon an issue in the b64 encoding function that broke the Basic Authentication when using a longer password e.g. API token.
I share in this post how I do REST API calls with a Basic Authentication example for Jira including the fix for this issue.

b64 Encoding

For a Basic Authentication you need to encode as binary64 the string passed in the Request Header. 

I had a bug in the encoding function I was using. 
(This was triggering an error "setRequestHeader 0x80070057 - The parameter is incorrect." because the encoded string contained line breaks)
I have found the fix in this blog forum Base64 encoder/decoder for Binary data - AutoHotkey Community

The fix is also available in my repo here: https://github.com/tdalon/ahk/blob/main/Lib/b64Encode.ahk 

Basic Auth for Jira

I use to configure the API token for the Jira authentication in an INI File with following Json structure:

[Jira]

JiraAuth=[{"url":"<YourJiraUrl>","username":"<YourJiraEmail>","apitoken":"<YourApitoken>"}]

The function to create the Basic Authentication String looks like this:
Jira_BasicAuth(sUrl:="",sToken:="") {
; Get Auth String for basic authentification
; sAuth := Jira_BasicAuth(sUrl:="",sToken:="")
; Default Url is Setting JiraRootUrl
; Calls: b64Encode
If !RegExMatch(sUrl,"^http") ; missing root url or default url
sUrl := Jira_GetRootUrl() . sUrl
If (sToken = "") {
; If Cloud
If InStr(sUrl,".atlassian.net") {
; Read Jira PowerTools.ini setting
If !FileExist("PowerTools.ini") {
PowerTools_ErrDlg("No PowerTools.ini file found!")
return
}
IniRead, JiraAuth, PowerTools.ini,Jira,JiraAuth
If (JiraAuth="ERROR") { ; Section [Jira] Key JiraAuth not found
PowerTools_ErrDlg("JiraAuth key not found in PowerTools.ini file [Jira] section!")
return
}
JsonObj := Jxon_Load(JiraAuth)
For i, inst in JsonObj
{
url := inst["url"]
If InStr(sUrl,url) {
JiraToken := inst["apitoken"]
If (JiraToken="") { ; Section [Jira] Key JiraAuth not found
PowerTools_ErrDlg("ApiToken is not defined in PowerTools.ini file [Jira] section, JiraAuth key for url '" . url . "'!")
return
}
JiraUserName := inst["username"]
break
}
}
If (JiraToken="") { ; Section [Jira] Key JiraAuth not found
RegExMatch(sUrl,"https?://[^/]*",sRootUrl)
PowerTools_ErrDlg("No instance defined in PowerTools.ini file [Jira] section, JiraAuth key for url '" . sRootUrl . "'!")
return
}
} Else { ; server
JiraToken := Jira_GetPassword()
If (JiraToken="") ; cancel
return
}
}
If (JiraUserName ="")
JiraUserName := Jira_GetUserName()
If (JiraUserName ="")
return
sAuth := b64Encode( JiraUserName . ":" . JiraToken)
return sAuth
} ; eofun

API Requests

Doing REST API Calls can be then done as shown in following functions:
; ----------------------------------------------------------------------
Jira_Get(sUrl,sPassword:=""){
; Syntax: sResponseText := Jira_Get(sUrl,sPassword*)
; sPassword Password for server or Api Token for cloud
; Calls: Jira_BasicAuth
If !RegExMatch(sUrl,"^http") ; missing root url or default url
sUrl := Jira_GetRootUrl() . sUrl
sAuth := Jira_BasicAuth(sUrl,sPassword)
Clipboard := sAuth
If (sAuth="")
return
WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1")
WebRequest.Open("GET", sUrl, false) ; Async=false
sAuth := "Basic " . sAuth
WebRequest.setRequestHeader("Authorization", sAuth)
WebRequest.setRequestHeader("Content-Type", "application/json")
WebRequest.Send()
return WebRequest.responseText
}
; ----------------------------------------------------------------------
Jira_Post(sUrl,sBody:="",sPassword:=""){
; Syntax: sResponseText := Jira_Post(sUrl,sBody,sPassword*)
; Calls: Jira_BasicAuth
If !RegExMatch(sUrl,"^http") ; missing root url or default url
sUrl := Jira_GetRootUrl() . sUrl
sAuth := Jira_BasicAuth(sUrl,sPassword)
If (sAuth="")
return
WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1")
WebRequest.Open("POST", sUrl, false) ; Async=false
WebRequest.setRequestHeader("Authorization", "Basic " . sAuth)
WebRequest.setRequestHeader("Content-Type", "application/json")
If (sBody = "")
WebRequest.Send()
Else
WebRequest.Send(sBody)
WebRequest.WaitForResponse()
return WebRequest.responseText
}
; ----------------------------------------------------------------------
Jira_WebRequest(sReqType,sUrl,sBody:="",sPassword:=""){
; Syntax: WebRequest := Jira_WebRequest(sReqType,sUrl,sBody:="",sPassword:="")
; Output WebRequest with fields Status and ResponseText
; Calls: Jira_BasicAuth
If !RegExMatch(sUrl,"^http") ; missing root url or default url
sUrl := Jira_GetRootUrl() . sUrl
sAuth := Jira_BasicAuth(sUrl,sPassword)
If (sAuth="")
return
WebRequest := ComObjCreate("WinHttp.WinHttpRequest.5.1")
WebRequest.Open(sReqType, sUrl, false) ; Async=false
WebRequest.setRequestHeader("Authorization", "Basic " . sAuth)
WebRequest.setRequestHeader("Content-Type", "application/json")
If (sBody = "")
WebRequest.Send()
Else
WebRequest.Send(sBody)
WebRequest.WaitForResponse()
return WebRequest
}
view raw Jira.ahk hosted with ❤ by GitHub

See also

Base64 encoder/decoder for Binary data - AutoHotkey Community