Skip to content

Commit

Permalink
Add JWT token issue and validate handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanprodan committed Sep 10, 2018
1 parent 54f6d9f commit 6c98fbf
Show file tree
Hide file tree
Showing 26 changed files with 1,731 additions and 1 deletion.
8 changes: 7 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
name = "github.com/spf13/cobra"
version = "v0.0.3"

[[constraint]]
name = "github.com/dgrijalva/jwt-go"
version = "v3.2.0"

[prune]
go-tests = true
unused-packages = true
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Web API:
* `GET /env` returns the environment variables as a JSON array
* `GET /headers` returns a JSON with the request HTTP headers
* `GET /delay/{seconds}` waits for the specified period
* `POST /token` issues a JWT token valid for one minute `JWT=$(curl -sd 'anon' podinfo:9898/token | jq -r .token)`
* `GET /token/validate` validates the JWT token `curl -H "Authorization: Bearer ${JWT}" podinfo:9898/token/validate`
* `GET /configs` returns a JSON with configmaps and/or secrets mounted in the `config` volume
* `POST /write` writes the posted content to disk at /data/hash and returns the SHA1 hash of the content
* `GET /read/{hash}` returns the content of the file /data/hash if exists
Expand Down
1 change: 1 addition & 0 deletions cmd/podinfo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func main() {
viper.BindPFlags(fs)
viper.RegisterAlias("backendUrl", "backend-url")
hostname, _ := os.Hostname()
viper.SetDefault("jwt-secret", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9")
viper.Set("hostname", hostname)
viper.Set("version", version.VERSION)
viper.Set("revision", version.REVISION)
Expand Down
3 changes: 3 additions & 0 deletions pkg/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type Config struct {
Hostname string `mapstructure:"hostname"`
RandomDelay bool `mapstructure:"random-delay"`
RandomError bool `mapstructure:"random-error"`
JWTSecret string `mapstructure:"jwt-secret"`
}

type Server struct {
Expand Down Expand Up @@ -73,6 +74,8 @@ func (s *Server) registerHandlers() {
s.router.HandleFunc("/store", s.storeWriteHandler).Methods("POST")
s.router.HandleFunc("/store/{hash}", s.storeReadHandler).Methods("GET").Name("store")
s.router.HandleFunc("/configs", s.configReadHandler).Methods("GET")
s.router.HandleFunc("/token", s.tokenGenerateHandler).Methods("POST")
s.router.HandleFunc("/token/validate", s.tokenValidateHandler).Methods("GET")
s.router.HandleFunc("/api/info", s.infoHandler).Methods("GET")
s.router.HandleFunc("/api/echo", s.echoHandler).Methods("POST")
}
Expand Down
102 changes: 102 additions & 0 deletions pkg/api/token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package api

import (
"fmt"
"net/http"
"strings"
"time"

"io/ioutil"

"github.com/dgrijalva/jwt-go"
"go.uber.org/zap"
)

type jwtCustomClaims struct {
Name string `json:"name"`
jwt.StandardClaims
}

func (s *Server) tokenGenerateHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
s.logger.Error("reading the request body failed", zap.Error(err))
s.ErrorResponse(w, r, "invalid request body", http.StatusBadRequest)
return
}
defer r.Body.Close()

user := "anonymous"
if len(body) > 0 {
user = string(body)
}

claims := &jwtCustomClaims{
user,
jwt.StandardClaims{
Issuer: "podinfo",
ExpiresAt: time.Now().Add(time.Minute * 1).Unix(),
},
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
t, err := token.SignedString([]byte(s.config.JWTSecret))
if err != nil {
s.ErrorResponse(w, r, err.Error(), http.StatusBadRequest)
return
}

var result = struct {
Token string `json:"token"`
ExpiresAt time.Time `json:"expires_at"`
}{
Token: t,
ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt, 0),
}

s.JSONResponse(w, r, result)
}

// Get: JWT=$(curl -s -d 'test' localhost:9898/token | jq -r .token)
// Post: curl -H "Authorization: Bearer ${JWT}" localhost:9898/token/validate
func (s *Server) tokenValidateHandler(w http.ResponseWriter, r *http.Request) {
authorizationHeader := r.Header.Get("authorization")
if authorizationHeader == "" {
s.ErrorResponse(w, r, "authorization bearer header required", http.StatusUnauthorized)
return
}
bearerToken := strings.Split(authorizationHeader, " ")
if len(bearerToken) != 2 || strings.ToLower(bearerToken[0]) != "bearer" {
s.ErrorResponse(w, r, "authorization bearer header required", http.StatusUnauthorized)
return
}

claims := jwtCustomClaims{}
token, err := jwt.ParseWithClaims(bearerToken[1], &claims, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("invalid signing method")
}
return []byte(s.config.JWTSecret), nil
})
if err != nil {
s.ErrorResponse(w, r, err.Error(), http.StatusUnauthorized)
return
}

if token.Valid {
if claims.StandardClaims.Issuer != "podinfo" {
s.ErrorResponse(w, r, "invalid issuer", http.StatusUnauthorized)
} else {
var result = struct {
TokenName string `json:"token_name"`
ExpiresAt time.Time `json:"expires_at"`
}{
TokenName: claims.Name,
ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt, 0),
}
s.JSONResponse(w, r, result)
}
} else {
s.ErrorResponse(w, r, "Invalid authorization token", http.StatusUnauthorized)
}
}
4 changes: 4 additions & 0 deletions vendor/github.com/dgrijalva/jwt-go/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions vendor/github.com/dgrijalva/jwt-go/.travis.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions vendor/github.com/dgrijalva/jwt-go/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

97 changes: 97 additions & 0 deletions vendor/github.com/dgrijalva/jwt-go/MIGRATION_GUIDE.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 6c98fbf

Please sign in to comment.