Reputation: 155
I'm trying to use GitHub App and I need to generate a JWT for authenticating (https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps#generating-a-private-key) I'm trying to do that using Goland. How can I generate a JWT from PEM private key in Go??
Upvotes: 2
Views: 1813
Reputation: 4202
To successfully create a github app jwt token from a private permissions file in Go, the following jwt claims are required:
iat
: the "issued at" date of the token (minus 60 seconds for clock float)exp
: the expiry date of the token (no more than 10 minutes from the iat
iss
: the App ID of the Github app (Note: This is not the client id of the app).Once this is in place, a new jwt token can be created which can be used to communicate with the github api.
package main
import "github.com/golang-jwt/jwt/v5"
func main() {
pemFilePath := "private-key.pem" // replace
appId := "<app-id>" // replace
now := time.Now()
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
"iat": jwt.NewNumericDate(now.Add(-time.Minute)),
"exp": jwt.NewNumericDate(now.Add(5 * time.Minute)),
"iss": appID,
})
pemKey, _ := ioutil.ReadFile(pemFilePath)
privateKey, _ := jwt.ParseRSAPrivateKeyFromPEM(pemKey)
tokenString, err := token.SignedString(privateKey)
if err != nil {
panic(err)
}
fmt.Println(tokenString)
}
In the example above, we use ioutil.ReadFile
to read the bytes of the pem file (this is supplied by Github). When then parse the bytes and finally create a signed string with it.
Notes:
exp
set to 5 minutes. It can be no longer than 10 minutes from the iat
. It would be better to .Add()
from an iat
value to ensure it is the correct time.now.Add(-time.Minute)
will essentially subtract the current time by a minuteHope this helps.
Upvotes: 2
Reputation: 3539
I suggest reading code from this repository:
https://github.com/bradleyfalzon/ghinstallation
I don't know why, but the code in the answer from @JesseB above didn't work for me - it always throws: 401 Unauthorized. Although this repository does use golang-jwt
package internally
Upvotes: 0
Reputation: 76
The jwt-go library has all the tools you need, and is fairly well documented. You can find it at https://github.com/golang-jwt/jwt.
Assuming you understand what JWTs are and how they're structured, and that you can get that PEM key as a []byte, the process is roughly:
In practice, it will look something like this:
imports "github.com/golang-jwt/jwt/v4"
type MyCustomClaims struct {
*jwt.RegisteredClaims
FooClaim int
BarClaim string
}
func CreateJWT(pemKey []byte) string {
// expires in 60 minutes
expiration := time.Now().Add(time.Second * 3600)
claims := MyCustomClaims{
RegisteredClaims: &jwt.RegisteredClaims{
Issuer: "Example Code Inc.",
ExpiresAt: jwt.NewNumericDate(expiration),
Subject: "JWT Creation",
},
FooClaim: 123,
BarClaim: "bar",
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
privateKey, _ := jwt.ParseRSAPrivateKeyFromPEM(pemKey)
myJWT, _ := jwt.SignedString(privateKey)
return myJWT
}
Upvotes: 6