Skip to content

Commit

Permalink
asd
Browse files Browse the repository at this point in the history
  • Loading branch information
dbarrosop committed Feb 16, 2024
1 parent 8b40697 commit 2378f74
Show file tree
Hide file tree
Showing 101 changed files with 9,374 additions and 2,628 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ go 1.21
require (
github.com/getkin/kin-openapi v0.123.0
github.com/gin-gonic/gin v1.9.1
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/google/go-cmp v0.5.5
github.com/google/uuid v1.5.0
github.com/jackc/pgx/v5 v5.3.1
github.com/lmittmann/tint v1.0.4
Expand All @@ -15,7 +17,6 @@ require (
)

require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/bytedance/sonic v1.10.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.0 // indirect
Expand Down
8 changes: 2 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc=
Expand Down Expand Up @@ -41,6 +37,8 @@ github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
Expand All @@ -61,7 +59,6 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
Expand Down Expand Up @@ -99,7 +96,6 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
Expand Down
113 changes: 113 additions & 0 deletions go/controller/jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package controller

import (
"encoding/json"
"fmt"
"time"

"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
)

type JWTSecret struct {
Key string `json:"key"`
Type string `json:"type"`
Issuer string `json:"issuer"`
ClaimsNamespace string `json:"claims_namespace"`
}

func decodeJWTSecret(jwtSecretb []byte) (JWTSecret, error) {
var jwtSecret JWTSecret
if err := json.Unmarshal(jwtSecretb, &jwtSecret); err != nil {
return JWTSecret{}, fmt.Errorf("error unmarshalling jwt secret: %w", err)
}

if jwtSecret.Issuer == "" {
jwtSecret.Issuer = "hasura-auth"
}

if jwtSecret.ClaimsNamespace == "" {
jwtSecret.ClaimsNamespace = "https://hasura.io/jwt/claims"
}

return jwtSecret, nil
}

func JWTGetterFn(
jwtSecretb []byte,
accessTokenExpiresIn time.Duration,
) (
func(userID uuid.UUID) (string, error),
error,
) {
jwtSecret, err := decodeJWTSecret(jwtSecretb)
if err != nil {
return nil, err
}

mySigningKey := []byte(jwtSecret.Key)
method := jwt.GetSigningMethod(jwtSecret.Type)

return func(userID uuid.UUID) (string, error) {
now := time.Now()
iat := now.Unix()
exp := now.Add(accessTokenExpiresIn).Unix()

// Create the Claims
claims := &jwt.MapClaims{
"sub": userID.String(),
"iss": jwtSecret.Issuer,
"iat": iat,
"exp": exp,
jwtSecret.ClaimsNamespace: map[string]any{
"x-hasura-allowed-roles": []string{
"admin",
"user",
"project_manager",
"anonymous",
},
"x-hasura-default-role": "user",
"x-hasura-user-id": userID.String(),
"x-hasura-user-isAnonymous": "false",
},
}
token := jwt.NewWithClaims(method, claims)
ss, err := token.SignedString(mySigningKey)
if err != nil {
return "", fmt.Errorf("error signing token: %w", err)
}

return ss, nil
}, nil
}

func JWTValidateFn(
jwtSecretb []byte,
) (
func(jwt string) (*jwt.Token, error),
error,
) {
jwtSecret, err := decodeJWTSecret(jwtSecretb)
if err != nil {
return nil, err
}

mySigningKey := []byte(jwtSecret.Key)

return func(accessToken string) (*jwt.Token, error) {
jwtToken, err := jwt.Parse(
accessToken,
func(token *jwt.Token) (interface{}, error) {
return mySigningKey, nil
},
jwt.WithValidMethods([]string{jwtSecret.Type}),
jwt.WithIssuer(jwtSecret.Issuer),
jwt.WithIssuedAt(),
jwt.WithExpirationRequired(),
)
if err != nil {
return nil, fmt.Errorf("error parsing token: %w", err)
}
return jwtToken, nil
}, nil
}
166 changes: 166 additions & 0 deletions go/controller/jwt_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package controller_test

import (
"crypto"
"testing"
"time"

"github.com/golang-jwt/jwt/v5"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/google/uuid"
"github.com/nhost/hasura-auth/go/controller"
)

//nolint:lll,gochecknoglobals
var (
jwtSecret = []byte(`{"type":"HS256", "key":"5152fa850c02dc222631cca898ed1485821a70912a6e3649c49076912daa3b62182ba013315915d64f40cddfbb8b58eb5bd11ba225336a6af45bbae07ca873f3"}`)
jwtSecretWithIssuer = []byte(`{"type":"HS256", "key":"5152fa850c02dc222631cca898ed1485821a70912a6e3649c49076912daa3b62182ba013315915d64f40cddfbb8b58eb5bd11ba225336a6af45bbae07ca873f3","issuer":"some-issuer"}`)
jwtSecretWithClaimsNamespace = []byte(`{"type":"HS256", "key":"5152fa850c02dc222631cca898ed1485821a70912a6e3649c49076912daa3b62182ba013315915d64f40cddfbb8b58eb5bd11ba225336a6af45bbae07ca873f3","claims_namespace":"some/namespace"}`)
)

func TestGetJWTFunc(t *testing.T) {
t.Parallel()

userID := uuid.MustParse("585e21fc-3664-4d03-8539-69945342a4f4")

cases := []struct {
name string
key []byte
userID uuid.UUID
expiresIn time.Duration
expectedToken *jwt.Token
}{
{
name: "with valid key",
key: jwtSecret,
userID: userID,
expiresIn: time.Hour,
expectedToken: &jwt.Token{
Raw: "ignored",
Method: &jwt.SigningMethodHMAC{Name: "HS256", Hash: crypto.SHA256},
Header: map[string]interface{}{"alg": string("HS256"), "typ": string("JWT")},
Claims: jwt.MapClaims{
"exp": float64(1.708103735e+09),
"https://hasura.io/jwt/claims": map[string]interface{}{
"x-hasura-allowed-roles": []interface{}{
string("admin"),
string("user"),
string("project_manager"),
string("anonymous"),
},
"x-hasura-default-role": string("user"),
"x-hasura-user-id": string("585e21fc-3664-4d03-8539-69945342a4f4"),
"x-hasura-user-isAnonymous": string("false"),
},
"iat": float64(1.708100135e+09),
"iss": string("hasura-auth"),
"sub": string("585e21fc-3664-4d03-8539-69945342a4f4"),
},
Signature: []uint8{},
Valid: true,
},
},

{
name: "with valid key with issuer",
key: jwtSecretWithIssuer,
userID: userID,
expiresIn: time.Hour,
expectedToken: &jwt.Token{
Raw: "ignored",
Method: &jwt.SigningMethodHMAC{Name: "HS256", Hash: crypto.SHA256},
Header: map[string]interface{}{"alg": string("HS256"), "typ": string("JWT")},
Claims: jwt.MapClaims{
"exp": float64(1.708103735e+09),
"https://hasura.io/jwt/claims": map[string]interface{}{
"x-hasura-allowed-roles": []interface{}{
string("admin"),
string("user"),
string("project_manager"),
string("anonymous"),
},
"x-hasura-default-role": string("user"),
"x-hasura-user-id": string("585e21fc-3664-4d03-8539-69945342a4f4"),
"x-hasura-user-isAnonymous": string("false"),
},
"iat": float64(1.708100135e+09),
"iss": string("some-issuer"),
"sub": string("585e21fc-3664-4d03-8539-69945342a4f4"),
},
Signature: []uint8{},
Valid: true,
},
},

{
name: "with valid key with claims namespace",
key: jwtSecretWithClaimsNamespace,
userID: userID,
expiresIn: time.Hour,
expectedToken: &jwt.Token{
Raw: "ignored",
Method: &jwt.SigningMethodHMAC{Name: "HS256", Hash: crypto.SHA256},
Header: map[string]interface{}{"alg": string("HS256"), "typ": string("JWT")},
Claims: jwt.MapClaims{
"exp": float64(1.708103735e+09),
"some/namespace": map[string]interface{}{
"x-hasura-allowed-roles": []interface{}{
string("admin"),
string("user"),
string("project_manager"),
string("anonymous"),
},
"x-hasura-default-role": string("user"),
"x-hasura-user-id": string("585e21fc-3664-4d03-8539-69945342a4f4"),
"x-hasura-user-isAnonymous": string("false"),
},
"iat": float64(1.708100135e+09),
"iss": string("hasura-auth"),
"sub": string("585e21fc-3664-4d03-8539-69945342a4f4"),
},
Signature: []uint8{},
Valid: true,
},
},
}

for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
tc := tc

getterFn, err := controller.JWTGetterFn(tc.key, tc.expiresIn)
if err != nil {
t.Fatalf("GetJWTFunc() err = %v; want nil", err)
}

accessToken, err := getterFn(tc.userID)
if err != nil {
t.Fatalf("fn() err = %v; want nil", err)
}
t.Logf("token = %v", accessToken)

validatorFn, err := controller.JWTValidateFn(tc.key)
if err != nil {
t.Fatalf("JWTValidatorFn() err = %v; want nil", err)
}

decodedToken, err := validatorFn(accessToken)
if err != nil {
t.Fatalf("fn() err = %v; want nil", err)
}

cmpopts := []cmp.Option{
cmpopts.IgnoreFields(jwt.Token{}, "Raw", "Signature"), //nolint:exhaustruct
cmpopts.IgnoreMapEntries(func(key string, value interface{}) bool {
return key == "iat" || key == "exp"
}),
}
if diff := cmp.Diff(decodedToken, tc.expectedToken, cmpopts...); diff != "" {
t.Errorf("decodedToken mismatch (-want +got):\n%s", diff)
}
})
}
}
6 changes: 0 additions & 6 deletions vendor/github.com/apapsch/go-jsonmerge/v2/.editorconfig

This file was deleted.

Loading

0 comments on commit 2378f74

Please sign in to comment.