From 8a2d0342e072b3bdbc232f5158f421a3b96cc2a0 Mon Sep 17 00:00:00 2001 From: Greg Steuck Date: Sun, 4 Jul 2021 06:35:26 -0700 Subject: dashboard/app: parse JWT into a separate JSON struct Between JSON type choices and Go struct embedding syntax noise, relying on field duplication seems the least ugly option. --- dashboard/app/auth.go | 34 +++++++++++++++++++++++++++------- dashboard/app/auth_test.go | 17 +++++++++-------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/dashboard/app/auth.go b/dashboard/app/auth.go index c356ad543..b096ebf38 100644 --- a/dashboard/app/auth.go +++ b/dashboard/app/auth.go @@ -32,6 +32,7 @@ import ( "fmt" "net/http" "net/url" + "strconv" "strings" "time" @@ -57,10 +58,20 @@ func mkAuthEndpoint(u string) authEndpoint { return authEndpoint{url: u} } +// The JSON representaion of JWT claims. +type jwtClaimsParse struct { + Subject string `json:"sub"` + Audience string `json:"aud"` + // The field in the JSON is a string but contains a UNIX time. + Expiration string `json:"exp"` +} + +// The typed representation of JWT claims. type jwtClaims struct { - Subject string `json:"sub"` - Expiration float64 `json:"exp"` - Audience string `json:"aud"` + Subject string + Audience string + // The app uses the typed value. + Expiration time.Time } func (auth *authEndpoint) queryTokenInfo(tokenValue string) (*jwtClaims, error) { @@ -73,11 +84,20 @@ func (auth *authEndpoint) queryTokenInfo(tokenValue string) (*jwtClaims, error) if err != nil { return nil, err } - claims := new(jwtClaims) + claims := new(jwtClaimsParse) if err = json.Unmarshal(body, claims); err != nil { return nil, err } - return claims, nil + expInt, err := strconv.ParseInt(claims.Expiration, 10, 64) + if err != nil { + return nil, err + } + r := jwtClaims{ + Subject: claims.Subject, + Audience: claims.Audience, + Expiration: time.Unix(expInt, 0), + } + return &r, nil } // Returns the verified subject value based on the provided header @@ -96,10 +116,10 @@ func (auth *authEndpoint) determineAuthSubj(authHeader []string) (string, error) return "", err } if claims.Audience != dashapi.DashboardAudience { - err := fmt.Errorf("Unexpected audience %v", claims.Audience) + err := fmt.Errorf("Unexpected audience %v %v", claims.Audience, claims) return "", err } - if claims.Expiration < float64(time.Now().Unix()) { + if claims.Expiration.Before(time.Now()) { err := fmt.Errorf("Token past expiration %v", claims.Expiration) return "", err } diff --git a/dashboard/app/auth_test.go b/dashboard/app/auth_test.go index 6b68f61a1..02d7ad9fc 100644 --- a/dashboard/app/auth_test.go +++ b/dashboard/app/auth_test.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "github.com/google/syzkaller/dashboard/dashapi" "net/http" "net/http/httptest" @@ -10,13 +11,13 @@ import ( "time" ) -func dayFromNow() float64 { - return float64(time.Now().AddDate(0, 0, 1).Unix()) -} - func reponseFor(t *testing.T, claims jwtClaims) (*httptest.Server, authEndpoint) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - bytes, err := json.Marshal(claims) + bytes, err := json.Marshal(jwtClaimsParse{ + Subject: claims.Subject, + Audience: claims.Audience, + Expiration: fmt.Sprint(claims.Expiration.Unix()), + }) if err != nil { t.Fatalf("Marshal %v", err) } @@ -30,8 +31,8 @@ func TestBearerValid(t *testing.T) { magic := "ValidSubj" ts, dut := reponseFor(t, jwtClaims{ Subject: magic, - Expiration: dayFromNow(), Audience: dashapi.DashboardAudience, + Expiration: time.Now().AddDate(0, 0, 1), }) defer ts.Close() @@ -47,7 +48,7 @@ func TestBearerValid(t *testing.T) { func TestBearerWrongAudience(t *testing.T) { ts, dut := reponseFor(t, jwtClaims{ Subject: "irrelevant", - Expiration: dayFromNow(), + Expiration: time.Now().AddDate(0, 0, 1), Audience: "junk", }) defer ts.Close() @@ -61,7 +62,7 @@ func TestBearerWrongAudience(t *testing.T) { func TestBearerExpired(t *testing.T) { ts, dut := reponseFor(t, jwtClaims{ Subject: "irrelevant", - Expiration: -1234, + Expiration: time.Now().AddDate(0, 0, -1), Audience: dashapi.DashboardAudience, }) defer ts.Close() -- cgit mrf-deployment