I am using Golang to work with the Box API. However, I am trying to generate access tokens through the method of using JWT to eliminate the need for user interaction beyond initial setup. However, after creating a JWT and trying to send it in an oAuth request, I am failing to retrieve an access token. The following to functions create a JWT to use in an oAuth request to gain an access token. However, the response body is empty and the HTTP request generates a 400 (Bad Request) error. After the functions I show the result of printing out the resp.Status and resp.Body
// JWTAUTHURL - URL for oAUTH for Box
const JWTAUTHURL string = "https://api.box.com/oauth2/token"
// JWTGRANTTYPE - mandatory parameter for box oAuth
const JWTGRANTTYPE string = "urn:ietf:params:oauth:grant-type:jwt-bearer"
// oAuthResponse holds decoded JSON response from Box
type oAuthResponse struct {
AccessToken string `json:"access_token"`
Expires int `json:"expires_in"`
RestrictedTo []string `json:"restricted_to"`
TokenType string `json:"token_type"`
}
// CreateJWTAssertion - build up the JSON Web Token for oAuth
func CreateJWTAssertion(PublicKeyID string, ClientID string, Sub string, user *AppUserResponse) (string, error) {
var signingKey []byte
var err error
var msg, tokenString string
signingKey, err = ioutil.ReadFile("private_key.pem")
if err != nil {
msg = "Unable to read signing key. Please ensure your private signing key is in the ./keys/ directory"
return msg, err
}
// Generate JTI Value
jti, err := exec.Command("uuidgen").Output()
if err != nil {
msg = "Unable to generate JTI value"
return msg, err
}
token := jwt.New(jwt.SigningMethodHS256)
claims := make(jwt.MapClaims)
token.Header["alg"] = "HS256"
token.Header["typ"] = "JWT"
token.Header["kid"] = PublicKeyID
claims["iss"] = ClientID
claims["aud"] = JWTAUTHURL
claims["jti"] = jti
claims["exp"] = time.Now().Add(time.Second * 45).Unix()
// Create the JWT either for the enterprise or for a specific user.
if user.ID != "" {
claims["box_sub_type"] = "user"
claims["sub"] = user.ID
} else {
claims["box_sub_type"] = "enterprise"
claims["sub"] = Sub
}
token.Claims = claims
// Sign the JWT
tokenString, err = token.SignedString(signingKey)
if err != nil {
msg = "Unable to sign token, please check that you have a signing key in ./keys/"
return msg, err
}
return tokenString, err
}
// SendOAuthRequest - Sends a POST to authenticate against Box using JWT Assertion
func SendOAuthRequest(ClientID string, ClientSecret string, JWToken string) (string, error) {
var err error
var msg string
var decodedResponse *oAuthResponse
hc := http.Client{}
form := url.Values{}
// Build form to POST
form.Add("grant_type", JWTGRANTTYPE)
form.Add("client_id", ClientID)
form.Add("client_secret", ClientSecret)
form.Add("assertion", JWToken)
// Here is where the code stops working, returns an empty body
// The request looks like it should be working
req, err := http.NewRequest("POST", JWTAUTHURL, strings.NewReader(form.Encode()))
req.PostForm = form
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
if err != nil {
return msg, err
}
resp, err := hc.Do(req)
if err != nil {
msg = "Error submitting request to Box API"
return msg, err
}
//
fmt.Println(resp.Status)
fmt.Println(resp.Body)
//
err = json.NewDecoder(resp.Body).Decode(&decodedResponse)
if err != nil {
msg = "Error decoding OAuthResponse"
} else {
// We only need the Access Token
msg = decodedResponse.AccessToken
}
return msg, err
}
400 Bad Request
&{0xc420064100 {0 0} false 0x5f48b0 0x5f4840}