istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/common/jwt/jwt_token_test.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package jwt
    16  
    17  import (
    18  	"encoding/json"
    19  	"os"
    20  	"reflect"
    21  	"testing"
    22  
    23  	"github.com/lestrrat-go/jwx/jwa"
    24  	"github.com/lestrrat-go/jwx/jwk"
    25  	"github.com/lestrrat-go/jwx/jws"
    26  )
    27  
    28  func getKey(jwksFile string, t *testing.T) any {
    29  	t.Helper()
    30  
    31  	data, err := os.ReadFile(jwksFile)
    32  	if err != nil {
    33  		t.Fatalf("failed to read jwks: %s", err)
    34  	}
    35  	jwks, err := jwk.Parse(data)
    36  	if err != nil {
    37  		t.Fatalf("failed to parse jwks: %s", err)
    38  	}
    39  	var key any
    40  	k, _ := jwks.Get(0)
    41  	if err := k.Raw(&key); err != nil {
    42  		t.Fatalf("failed to materialize jwks: %s", err)
    43  	}
    44  	return key
    45  }
    46  
    47  func TestSampleJwtToken(t *testing.T) {
    48  	testCases := []struct {
    49  		name        string
    50  		token       string
    51  		wantClaims  map[string]any
    52  		wantInvalid bool
    53  	}{
    54  		{
    55  			name:  "TokenIssuer1",
    56  			token: TokenIssuer1,
    57  			wantClaims: map[string]any{
    58  				"groups": []any{"group-1"},
    59  				"iss":    "test-issuer-1@istio.io",
    60  				"sub":    "sub-1",
    61  				"exp":    4715782722.0,
    62  			},
    63  		},
    64  		{
    65  			name:  "TokenIssuer1NestedClaims1",
    66  			token: TokenIssuer1WithNestedClaims1,
    67  			wantClaims: map[string]any{
    68  				"nested": map[string]any{
    69  					"key1": []any{"valueA", "valueB"},
    70  					"nested-2": map[string]any{
    71  						"key1": []any{"valueA", "valueB"},
    72  					},
    73  				},
    74  				"iss": "test-issuer-1@istio.io",
    75  				"sub": "sub-1",
    76  				"exp": 4757607896.0,
    77  			},
    78  		},
    79  		{
    80  			name:  "TokenIssuer1NestedClaims2",
    81  			token: TokenIssuer1WithNestedClaims2,
    82  			wantClaims: map[string]any{
    83  				"nested": map[string]any{
    84  					"key2": "valueC",
    85  					"nested-2": map[string]any{
    86  						"key2": "valueC",
    87  					},
    88  				},
    89  				"iss": "test-issuer-1@istio.io",
    90  				"sub": "sub-1",
    91  				"exp": 4757608018.0,
    92  			},
    93  		},
    94  		{
    95  			name:  "TokenIssuer2",
    96  			token: TokenIssuer2,
    97  			wantClaims: map[string]any{
    98  				"groups": []any{"group-2"},
    99  				"iss":    "test-issuer-2@istio.io",
   100  				"sub":    "sub-2",
   101  				"exp":    4715782783.0,
   102  			},
   103  		},
   104  		{
   105  			name:  "TokenExpired",
   106  			token: TokenExpired,
   107  			wantClaims: map[string]any{
   108  				"groups": []any{"group-1"},
   109  				"iss":    "test-issuer-1@istio.io",
   110  				"sub":    "sub-1",
   111  				"exp":    1562182856.0,
   112  			},
   113  		},
   114  		{
   115  			name:        "TokenInvalid",
   116  			token:       TokenInvalid,
   117  			wantInvalid: true,
   118  		},
   119  	}
   120  
   121  	key := getKey("jwks.json", t)
   122  	for _, tc := range testCases {
   123  		token, err := jws.Verify([]byte(tc.token), jwa.RS256, key)
   124  		if tc.wantInvalid {
   125  			if err == nil {
   126  				t.Errorf("%s: got valid token but want invalid", tc.name)
   127  			}
   128  			continue
   129  		}
   130  		if err != nil {
   131  			t.Fatalf("%s: failed to parse token: %v", tc.name, err)
   132  		}
   133  
   134  		claims := map[string]any{}
   135  		err = json.Unmarshal(token, &claims)
   136  		if err != nil {
   137  			t.Fatalf("%s: failed to parse payload: %v", tc.name, err)
   138  		}
   139  
   140  		for k, v := range tc.wantClaims {
   141  			got, ok := claims[k]
   142  			if ok {
   143  				if !reflect.DeepEqual(got, v) {
   144  					t.Errorf("%s: claim %q got value %v but want %v", tc.name, k, got, v)
   145  				}
   146  			} else {
   147  				t.Errorf("%s: want claim %s but not found", tc.name, k)
   148  			}
   149  		}
   150  	}
   151  }