1*e7b1675dSTing-Kang Chang// Copyright 2022 Google LLC 2*e7b1675dSTing-Kang Chang// 3*e7b1675dSTing-Kang Chang// Licensed under the Apache License, Version 2.0 (the "License"); 4*e7b1675dSTing-Kang Chang// you may not use this file except in compliance with the License. 5*e7b1675dSTing-Kang Chang// You may obtain a copy of the License at 6*e7b1675dSTing-Kang Chang// 7*e7b1675dSTing-Kang Chang// http://www.apache.org/licenses/LICENSE-2.0 8*e7b1675dSTing-Kang Chang// 9*e7b1675dSTing-Kang Chang// Unless required by applicable law or agreed to in writing, software 10*e7b1675dSTing-Kang Chang// distributed under the License is distributed on an "AS IS" BASIS, 11*e7b1675dSTing-Kang Chang// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*e7b1675dSTing-Kang Chang// See the License for the specific language governing permissions and 13*e7b1675dSTing-Kang Chang// limitations under the License. 14*e7b1675dSTing-Kang Chang// 15*e7b1675dSTing-Kang Chang/////////////////////////////////////////////////////////////////////////////// 16*e7b1675dSTing-Kang Chang 17*e7b1675dSTing-Kang Changpackage services_test 18*e7b1675dSTing-Kang Chang 19*e7b1675dSTing-Kang Changimport ( 20*e7b1675dSTing-Kang Chang "context" 21*e7b1675dSTing-Kang Chang "errors" 22*e7b1675dSTing-Kang Chang "fmt" 23*e7b1675dSTing-Kang Chang "testing" 24*e7b1675dSTing-Kang Chang 25*e7b1675dSTing-Kang Chang dpb "google.golang.org/protobuf/types/known/durationpb" 26*e7b1675dSTing-Kang Chang spb "google.golang.org/protobuf/types/known/structpb" 27*e7b1675dSTing-Kang Chang tpb "google.golang.org/protobuf/types/known/timestamppb" 28*e7b1675dSTing-Kang Chang wpb "google.golang.org/protobuf/types/known/wrapperspb" 29*e7b1675dSTing-Kang Chang "github.com/google/go-cmp/cmp" 30*e7b1675dSTing-Kang Chang "google.golang.org/protobuf/proto" 31*e7b1675dSTing-Kang Chang "google.golang.org/protobuf/testing/protocmp" 32*e7b1675dSTing-Kang Chang "github.com/google/tink/go/aead" 33*e7b1675dSTing-Kang Chang "github.com/google/tink/go/jwt" 34*e7b1675dSTing-Kang Chang "github.com/google/tink/go/signature" 35*e7b1675dSTing-Kang Chang "github.com/google/tink/testing/go/services" 36*e7b1675dSTing-Kang Chang pb "github.com/google/tink/testing/go/protos/testing_api_go_grpc" 37*e7b1675dSTing-Kang Chang) 38*e7b1675dSTing-Kang Chang 39*e7b1675dSTing-Kang Changfunc verifiedJWTFromResponse(response *pb.JwtVerifyResponse) (*pb.JwtToken, error) { 40*e7b1675dSTing-Kang Chang switch r := response.Result.(type) { 41*e7b1675dSTing-Kang Chang case *pb.JwtVerifyResponse_VerifiedJwt: 42*e7b1675dSTing-Kang Chang return r.VerifiedJwt, nil 43*e7b1675dSTing-Kang Chang case *pb.JwtVerifyResponse_Err: 44*e7b1675dSTing-Kang Chang return nil, errors.New(r.Err) 45*e7b1675dSTing-Kang Chang default: 46*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("response.Result has unexpected type %T", r) 47*e7b1675dSTing-Kang Chang } 48*e7b1675dSTing-Kang Chang} 49*e7b1675dSTing-Kang Chang 50*e7b1675dSTing-Kang Changfunc signedCompactJWTFromResponse(response *pb.JwtSignResponse) (string, error) { 51*e7b1675dSTing-Kang Chang switch r := response.Result.(type) { 52*e7b1675dSTing-Kang Chang case *pb.JwtSignResponse_SignedCompactJwt: 53*e7b1675dSTing-Kang Chang return r.SignedCompactJwt, nil 54*e7b1675dSTing-Kang Chang case *pb.JwtSignResponse_Err: 55*e7b1675dSTing-Kang Chang return "", errors.New(r.Err) 56*e7b1675dSTing-Kang Chang default: 57*e7b1675dSTing-Kang Chang return "", fmt.Errorf("response.Result has unexpected type %T", r) 58*e7b1675dSTing-Kang Chang } 59*e7b1675dSTing-Kang Chang} 60*e7b1675dSTing-Kang Chang 61*e7b1675dSTing-Kang Changfunc jwkSetFromResponse(response *pb.JwtToJwkSetResponse) (string, error) { 62*e7b1675dSTing-Kang Chang switch r := response.Result.(type) { 63*e7b1675dSTing-Kang Chang case *pb.JwtToJwkSetResponse_JwkSet: 64*e7b1675dSTing-Kang Chang return r.JwkSet, nil 65*e7b1675dSTing-Kang Chang case *pb.JwtToJwkSetResponse_Err: 66*e7b1675dSTing-Kang Chang return "", errors.New(r.Err) 67*e7b1675dSTing-Kang Chang default: 68*e7b1675dSTing-Kang Chang return "", fmt.Errorf("response.Result has unexpected type %T", r) 69*e7b1675dSTing-Kang Chang } 70*e7b1675dSTing-Kang Chang} 71*e7b1675dSTing-Kang Chang 72*e7b1675dSTing-Kang Changfunc keysetFromResponse(response *pb.JwtFromJwkSetResponse) ([]byte, error) { 73*e7b1675dSTing-Kang Chang switch r := response.Result.(type) { 74*e7b1675dSTing-Kang Chang case *pb.JwtFromJwkSetResponse_Keyset: 75*e7b1675dSTing-Kang Chang return r.Keyset, nil 76*e7b1675dSTing-Kang Chang case *pb.JwtFromJwkSetResponse_Err: 77*e7b1675dSTing-Kang Chang return nil, errors.New(r.Err) 78*e7b1675dSTing-Kang Chang default: 79*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("response.Result has unexpected type %T", r) 80*e7b1675dSTing-Kang Chang } 81*e7b1675dSTing-Kang Chang} 82*e7b1675dSTing-Kang Chang 83*e7b1675dSTing-Kang Changtype jwtTestCase struct { 84*e7b1675dSTing-Kang Chang tag string 85*e7b1675dSTing-Kang Chang rawJWT *pb.JwtToken 86*e7b1675dSTing-Kang Chang validator *pb.JwtValidator 87*e7b1675dSTing-Kang Chang} 88*e7b1675dSTing-Kang Chang 89*e7b1675dSTing-Kang Changfunc TestJWTComputeInvalidJWT(t *testing.T) { 90*e7b1675dSTing-Kang Chang for _, tc := range []jwtTestCase{ 91*e7b1675dSTing-Kang Chang { 92*e7b1675dSTing-Kang Chang tag: "nil rawJWT", 93*e7b1675dSTing-Kang Chang rawJWT: nil, 94*e7b1675dSTing-Kang Chang }, 95*e7b1675dSTing-Kang Chang { 96*e7b1675dSTing-Kang Chang tag: "invalid json array string", 97*e7b1675dSTing-Kang Chang rawJWT: &pb.JwtToken{ 98*e7b1675dSTing-Kang Chang CustomClaims: map[string]*pb.JwtClaimValue{ 99*e7b1675dSTing-Kang Chang "cc-array": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_JsonArrayValue{JsonArrayValue: "{35}"}}, 100*e7b1675dSTing-Kang Chang }, 101*e7b1675dSTing-Kang Chang }, 102*e7b1675dSTing-Kang Chang }, 103*e7b1675dSTing-Kang Chang { 104*e7b1675dSTing-Kang Chang tag: "invalid json object string", 105*e7b1675dSTing-Kang Chang rawJWT: &pb.JwtToken{ 106*e7b1675dSTing-Kang Chang CustomClaims: map[string]*pb.JwtClaimValue{ 107*e7b1675dSTing-Kang Chang "cc-object": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_JsonObjectValue{JsonObjectValue: `["o":"a"]`}}, 108*e7b1675dSTing-Kang Chang }, 109*e7b1675dSTing-Kang Chang }, 110*e7b1675dSTing-Kang Chang }, 111*e7b1675dSTing-Kang Chang } { 112*e7b1675dSTing-Kang Chang t.Run(tc.tag, func(t *testing.T) { 113*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 114*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 115*e7b1675dSTing-Kang Chang ctx := context.Background() 116*e7b1675dSTing-Kang Chang template, err := proto.Marshal(jwt.HS256Template()) 117*e7b1675dSTing-Kang Chang if err != nil { 118*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(jwt.HS256Template()) failed: %v", err) 119*e7b1675dSTing-Kang Chang } 120*e7b1675dSTing-Kang Chang keyset, err := genKeyset(ctx, keysetService, template) 121*e7b1675dSTing-Kang Chang if err != nil { 122*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 123*e7b1675dSTing-Kang Chang } 124*e7b1675dSTing-Kang Chang signResponse, err := jwtService.ComputeMacAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, RawJwt: tc.rawJWT}) 125*e7b1675dSTing-Kang Chang if err != nil { 126*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.ComputeMacAndEncode() err = %v, want nil", err) 127*e7b1675dSTing-Kang Chang } 128*e7b1675dSTing-Kang Chang if _, err := signedCompactJWTFromResponse(signResponse); err == nil { 129*e7b1675dSTing-Kang Chang t.Fatalf("JwtSignResponse: error = nil, want error") 130*e7b1675dSTing-Kang Chang } 131*e7b1675dSTing-Kang Chang }) 132*e7b1675dSTing-Kang Chang } 133*e7b1675dSTing-Kang Chang} 134*e7b1675dSTing-Kang Chang 135*e7b1675dSTing-Kang Changfunc TestSuccessfulJwtMacCreation(t *testing.T) { 136*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 137*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 138*e7b1675dSTing-Kang Chang ctx := context.Background() 139*e7b1675dSTing-Kang Chang 140*e7b1675dSTing-Kang Chang template, err := proto.Marshal(jwt.HS256Template()) 141*e7b1675dSTing-Kang Chang if err != nil { 142*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(jwt.HS256Template()) failed: %v, want nil", err) 143*e7b1675dSTing-Kang Chang } 144*e7b1675dSTing-Kang Chang 145*e7b1675dSTing-Kang Chang keyset, err := genKeyset(ctx, keysetService, template) 146*e7b1675dSTing-Kang Chang if err != nil { 147*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 148*e7b1675dSTing-Kang Chang } 149*e7b1675dSTing-Kang Chang 150*e7b1675dSTing-Kang Chang result, err := jwtService.CreateJwtMac(ctx, &pb.CreationRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}}) 151*e7b1675dSTing-Kang Chang if err != nil { 152*e7b1675dSTing-Kang Chang t.Fatalf("CreateJwtMac with good keyset failed with gRPC error: %v, want nil", err) 153*e7b1675dSTing-Kang Chang } 154*e7b1675dSTing-Kang Chang if result.GetErr() != "" { 155*e7b1675dSTing-Kang Chang t.Fatalf("CreateJwtMac with good keyset failed with result.GetErr() = %q, want empty string", result.GetErr()) 156*e7b1675dSTing-Kang Chang } 157*e7b1675dSTing-Kang Chang} 158*e7b1675dSTing-Kang Chang 159*e7b1675dSTing-Kang Changfunc TestFailingJwtMacCreation(t *testing.T) { 160*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 161*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 162*e7b1675dSTing-Kang Chang ctx := context.Background() 163*e7b1675dSTing-Kang Chang 164*e7b1675dSTing-Kang Chang // We use signature keys -- then we cannot create a JwtMac 165*e7b1675dSTing-Kang Chang template, err := proto.Marshal(aead.AES128GCMKeyTemplate()) 166*e7b1675dSTing-Kang Chang if err != nil { 167*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(signature.ECDSAP256KeyTemplate()) failed: %v", err) 168*e7b1675dSTing-Kang Chang } 169*e7b1675dSTing-Kang Chang 170*e7b1675dSTing-Kang Chang badKeyset, err := genKeyset(ctx, keysetService, template) 171*e7b1675dSTing-Kang Chang if err != nil { 172*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 173*e7b1675dSTing-Kang Chang } 174*e7b1675dSTing-Kang Chang 175*e7b1675dSTing-Kang Chang result, err := jwtService.CreateJwtMac(ctx, &pb.CreationRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: badKeyset}}) 176*e7b1675dSTing-Kang Chang if err != nil { 177*e7b1675dSTing-Kang Chang t.Fatalf("CreateJwtMac with bad keyset failed with gRPC error: %v", err) 178*e7b1675dSTing-Kang Chang } 179*e7b1675dSTing-Kang Chang if result.GetErr() == "" { 180*e7b1675dSTing-Kang Chang t.Fatalf("result.GetErr() of bad keyset after CreateJwtMac is empty, want not empty") 181*e7b1675dSTing-Kang Chang } 182*e7b1675dSTing-Kang Chang} 183*e7b1675dSTing-Kang Chang 184*e7b1675dSTing-Kang Changfunc TestJWTComputeMACWithInvalidKeysetFails(t *testing.T) { 185*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 186*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 187*e7b1675dSTing-Kang Chang ctx := context.Background() 188*e7b1675dSTing-Kang Chang template, err := proto.Marshal(aead.AES256GCMKeyTemplate()) 189*e7b1675dSTing-Kang Chang if err != nil { 190*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(jwt.AES256GCMKeyTemplate()) failed: %v", err) 191*e7b1675dSTing-Kang Chang } 192*e7b1675dSTing-Kang Chang keyset, err := genKeyset(ctx, keysetService, template) 193*e7b1675dSTing-Kang Chang if err != nil { 194*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 195*e7b1675dSTing-Kang Chang } 196*e7b1675dSTing-Kang Chang rawJWT := &pb.JwtToken{ 197*e7b1675dSTing-Kang Chang TypeHeader: &wpb.StringValue{Value: "JWT"}, 198*e7b1675dSTing-Kang Chang Issuer: &wpb.StringValue{Value: "issuer"}, 199*e7b1675dSTing-Kang Chang } 200*e7b1675dSTing-Kang Chang signResponse, err := jwtService.ComputeMacAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, RawJwt: rawJWT}) 201*e7b1675dSTing-Kang Chang if err != nil { 202*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.ComputeMacAndEncode() err = %v, want nil", err) 203*e7b1675dSTing-Kang Chang } 204*e7b1675dSTing-Kang Chang if _, err := signedCompactJWTFromResponse(signResponse); err == nil { 205*e7b1675dSTing-Kang Chang t.Fatalf("JwtSignResponse: error = nil, want error") 206*e7b1675dSTing-Kang Chang } 207*e7b1675dSTing-Kang Chang} 208*e7b1675dSTing-Kang Chang 209*e7b1675dSTing-Kang Changfunc TestJWTComputeAndVerifyMac(t *testing.T) { 210*e7b1675dSTing-Kang Chang for _, tc := range []jwtTestCase{ 211*e7b1675dSTing-Kang Chang { 212*e7b1675dSTing-Kang Chang tag: "all claims and custom claims", 213*e7b1675dSTing-Kang Chang rawJWT: &pb.JwtToken{ 214*e7b1675dSTing-Kang Chang TypeHeader: &wpb.StringValue{Value: "JWT"}, 215*e7b1675dSTing-Kang Chang Issuer: &wpb.StringValue{Value: "issuer"}, 216*e7b1675dSTing-Kang Chang Subject: &wpb.StringValue{Value: "subject"}, 217*e7b1675dSTing-Kang Chang JwtId: &wpb.StringValue{Value: "tink"}, 218*e7b1675dSTing-Kang Chang Audiences: []string{"audience"}, 219*e7b1675dSTing-Kang Chang Expiration: &tpb.Timestamp{Seconds: 123456}, 220*e7b1675dSTing-Kang Chang NotBefore: &tpb.Timestamp{Seconds: 12345}, 221*e7b1675dSTing-Kang Chang IssuedAt: &tpb.Timestamp{Seconds: 1234}, 222*e7b1675dSTing-Kang Chang CustomClaims: map[string]*pb.JwtClaimValue{ 223*e7b1675dSTing-Kang Chang "cc-null": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_NullValue{}}, 224*e7b1675dSTing-Kang Chang "cc-num": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_NumberValue{NumberValue: 5.67}}, 225*e7b1675dSTing-Kang Chang "cc-bool": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_BoolValue{BoolValue: true}}, 226*e7b1675dSTing-Kang Chang "cc-string": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_StringValue{StringValue: "foo bar"}}, 227*e7b1675dSTing-Kang Chang "cc-array": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_JsonArrayValue{JsonArrayValue: "[35]"}}, 228*e7b1675dSTing-Kang Chang "cc-object": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_JsonObjectValue{JsonObjectValue: `{"key":"val"}`}}, 229*e7b1675dSTing-Kang Chang }, 230*e7b1675dSTing-Kang Chang }, 231*e7b1675dSTing-Kang Chang validator: &pb.JwtValidator{ 232*e7b1675dSTing-Kang Chang ExpectedTypeHeader: &wpb.StringValue{Value: "JWT"}, 233*e7b1675dSTing-Kang Chang ExpectedIssuer: &wpb.StringValue{Value: "issuer"}, 234*e7b1675dSTing-Kang Chang ExpectedAudience: &wpb.StringValue{Value: "audience"}, 235*e7b1675dSTing-Kang Chang Now: &tpb.Timestamp{Seconds: 12345}, 236*e7b1675dSTing-Kang Chang ClockSkew: &dpb.Duration{Seconds: 0}, 237*e7b1675dSTing-Kang Chang }, 238*e7b1675dSTing-Kang Chang }, 239*e7b1675dSTing-Kang Chang { 240*e7b1675dSTing-Kang Chang tag: "without custom claims", 241*e7b1675dSTing-Kang Chang rawJWT: &pb.JwtToken{ 242*e7b1675dSTing-Kang Chang TypeHeader: &wpb.StringValue{Value: "JWT"}, 243*e7b1675dSTing-Kang Chang Issuer: &wpb.StringValue{Value: "issuer"}, 244*e7b1675dSTing-Kang Chang Subject: &wpb.StringValue{Value: "subject"}, 245*e7b1675dSTing-Kang Chang Audiences: []string{"audience"}, 246*e7b1675dSTing-Kang Chang }, 247*e7b1675dSTing-Kang Chang validator: &pb.JwtValidator{ 248*e7b1675dSTing-Kang Chang ExpectedTypeHeader: &wpb.StringValue{Value: "JWT"}, 249*e7b1675dSTing-Kang Chang ExpectedIssuer: &wpb.StringValue{Value: "issuer"}, 250*e7b1675dSTing-Kang Chang ExpectedAudience: &wpb.StringValue{Value: "audience"}, 251*e7b1675dSTing-Kang Chang AllowMissingExpiration: true, 252*e7b1675dSTing-Kang Chang }, 253*e7b1675dSTing-Kang Chang }, 254*e7b1675dSTing-Kang Chang { 255*e7b1675dSTing-Kang Chang tag: "without expiration", 256*e7b1675dSTing-Kang Chang rawJWT: &pb.JwtToken{ 257*e7b1675dSTing-Kang Chang Subject: &wpb.StringValue{Value: "subject"}, 258*e7b1675dSTing-Kang Chang }, 259*e7b1675dSTing-Kang Chang validator: &pb.JwtValidator{ 260*e7b1675dSTing-Kang Chang AllowMissingExpiration: true, 261*e7b1675dSTing-Kang Chang }, 262*e7b1675dSTing-Kang Chang }, 263*e7b1675dSTing-Kang Chang { 264*e7b1675dSTing-Kang Chang tag: "clock skew", 265*e7b1675dSTing-Kang Chang rawJWT: &pb.JwtToken{ 266*e7b1675dSTing-Kang Chang Expiration: &tpb.Timestamp{Seconds: 1234}, 267*e7b1675dSTing-Kang Chang }, 268*e7b1675dSTing-Kang Chang validator: &pb.JwtValidator{ 269*e7b1675dSTing-Kang Chang Now: &tpb.Timestamp{Seconds: 1235}, 270*e7b1675dSTing-Kang Chang ClockSkew: &dpb.Duration{Seconds: 2}, 271*e7b1675dSTing-Kang Chang }, 272*e7b1675dSTing-Kang Chang }, 273*e7b1675dSTing-Kang Chang } { 274*e7b1675dSTing-Kang Chang t.Run(tc.tag, func(t *testing.T) { 275*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 276*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 277*e7b1675dSTing-Kang Chang ctx := context.Background() 278*e7b1675dSTing-Kang Chang template, err := proto.Marshal(jwt.HS256Template()) 279*e7b1675dSTing-Kang Chang if err != nil { 280*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(jwt.HS256Template()) failed: %v", err) 281*e7b1675dSTing-Kang Chang } 282*e7b1675dSTing-Kang Chang keyset, err := genKeyset(ctx, keysetService, template) 283*e7b1675dSTing-Kang Chang if err != nil { 284*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 285*e7b1675dSTing-Kang Chang } 286*e7b1675dSTing-Kang Chang 287*e7b1675dSTing-Kang Chang signResponse, err := jwtService.ComputeMacAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, RawJwt: tc.rawJWT}) 288*e7b1675dSTing-Kang Chang if err != nil { 289*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.ComputeMacAndEncode() err = %v, want nil", err) 290*e7b1675dSTing-Kang Chang } 291*e7b1675dSTing-Kang Chang compact, err := signedCompactJWTFromResponse(signResponse) 292*e7b1675dSTing-Kang Chang if err != nil { 293*e7b1675dSTing-Kang Chang t.Fatalf("JwtSignResponse_Err: %v", err) 294*e7b1675dSTing-Kang Chang } 295*e7b1675dSTing-Kang Chang verifyResponse, err := jwtService.VerifyMacAndDecode(ctx, &pb.JwtVerifyRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, SignedCompactJwt: compact, Validator: tc.validator}) 296*e7b1675dSTing-Kang Chang if err != nil { 297*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.VerifyMacAndDecode() err = %v, want nil", err) 298*e7b1675dSTing-Kang Chang } 299*e7b1675dSTing-Kang Chang verifiedJWT, err := verifiedJWTFromResponse(verifyResponse) 300*e7b1675dSTing-Kang Chang if err != nil { 301*e7b1675dSTing-Kang Chang t.Fatalf("JwtVerifyResponse_Err: %v", err) 302*e7b1675dSTing-Kang Chang } 303*e7b1675dSTing-Kang Chang if !cmp.Equal(verifiedJWT, tc.rawJWT, protocmp.Transform()) { 304*e7b1675dSTing-Kang Chang t.Errorf("verifiedJWT doesn't match expected value: (+ got, - want) %v", cmp.Diff(verifiedJWT, tc.rawJWT, protocmp.Transform())) 305*e7b1675dSTing-Kang Chang } 306*e7b1675dSTing-Kang Chang }) 307*e7b1675dSTing-Kang Chang } 308*e7b1675dSTing-Kang Chang} 309*e7b1675dSTing-Kang Chang 310*e7b1675dSTing-Kang Changfunc TestJWTVerifyMACFailures(t *testing.T) { 311*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 312*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 313*e7b1675dSTing-Kang Chang ctx := context.Background() 314*e7b1675dSTing-Kang Chang template, err := proto.Marshal(jwt.HS256Template()) 315*e7b1675dSTing-Kang Chang if err != nil { 316*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(jwt.HS256Template()) failed: %v", err) 317*e7b1675dSTing-Kang Chang } 318*e7b1675dSTing-Kang Chang keyset, err := genKeyset(ctx, keysetService, template) 319*e7b1675dSTing-Kang Chang if err != nil { 320*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 321*e7b1675dSTing-Kang Chang } 322*e7b1675dSTing-Kang Chang rawJWT := &pb.JwtToken{ 323*e7b1675dSTing-Kang Chang TypeHeader: &wpb.StringValue{Value: "JWT"}, 324*e7b1675dSTing-Kang Chang Expiration: &tpb.Timestamp{Seconds: 123456}, 325*e7b1675dSTing-Kang Chang NotBefore: &tpb.Timestamp{Seconds: 12345}, 326*e7b1675dSTing-Kang Chang IssuedAt: &tpb.Timestamp{Seconds: 1234}, 327*e7b1675dSTing-Kang Chang } 328*e7b1675dSTing-Kang Chang signResponse, err := jwtService.ComputeMacAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, RawJwt: rawJWT}) 329*e7b1675dSTing-Kang Chang if err != nil { 330*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.ComputeMacAndEncode() err = %v, want nil", err) 331*e7b1675dSTing-Kang Chang } 332*e7b1675dSTing-Kang Chang compact, err := signedCompactJWTFromResponse(signResponse) 333*e7b1675dSTing-Kang Chang if err != nil { 334*e7b1675dSTing-Kang Chang t.Fatalf("JwtSignResponse_Err: %v", err) 335*e7b1675dSTing-Kang Chang } 336*e7b1675dSTing-Kang Chang validator := &pb.JwtValidator{ 337*e7b1675dSTing-Kang Chang ExpectedTypeHeader: &wpb.StringValue{Value: "JWT"}, 338*e7b1675dSTing-Kang Chang Now: &tpb.Timestamp{Seconds: 12345}, 339*e7b1675dSTing-Kang Chang } 340*e7b1675dSTing-Kang Chang verifyResponse, err := jwtService.VerifyMacAndDecode(ctx, &pb.JwtVerifyRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, SignedCompactJwt: compact, Validator: validator}) 341*e7b1675dSTing-Kang Chang if err != nil { 342*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.VerifyMacAndDecode() err = %v, want nil", err) 343*e7b1675dSTing-Kang Chang } 344*e7b1675dSTing-Kang Chang if _, err := verifiedJWTFromResponse(verifyResponse); err != nil { 345*e7b1675dSTing-Kang Chang t.Fatalf("JwtVerifyResponse_Err: %v", err) 346*e7b1675dSTing-Kang Chang } 347*e7b1675dSTing-Kang Chang for _, tc := range []jwtTestCase{ 348*e7b1675dSTing-Kang Chang { 349*e7b1675dSTing-Kang Chang tag: "unexpected type header", 350*e7b1675dSTing-Kang Chang validator: &pb.JwtValidator{ 351*e7b1675dSTing-Kang Chang ExpectedTypeHeader: &wpb.StringValue{Value: "unexpected"}, 352*e7b1675dSTing-Kang Chang Now: &tpb.Timestamp{Seconds: 12345}, 353*e7b1675dSTing-Kang Chang }, 354*e7b1675dSTing-Kang Chang }, 355*e7b1675dSTing-Kang Chang { 356*e7b1675dSTing-Kang Chang tag: "expired token", 357*e7b1675dSTing-Kang Chang validator: &pb.JwtValidator{ 358*e7b1675dSTing-Kang Chang ExpectedTypeHeader: &wpb.StringValue{Value: "JWT"}, 359*e7b1675dSTing-Kang Chang Now: &tpb.Timestamp{Seconds: 999999999999}, 360*e7b1675dSTing-Kang Chang }, 361*e7b1675dSTing-Kang Chang }, 362*e7b1675dSTing-Kang Chang { 363*e7b1675dSTing-Kang Chang tag: "expect issued in the past", 364*e7b1675dSTing-Kang Chang validator: &pb.JwtValidator{ 365*e7b1675dSTing-Kang Chang ExpectedTypeHeader: &wpb.StringValue{Value: "JWT"}, 366*e7b1675dSTing-Kang Chang Now: &tpb.Timestamp{Seconds: 1233}, 367*e7b1675dSTing-Kang Chang ExpectIssuedInThePast: true, 368*e7b1675dSTing-Kang Chang }, 369*e7b1675dSTing-Kang Chang }, 370*e7b1675dSTing-Kang Chang } { 371*e7b1675dSTing-Kang Chang t.Run(tc.tag, func(t *testing.T) { 372*e7b1675dSTing-Kang Chang verifyResponse, err := jwtService.VerifyMacAndDecode(ctx, &pb.JwtVerifyRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: keyset}, SignedCompactJwt: compact, Validator: tc.validator}) 373*e7b1675dSTing-Kang Chang if err != nil { 374*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.VerifyMacAndDecode() err = %v, want nil", err) 375*e7b1675dSTing-Kang Chang } 376*e7b1675dSTing-Kang Chang if _, err := verifiedJWTFromResponse(verifyResponse); err == nil { 377*e7b1675dSTing-Kang Chang t.Fatalf("JwtVerifyResponse_Err: nil, want error") 378*e7b1675dSTing-Kang Chang } 379*e7b1675dSTing-Kang Chang }) 380*e7b1675dSTing-Kang Chang } 381*e7b1675dSTing-Kang Chang} 382*e7b1675dSTing-Kang Chang 383*e7b1675dSTing-Kang Changfunc TestSuccessfulJwtSignVerifyCreation(t *testing.T) { 384*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 385*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 386*e7b1675dSTing-Kang Chang ctx := context.Background() 387*e7b1675dSTing-Kang Chang 388*e7b1675dSTing-Kang Chang template, err := proto.Marshal(jwt.ES256Template()) 389*e7b1675dSTing-Kang Chang if err != nil { 390*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(hybrid.ES256Template()) failed: %v", err) 391*e7b1675dSTing-Kang Chang } 392*e7b1675dSTing-Kang Chang 393*e7b1675dSTing-Kang Chang privateKeyset, err := genKeyset(ctx, keysetService, template) 394*e7b1675dSTing-Kang Chang if err != nil { 395*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 396*e7b1675dSTing-Kang Chang } 397*e7b1675dSTing-Kang Chang 398*e7b1675dSTing-Kang Chang result, err := jwtService.CreateJwtPublicKeySign(ctx, &pb.CreationRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: privateKeyset}}) 399*e7b1675dSTing-Kang Chang if err != nil { 400*e7b1675dSTing-Kang Chang t.Fatalf("CreateJwtPublicKeySign with good keyset failed with gRPC error: %v, want nil", err) 401*e7b1675dSTing-Kang Chang } 402*e7b1675dSTing-Kang Chang if result.GetErr() != "" { 403*e7b1675dSTing-Kang Chang t.Fatalf("CreateJwtPublicKeySign with good keyset failed with result.GetErr() = %q, want empty string", result.GetErr()) 404*e7b1675dSTing-Kang Chang } 405*e7b1675dSTing-Kang Chang} 406*e7b1675dSTing-Kang Chang 407*e7b1675dSTing-Kang Changfunc TestSuccessfulJwtVerifyCreation(t *testing.T) { 408*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 409*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 410*e7b1675dSTing-Kang Chang ctx := context.Background() 411*e7b1675dSTing-Kang Chang 412*e7b1675dSTing-Kang Chang template, err := proto.Marshal(jwt.ES256Template()) 413*e7b1675dSTing-Kang Chang if err != nil { 414*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(hybrid.ES256Template()) failed: %v", err) 415*e7b1675dSTing-Kang Chang } 416*e7b1675dSTing-Kang Chang 417*e7b1675dSTing-Kang Chang privateKeyset, err := genKeyset(ctx, keysetService, template) 418*e7b1675dSTing-Kang Chang if err != nil { 419*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 420*e7b1675dSTing-Kang Chang } 421*e7b1675dSTing-Kang Chang publicKeyset, err := pubKeyset(ctx, keysetService, privateKeyset) 422*e7b1675dSTing-Kang Chang if err != nil { 423*e7b1675dSTing-Kang Chang t.Fatalf("pubKeyset failed: %v", err) 424*e7b1675dSTing-Kang Chang } 425*e7b1675dSTing-Kang Chang 426*e7b1675dSTing-Kang Chang result, err := jwtService.CreateJwtPublicKeyVerify(ctx, &pb.CreationRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: publicKeyset}}) 427*e7b1675dSTing-Kang Chang if err != nil { 428*e7b1675dSTing-Kang Chang t.Fatalf("CreateJwtPublicKeyVerify with good keyset failed with gRPC error: %v", err) 429*e7b1675dSTing-Kang Chang } 430*e7b1675dSTing-Kang Chang if result.GetErr() != "" { 431*e7b1675dSTing-Kang Chang t.Fatalf("CreateJwtPublicKeyVerify with good keyset failed with result.GetErr() = %q, want empty string", result.GetErr()) 432*e7b1675dSTing-Kang Chang } 433*e7b1675dSTing-Kang Chang} 434*e7b1675dSTing-Kang Chang 435*e7b1675dSTing-Kang Changfunc TestFailingJwtSignCreation(t *testing.T) { 436*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 437*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 438*e7b1675dSTing-Kang Chang ctx := context.Background() 439*e7b1675dSTing-Kang Chang 440*e7b1675dSTing-Kang Chang // We use signature keys -- then we cannot create a hybrid encrypt 441*e7b1675dSTing-Kang Chang template, err := proto.Marshal(signature.ECDSAP256KeyTemplate()) 442*e7b1675dSTing-Kang Chang if err != nil { 443*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(signature.ECDSAP256KeyTemplate()) failed: %v", err) 444*e7b1675dSTing-Kang Chang } 445*e7b1675dSTing-Kang Chang 446*e7b1675dSTing-Kang Chang privateKeyset, err := genKeyset(ctx, keysetService, template) 447*e7b1675dSTing-Kang Chang if err != nil { 448*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 449*e7b1675dSTing-Kang Chang } 450*e7b1675dSTing-Kang Chang 451*e7b1675dSTing-Kang Chang result, err := jwtService.CreateJwtPublicKeySign(ctx, &pb.CreationRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: privateKeyset}}) 452*e7b1675dSTing-Kang Chang if err != nil { 453*e7b1675dSTing-Kang Chang t.Fatalf("CreateJwtPublicKeySign with bad keyset failed with gRPC error: %v", err) 454*e7b1675dSTing-Kang Chang } 455*e7b1675dSTing-Kang Chang if result.GetErr() == "" { 456*e7b1675dSTing-Kang Chang t.Fatalf("CreateJwtPublicKeySign with bad keyset succeeded") 457*e7b1675dSTing-Kang Chang } 458*e7b1675dSTing-Kang Chang} 459*e7b1675dSTing-Kang Chang 460*e7b1675dSTing-Kang Changfunc TestFailingJwtVerifyCreation(t *testing.T) { 461*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 462*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 463*e7b1675dSTing-Kang Chang ctx := context.Background() 464*e7b1675dSTing-Kang Chang 465*e7b1675dSTing-Kang Chang // We use signature keys -- then we cannot create a hybrid encrypt 466*e7b1675dSTing-Kang Chang template, err := proto.Marshal(signature.ECDSAP256KeyTemplate()) 467*e7b1675dSTing-Kang Chang if err != nil { 468*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(signature.ECDSAP256KeyTemplate()) failed: %v", err) 469*e7b1675dSTing-Kang Chang } 470*e7b1675dSTing-Kang Chang 471*e7b1675dSTing-Kang Chang privateKeyset, err := genKeyset(ctx, keysetService, template) 472*e7b1675dSTing-Kang Chang if err != nil { 473*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 474*e7b1675dSTing-Kang Chang } 475*e7b1675dSTing-Kang Chang publicKeyset, err := pubKeyset(ctx, keysetService, privateKeyset) 476*e7b1675dSTing-Kang Chang if err != nil { 477*e7b1675dSTing-Kang Chang t.Fatalf("pubKeyset failed: %v", err) 478*e7b1675dSTing-Kang Chang } 479*e7b1675dSTing-Kang Chang 480*e7b1675dSTing-Kang Chang result, err := jwtService.CreateJwtPublicKeyVerify(ctx, &pb.CreationRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: publicKeyset}}) 481*e7b1675dSTing-Kang Chang if err != nil { 482*e7b1675dSTing-Kang Chang t.Fatalf("CreateJwtPublicKeyVerify with good keyset failed with gRPC error: %v", err) 483*e7b1675dSTing-Kang Chang } 484*e7b1675dSTing-Kang Chang if result.GetErr() == "" { 485*e7b1675dSTing-Kang Chang t.Fatalf("CreateJwtPublicKeyVerify with bad keyset succeeded") 486*e7b1675dSTing-Kang Chang } 487*e7b1675dSTing-Kang Chang} 488*e7b1675dSTing-Kang Chang 489*e7b1675dSTing-Kang Changfunc TestJWTPublicKeySignWithInvalidKeysetFails(t *testing.T) { 490*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 491*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 492*e7b1675dSTing-Kang Chang 493*e7b1675dSTing-Kang Chang ctx := context.Background() 494*e7b1675dSTing-Kang Chang template, err := proto.Marshal(aead.AES256GCMKeyTemplate()) 495*e7b1675dSTing-Kang Chang if err != nil { 496*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(aead.AES256GCMKeyTemplate()) failed: %v", err) 497*e7b1675dSTing-Kang Chang } 498*e7b1675dSTing-Kang Chang privateKeyset, err := genKeyset(ctx, keysetService, template) 499*e7b1675dSTing-Kang Chang if err != nil { 500*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 501*e7b1675dSTing-Kang Chang } 502*e7b1675dSTing-Kang Chang rawJWT := &pb.JwtToken{ 503*e7b1675dSTing-Kang Chang Subject: &wpb.StringValue{Value: "tink-subject"}, 504*e7b1675dSTing-Kang Chang } 505*e7b1675dSTing-Kang Chang signResponse, err := jwtService.PublicKeySignAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: privateKeyset}, RawJwt: rawJWT}) 506*e7b1675dSTing-Kang Chang if err != nil { 507*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.PublicKeySignAndEncode() err = %v", err) 508*e7b1675dSTing-Kang Chang } 509*e7b1675dSTing-Kang Chang if _, err := signedCompactJWTFromResponse(signResponse); err == nil { 510*e7b1675dSTing-Kang Chang t.Fatalf("JwtSignResponse_Err: nil want error") 511*e7b1675dSTing-Kang Chang } 512*e7b1675dSTing-Kang Chang} 513*e7b1675dSTing-Kang Chang 514*e7b1675dSTing-Kang Changfunc TestJWTPublicKeySignInvalidTokenFails(t *testing.T) { 515*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 516*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 517*e7b1675dSTing-Kang Chang 518*e7b1675dSTing-Kang Chang ctx := context.Background() 519*e7b1675dSTing-Kang Chang template, err := proto.Marshal(jwt.ES256Template()) 520*e7b1675dSTing-Kang Chang if err != nil { 521*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(jwt.ES256Template()) failed: %v", err) 522*e7b1675dSTing-Kang Chang } 523*e7b1675dSTing-Kang Chang privateKeyset, err := genKeyset(ctx, keysetService, template) 524*e7b1675dSTing-Kang Chang if err != nil { 525*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 526*e7b1675dSTing-Kang Chang } 527*e7b1675dSTing-Kang Chang for _, tc := range []jwtTestCase{ 528*e7b1675dSTing-Kang Chang { 529*e7b1675dSTing-Kang Chang tag: "nil rawJWT", 530*e7b1675dSTing-Kang Chang rawJWT: nil, 531*e7b1675dSTing-Kang Chang }, 532*e7b1675dSTing-Kang Chang { 533*e7b1675dSTing-Kang Chang tag: "invalid json array string", 534*e7b1675dSTing-Kang Chang rawJWT: &pb.JwtToken{ 535*e7b1675dSTing-Kang Chang CustomClaims: map[string]*pb.JwtClaimValue{ 536*e7b1675dSTing-Kang Chang "cc-array": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_JsonArrayValue{JsonArrayValue: "{35}"}}, 537*e7b1675dSTing-Kang Chang }, 538*e7b1675dSTing-Kang Chang }, 539*e7b1675dSTing-Kang Chang }, 540*e7b1675dSTing-Kang Chang { 541*e7b1675dSTing-Kang Chang tag: "invalid json object string", 542*e7b1675dSTing-Kang Chang rawJWT: &pb.JwtToken{ 543*e7b1675dSTing-Kang Chang CustomClaims: map[string]*pb.JwtClaimValue{ 544*e7b1675dSTing-Kang Chang "cc-object": &pb.JwtClaimValue{Kind: &pb.JwtClaimValue_JsonObjectValue{JsonObjectValue: `["o":"a"]`}}, 545*e7b1675dSTing-Kang Chang }, 546*e7b1675dSTing-Kang Chang }, 547*e7b1675dSTing-Kang Chang }, 548*e7b1675dSTing-Kang Chang } { 549*e7b1675dSTing-Kang Chang t.Run(tc.tag, func(t *testing.T) { 550*e7b1675dSTing-Kang Chang signResponse, err := jwtService.PublicKeySignAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: privateKeyset}, RawJwt: tc.rawJWT}) 551*e7b1675dSTing-Kang Chang if err != nil { 552*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.PublicKeySignAndEncode() err = %v", err) 553*e7b1675dSTing-Kang Chang } 554*e7b1675dSTing-Kang Chang if _, err := signedCompactJWTFromResponse(signResponse); err == nil { 555*e7b1675dSTing-Kang Chang t.Fatalf("JwtSignResponse_Err: nil want error") 556*e7b1675dSTing-Kang Chang } 557*e7b1675dSTing-Kang Chang }) 558*e7b1675dSTing-Kang Chang } 559*e7b1675dSTing-Kang Chang} 560*e7b1675dSTing-Kang Chang 561*e7b1675dSTing-Kang Changfunc TestJWTPublicKeyVerifyFails(t *testing.T) { 562*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 563*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 564*e7b1675dSTing-Kang Chang 565*e7b1675dSTing-Kang Chang ctx := context.Background() 566*e7b1675dSTing-Kang Chang template, err := proto.Marshal(jwt.ES256Template()) 567*e7b1675dSTing-Kang Chang if err != nil { 568*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(jwt.ES256Template()) failed: %v", err) 569*e7b1675dSTing-Kang Chang } 570*e7b1675dSTing-Kang Chang privateKeyset, err := genKeyset(ctx, keysetService, template) 571*e7b1675dSTing-Kang Chang if err != nil { 572*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 573*e7b1675dSTing-Kang Chang } 574*e7b1675dSTing-Kang Chang publicKeyset, err := pubKeyset(ctx, keysetService, privateKeyset) 575*e7b1675dSTing-Kang Chang if err != nil { 576*e7b1675dSTing-Kang Chang t.Fatalf("pubKeyset failed: %v", err) 577*e7b1675dSTing-Kang Chang } 578*e7b1675dSTing-Kang Chang rawJWT := &pb.JwtToken{ 579*e7b1675dSTing-Kang Chang Subject: &wpb.StringValue{Value: "tink-subject"}, 580*e7b1675dSTing-Kang Chang } 581*e7b1675dSTing-Kang Chang signResponse, err := jwtService.PublicKeySignAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: privateKeyset}, RawJwt: rawJWT}) 582*e7b1675dSTing-Kang Chang if err != nil { 583*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.PublicKeySignAndEncode() err = %v", err) 584*e7b1675dSTing-Kang Chang } 585*e7b1675dSTing-Kang Chang compact, err := signedCompactJWTFromResponse(signResponse) 586*e7b1675dSTing-Kang Chang if err != nil { 587*e7b1675dSTing-Kang Chang t.Fatalf("JwtSignResponse_Err failed: %v", err) 588*e7b1675dSTing-Kang Chang } 589*e7b1675dSTing-Kang Chang validator := &pb.JwtValidator{ 590*e7b1675dSTing-Kang Chang ExpectedTypeHeader: &wpb.StringValue{Value: "JWT"}, 591*e7b1675dSTing-Kang Chang } 592*e7b1675dSTing-Kang Chang verifyResponse, err := jwtService.PublicKeyVerifyAndDecode(ctx, &pb.JwtVerifyRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: publicKeyset}, SignedCompactJwt: compact, Validator: validator}) 593*e7b1675dSTing-Kang Chang if err != nil { 594*e7b1675dSTing-Kang Chang t.Fatalf("jwtVerifySignature failed: %v", err) 595*e7b1675dSTing-Kang Chang } 596*e7b1675dSTing-Kang Chang if _, err := verifiedJWTFromResponse(verifyResponse); err == nil { 597*e7b1675dSTing-Kang Chang t.Fatalf("JwtVerifyResponse_Err: nil want error") 598*e7b1675dSTing-Kang Chang } 599*e7b1675dSTing-Kang Chang} 600*e7b1675dSTing-Kang Chang 601*e7b1675dSTing-Kang Changfunc TestJWTPublicKeySignAndEncodeVerifyAndDecode(t *testing.T) { 602*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 603*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 604*e7b1675dSTing-Kang Chang 605*e7b1675dSTing-Kang Chang ctx := context.Background() 606*e7b1675dSTing-Kang Chang template, err := proto.Marshal(jwt.ES256Template()) 607*e7b1675dSTing-Kang Chang if err != nil { 608*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(jwt.ES256Template()) failed: %v", err) 609*e7b1675dSTing-Kang Chang } 610*e7b1675dSTing-Kang Chang privateKeyset, err := genKeyset(ctx, keysetService, template) 611*e7b1675dSTing-Kang Chang if err != nil { 612*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 613*e7b1675dSTing-Kang Chang } 614*e7b1675dSTing-Kang Chang publicKeyset, err := pubKeyset(ctx, keysetService, privateKeyset) 615*e7b1675dSTing-Kang Chang if err != nil { 616*e7b1675dSTing-Kang Chang t.Fatalf("pubKeyset failed: %v", err) 617*e7b1675dSTing-Kang Chang } 618*e7b1675dSTing-Kang Chang rawJWT := &pb.JwtToken{ 619*e7b1675dSTing-Kang Chang Subject: &wpb.StringValue{Value: "tink-subject"}, 620*e7b1675dSTing-Kang Chang } 621*e7b1675dSTing-Kang Chang signResponse, err := jwtService.PublicKeySignAndEncode(ctx, &pb.JwtSignRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: privateKeyset}, RawJwt: rawJWT}) 622*e7b1675dSTing-Kang Chang if err != nil { 623*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.PublicKeySignAndEncode() err = %v", err) 624*e7b1675dSTing-Kang Chang } 625*e7b1675dSTing-Kang Chang compact, err := signedCompactJWTFromResponse(signResponse) 626*e7b1675dSTing-Kang Chang if err != nil { 627*e7b1675dSTing-Kang Chang t.Fatalf("JwtSignResponse_Err failed: %v", err) 628*e7b1675dSTing-Kang Chang } 629*e7b1675dSTing-Kang Chang validator := &pb.JwtValidator{ 630*e7b1675dSTing-Kang Chang AllowMissingExpiration: true, 631*e7b1675dSTing-Kang Chang } 632*e7b1675dSTing-Kang Chang verifyResponse, err := jwtService.PublicKeyVerifyAndDecode(ctx, &pb.JwtVerifyRequest{AnnotatedKeyset: &pb.AnnotatedKeyset{SerializedKeyset: publicKeyset}, SignedCompactJwt: compact, Validator: validator}) 633*e7b1675dSTing-Kang Chang if err != nil { 634*e7b1675dSTing-Kang Chang t.Fatalf("jwtVerifySignature failed: %v", err) 635*e7b1675dSTing-Kang Chang } 636*e7b1675dSTing-Kang Chang verifiedJWT, err := verifiedJWTFromResponse(verifyResponse) 637*e7b1675dSTing-Kang Chang if err != nil { 638*e7b1675dSTing-Kang Chang t.Fatalf("JwtVerifyResponse_Err: %v", err) 639*e7b1675dSTing-Kang Chang } 640*e7b1675dSTing-Kang Chang if !cmp.Equal(verifiedJWT, rawJWT, protocmp.Transform()) { 641*e7b1675dSTing-Kang Chang t.Errorf("verifiedJWT doesn't match expected value: (+ got, - want) %v", cmp.Diff(verifiedJWT, rawJWT, protocmp.Transform())) 642*e7b1675dSTing-Kang Chang } 643*e7b1675dSTing-Kang Chang} 644*e7b1675dSTing-Kang Chang 645*e7b1675dSTing-Kang Changfunc TestToJwkSetWithPrivateKeyFails(t *testing.T) { 646*e7b1675dSTing-Kang Chang keysetService := &services.KeysetService{} 647*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 648*e7b1675dSTing-Kang Chang 649*e7b1675dSTing-Kang Chang ctx := context.Background() 650*e7b1675dSTing-Kang Chang template, err := proto.Marshal(jwt.ES256Template()) 651*e7b1675dSTing-Kang Chang if err != nil { 652*e7b1675dSTing-Kang Chang t.Fatalf("proto.Marshal(jwt.ES256Template()) failed: %v", err) 653*e7b1675dSTing-Kang Chang } 654*e7b1675dSTing-Kang Chang privateKeyset, err := genKeyset(ctx, keysetService, template) 655*e7b1675dSTing-Kang Chang if err != nil { 656*e7b1675dSTing-Kang Chang t.Fatalf("genKeyset failed: %v", err) 657*e7b1675dSTing-Kang Chang } 658*e7b1675dSTing-Kang Chang toJWKResponse, err := jwtService.ToJwkSet(ctx, &pb.JwtToJwkSetRequest{Keyset: privateKeyset}) 659*e7b1675dSTing-Kang Chang if err != nil { 660*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.ToJwkSet() err = %v, want nil", err) 661*e7b1675dSTing-Kang Chang } 662*e7b1675dSTing-Kang Chang if _, err := jwkSetFromResponse(toJWKResponse); err == nil { 663*e7b1675dSTing-Kang Chang t.Fatalf("JwtToJwkSetResponse_Err: = nil, want error") 664*e7b1675dSTing-Kang Chang } 665*e7b1675dSTing-Kang Chang} 666*e7b1675dSTing-Kang Chang 667*e7b1675dSTing-Kang Changfunc TestFromJwkSetPrivateKeyFails(t *testing.T) { 668*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 669*e7b1675dSTing-Kang Chang ctx := context.Background() 670*e7b1675dSTing-Kang Chang jwkES256PublicKey := `{ 671*e7b1675dSTing-Kang Chang "keys":[{ 672*e7b1675dSTing-Kang Chang "kty":"EC", 673*e7b1675dSTing-Kang Chang "crv":"P-256", 674*e7b1675dSTing-Kang Chang "x":"wO6uIxh8SkKOO8VjZXNRTteRcwCPE4_4JElKyaa0fcQ", 675*e7b1675dSTing-Kang Chang "y":"7oRiYhnmkP6nqrdXWgtsWUWq5uFRLJkhyVFiWPRB278", 676*e7b1675dSTing-Kang Chang "d":"8oRinhnmkYjkqrdXWgtsWUWq5uFRLJkhyVFiWPRB278", 677*e7b1675dSTing-Kang Chang "use":"sig","alg":"ES256","key_ops":["verify"], 678*e7b1675dSTing-Kang Chang "kid":"EhuduQ"}] 679*e7b1675dSTing-Kang Chang }` 680*e7b1675dSTing-Kang Chang fromJWKResponse, err := jwtService.FromJwkSet(ctx, &pb.JwtFromJwkSetRequest{JwkSet: jwkES256PublicKey}) 681*e7b1675dSTing-Kang Chang if err != nil { 682*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.FromJwkSet() err = %v, want nil", err) 683*e7b1675dSTing-Kang Chang } 684*e7b1675dSTing-Kang Chang if _, err := keysetFromResponse(fromJWKResponse); err == nil { 685*e7b1675dSTing-Kang Chang t.Fatalf("JwtFromJwkSetResponse_Err = nil, want error") 686*e7b1675dSTing-Kang Chang } 687*e7b1675dSTing-Kang Chang} 688*e7b1675dSTing-Kang Chang 689*e7b1675dSTing-Kang Changfunc TestFromJwkToJwkSet(t *testing.T) { 690*e7b1675dSTing-Kang Chang jwtService := &services.JWTService{} 691*e7b1675dSTing-Kang Chang ctx := context.Background() 692*e7b1675dSTing-Kang Chang jwkES256PublicKey := `{ 693*e7b1675dSTing-Kang Chang "keys":[{ 694*e7b1675dSTing-Kang Chang "kty":"EC", 695*e7b1675dSTing-Kang Chang "crv":"P-256", 696*e7b1675dSTing-Kang Chang "x":"wO6uIxh8SkKOO8VjZXNRTteRcwCPE4_4JElKyaa0fcQ", 697*e7b1675dSTing-Kang Chang "y":"7oRiYhnmkP6nqrdXWgtsWUWq5uFRLJkhyVFiWPRB278", 698*e7b1675dSTing-Kang Chang "use":"sig","alg":"ES256","key_ops":["verify"], 699*e7b1675dSTing-Kang Chang "kid":"EhuduQ"}] 700*e7b1675dSTing-Kang Chang }` 701*e7b1675dSTing-Kang Chang fromJWKResponse, err := jwtService.FromJwkSet(ctx, &pb.JwtFromJwkSetRequest{JwkSet: jwkES256PublicKey}) 702*e7b1675dSTing-Kang Chang if err != nil { 703*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.FromJwkSet() err = %v, want nil", err) 704*e7b1675dSTing-Kang Chang } 705*e7b1675dSTing-Kang Chang ks, err := keysetFromResponse(fromJWKResponse) 706*e7b1675dSTing-Kang Chang if err != nil { 707*e7b1675dSTing-Kang Chang t.Fatalf("JwtFromJwkSetResponse_Err: = %v, want nil", err) 708*e7b1675dSTing-Kang Chang } 709*e7b1675dSTing-Kang Chang toJWKResponse, err := jwtService.ToJwkSet(ctx, &pb.JwtToJwkSetRequest{Keyset: ks}) 710*e7b1675dSTing-Kang Chang if err != nil { 711*e7b1675dSTing-Kang Chang t.Fatalf("jwtService.ToJwkSet() err = %v, want nil", err) 712*e7b1675dSTing-Kang Chang } 713*e7b1675dSTing-Kang Chang jwkSet, err := jwkSetFromResponse(toJWKResponse) 714*e7b1675dSTing-Kang Chang if err != nil { 715*e7b1675dSTing-Kang Chang t.Fatalf("JwtToJwkSetResponse_Err: = %v, want nil", err) 716*e7b1675dSTing-Kang Chang } 717*e7b1675dSTing-Kang Chang got := &spb.Struct{} 718*e7b1675dSTing-Kang Chang if err := got.UnmarshalJSON([]byte(jwkSet)); err != nil { 719*e7b1675dSTing-Kang Chang t.Fatalf("got.UnmarshalJSON() err = %v, want nil", err) 720*e7b1675dSTing-Kang Chang } 721*e7b1675dSTing-Kang Chang want := &spb.Struct{} 722*e7b1675dSTing-Kang Chang if err := want.UnmarshalJSON([]byte(jwkES256PublicKey)); err != nil { 723*e7b1675dSTing-Kang Chang t.Fatalf("want.UnmarshalJSON() err = %v, want nil", err) 724*e7b1675dSTing-Kang Chang } 725*e7b1675dSTing-Kang Chang if !cmp.Equal(want, got, protocmp.Transform()) { 726*e7b1675dSTing-Kang Chang t.Errorf("mismatch in jwk sets: diff (-want,+got): %v", cmp.Diff(want, got, protocmp.Transform())) 727*e7b1675dSTing-Kang Chang } 728*e7b1675dSTing-Kang Chang} 729