This commit is contained in:
2026-10-08 02:07:45 +07:00
parent 56bc009311
commit 0210a0ed15
6 changed files with 285 additions and 24 deletions
@@ -34,11 +34,17 @@ func NewAuthorizationInterceptor(
token := strings.TrimPrefix(values[0], "Bearer ")
if token == "" {
if publicMethods[info.FullMethod] {
return handler(ctx, req)
}
return nil, status.Errorf(codes.Unauthenticated, "invalid token format")
}
claims, err := processorJWT.GetClaims(token)
if err != nil {
if publicMethods[info.FullMethod] {
return handler(ctx, req)
}
return nil, status.Errorf(codes.Unauthenticated, "invalid token")
}
@@ -3,9 +3,13 @@ package processor_jwt
import (
"context"
"testing"
"time"
"github.com/golang-jwt/jwt/v5"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
type testProcessor struct{}
@@ -14,6 +18,53 @@ func (testProcessor) GetClaims(string) (*JWTClaims, error) {
return &JWTClaims{Roles: []string{"admin"}}, nil
}
func TestAuthorizationInterceptorHandlesExpiredTokens(t *testing.T) {
processor := NewProcessor("test-secret")
expired, err := jwt.NewWithClaims(jwt.SigningMethodHS256, &JWTClaims{
RegisteredClaims: jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(-time.Hour))},
}).SignedString([]byte("test-secret"))
if err != nil {
t.Fatal(err)
}
valid, err := processor.GenerateAccessToken(1, "test@example.invalid", []string{"admin"})
if err != nil {
t.Fatal(err)
}
for _, tc := range []struct {
name string
method string
token string
wantCode codes.Code
wantClaims bool
}{
{"public expired", "/public", expired, codes.OK, false},
{"public malformed", "/public", "invalid", codes.OK, false},
{"public empty", "/public", "", codes.OK, false},
{"public valid", "/public", valid, codes.OK, true},
{"protected expired", "/private", expired, codes.Unauthenticated, false},
{"protected malformed", "/private", "invalid", codes.Unauthenticated, false},
{"protected empty", "/private", "", codes.Unauthenticated, false},
{"protected valid", "/private", valid, codes.OK, true},
} {
t.Run(tc.name, func(t *testing.T) {
called := false
interceptor := NewAuthorizationInterceptor(map[string]bool{"/public": true}, processor)
ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "Bearer "+tc.token))
_, err := interceptor(ctx, nil, &grpc.UnaryServerInfo{FullMethod: tc.method}, func(ctx context.Context, _ any) (any, error) {
called = true
claims, ok := ClaimsFromContext(ctx)
if ok != tc.wantClaims || (ok && !claims.HasRole("admin")) {
t.Fatalf("unexpected claims: %#v", claims)
}
return nil, nil
})
if status.Code(err) != tc.wantCode || called != (tc.wantCode == codes.OK) {
t.Fatalf("called=%v, err=%v", called, err)
}
})
}
}
func (testProcessor) GenerateAccessToken(int, string, []string) (string, error) { return "", nil }
func (testProcessor) GenerateRefreshToken(int) (string, error) { return "", nil }