Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sign JSON Web Token (JWT) #772

Merged
merged 4 commits into from
May 26, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions lib/util/http/auth/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ func oAuth2FieldSpec() docs.FieldSpec {
)
}

func jwtFieldSpec() docs.FieldSpec {
return docs.FieldAdvanced("jwt",
"Allows you to specify JWT authentication.",
).WithChildren(
docs.FieldCommon("enabled", "Whether to use JWT authentication in requests."),
docs.FieldCommon("private_key", "A file with the PEM encoded via PKCS1 or PKCS8 as private key."),
docs.FieldCommon("signing_method", "A method used to sign the token such as RS256, RS384 or RS512."),
docs.FieldAdvanced("claims", "A value used to identify the claims that issued the JWT.").Map(),
)
}

// FieldSpecs returns a map of field specs for an auth type.
func FieldSpecs() docs.FieldSpecs {
return docs.FieldSpecs{
Expand All @@ -46,12 +57,12 @@ func FieldSpecs() docs.FieldSpecs {
}
}

// FieldSpecsExpanded includes OAuth2 fields that might not be appropriate for
// all components.
// FieldSpecsExpanded includes OAuth2 and JWT fields that might not be appropriate for all components.
func FieldSpecsExpanded() docs.FieldSpecs {
return docs.FieldSpecs{
oAuthFieldSpec(),
oAuth2FieldSpec(),
jwtFieldSpec(),
BasicAuthFieldSpec(),
}
}
77 changes: 77 additions & 0 deletions lib/util/http/auth/jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package auth

import (
"crypto/rsa"
"fmt"
"io/ioutil"
"net/http"

"github.com/dgrijalva/jwt-go"
)

//------------------------------------------------------------------------------

// JWTConfig holds the configuration parameters for an JWT exchange.
type JWTConfig struct {
Enabled bool `json:"enabled" yaml:"enabled"`
Claims jwt.MapClaims `json:"claims" yaml:"claims"`
SigningMethod string `json:"signing_method" yaml:"signing_method"`
PrivateKey string `json:"private_key" yaml:"private_key"`
manute marked this conversation as resolved.
Show resolved Hide resolved

// internal private fields
rsaKey *rsa.PrivateKey
}

// NewJWTConfig returns a new JWTConfig with default values.
func NewJWTConfig() JWTConfig {
return JWTConfig{
Enabled: false,
Claims: nil,
manute marked this conversation as resolved.
Show resolved Hide resolved
SigningMethod: "",
PrivateKey: "",
}
}

//------------------------------------------------------------------------------

// Sign method to sign an HTTP request for an JWT exchange.
func (j JWTConfig) Sign(req *http.Request) error {
if !j.Enabled {
return nil
}

// Must parse private key only once
if j.rsaKey == nil {
manute marked this conversation as resolved.
Show resolved Hide resolved
privateKey, err := ioutil.ReadFile(j.PrivateKey)
if err != nil {
return fmt.Errorf("failed to read private key: %v", err)
}

j.rsaKey, err = jwt.ParseRSAPrivateKeyFromPEM(privateKey)
if err != nil {
return fmt.Errorf("failed to parse private key: %v", err)
}
}

var bearer *jwt.Token
switch j.SigningMethod {
case "RS256":
bearer = jwt.NewWithClaims(jwt.SigningMethodRS256, j.Claims)
case "RS384":
bearer = jwt.NewWithClaims(jwt.SigningMethodRS384, j.Claims)
case "RS512":
bearer = jwt.NewWithClaims(jwt.SigningMethodRS512, j.Claims)
default:
return fmt.Errorf("jwt signing method %s not acepted. Try with RS256, RS384 or RS512", j.SigningMethod)
}

ss, err := bearer.SignedString(j.rsaKey)
if err != nil {
return fmt.Errorf("failed to sign jwt: %v", err)
}

req.Header.Set("Authorization", "Bearer "+ss)
return nil
}

//------------------------------------------------------------------------------
2 changes: 2 additions & 0 deletions lib/util/http/client/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type Config struct {
ProxyURL string `json:"proxy_url" yaml:"proxy_url"`
auth.Config `json:",inline" yaml:",inline"`
OAuth2 auth.OAuth2Config `json:"oauth2" yaml:"oauth2"`
JWT auth.JWTConfig `json:"jwt" yaml:"jwt"`
}

// NewConfig creates a new Config with default values.
Expand All @@ -71,6 +72,7 @@ func NewConfig() Config {
TLS: tls.NewConfig(),
Config: auth.NewConfig(),
OAuth2: auth.NewOAuth2Config(),
JWT: auth.NewJWTConfig(),
}
}

Expand Down