diff --git a/services-api/pkg/service/iam/auth.go b/services-api/pkg/service/iam/auth.go index 5db700d0c..71a0d3e76 100644 --- a/services-api/pkg/service/iam/auth.go +++ b/services-api/pkg/service/iam/auth.go @@ -538,7 +538,13 @@ func (o *OAuth20Service) ParseAccessTokenToClaims(accessToken string, validate b } } - errValidate := o.Validate(accessToken, perm, &claims.Namespace, nil) + namespace := claims.Namespace + + if claims.ExtendNamespace != "" { + namespace = claims.ExtendNamespace + } + + errValidate := o.Validate(accessToken, perm, &namespace, nil) if errValidate != nil { log.Fatalf("token validation failed: %s", errValidate.Error()) } diff --git a/services-api/pkg/service/iam/auth_validator.go b/services-api/pkg/service/iam/auth_validator.go index 23bcdc495..fb70d798e 100644 --- a/services-api/pkg/service/iam/auth_validator.go +++ b/services-api/pkg/service/iam/auth_validator.go @@ -104,6 +104,10 @@ func (v *TokenValidator) Validate(token string, permission *Permission, namespac } fmt.Println("token verified") + if errNamespace := v.hasValidNamespace(v.JwtClaims, namespace); errNamespace != nil { + return errors.New(errNamespace.Error()) + } + if !v.hasValidPermissions(v.JwtClaims, permission, namespace, userId) { return errors.New("insufficient permissions") } @@ -124,6 +128,10 @@ func (v *TokenValidator) Validate(token string, permission *Permission, namespac return errors.New("user was revoked") } + if errNamespace := v.hasValidNamespace(v.JwtClaims, namespace); errNamespace != nil { + return errors.New(errNamespace.Error()) + } + if !v.hasValidPermissions(v.JwtClaims, permission, namespace, userId) { return errors.New("insufficient permissions") } @@ -377,6 +385,20 @@ func (v *TokenValidator) hasValidPermissions(claims JWTClaims, permission *Permi return false } +func (v *TokenValidator) hasValidNamespace(claims JWTClaims, namespace *string) error { + if namespace == nil { + return fmt.Errorf("trying to validate access token against a namepace, but have an empty namespace") + } + + if claims.ExtendNamespace != "" { + if claims.ExtendNamespace != *namespace { + return fmt.Errorf("extend namespace from token has different a namespace with grpc server") + } + } + + return nil +} + func (v *TokenValidator) isTokenRevoked(token string) bool { return v.Filter.MightContain([]byte(token)) } diff --git a/services-api/pkg/service/iam/auth_validator_test.go b/services-api/pkg/service/iam/auth_validator_test.go index e02b7d623..cb9cd1cf3 100644 --- a/services-api/pkg/service/iam/auth_validator_test.go +++ b/services-api/pkg/service/iam/auth_validator_test.go @@ -6,8 +6,10 @@ package iam import ( "fmt" + "os" "testing" + "github.com/AccelByte/accelbyte-go-sdk/iam-sdk/pkg/iamclient/o_auth2_0" "github.com/AccelByte/accelbyte-go-sdk/services-api/pkg/factory" "github.com/AccelByte/accelbyte-go-sdk/services-api/pkg/utils/auth" "github.com/stretchr/testify/assert" @@ -40,7 +42,7 @@ func TestTokenValidator_ValidateToken(t *testing.T) { return } - namespace := "accelbyte" + namespace := os.Getenv("AB_NAMESPACE") resourceName := "MMV2GRPCSERVICE" requiredPermission := Permission{ Action: 2, @@ -62,3 +64,44 @@ func TestTokenValidator_ValidateToken(t *testing.T) { assert.Nil(t, err) assert.Equal(t, claims.Namespace, namespace) } + +func TestTokenValidator_ValidateExtendNamespace(t *testing.T) { + // should be moved and run as integration test, skip for now + t.Skip() + + // Arrange + configRepo := auth.DefaultConfigRepositoryImpl() + tokenRepo := auth.DefaultTokenRepositoryImpl() + authService := OAuth20Service{ + Client: factory.NewIamClient(configRepo), + ConfigRepository: configRepo, + TokenRepository: tokenRepo, + } + + extendNamespace := os.Getenv("AB_NAMESPACE") + + token, err := authService.TokenGrantV3Short(&o_auth2_0.TokenGrantV3Params{ + ExtendNamespace: &extendNamespace, + GrantType: o_auth2_0.TokenGrantV3UrnIetfParamsOauthGrantTypeExtendClientCredentialsConstant, + }) + if err != nil { + assert.Fail(t, err.Error()) + + return + } + + authService.SetLocalValidation(false) // true will do it locally, false will do it remotely + claims, errClaims := authService.ParseAccessTokenToClaims(*token.AccessToken, false) // false will not validate at all, only claim. This test will validate in the next line + if errClaims != nil { + assert.Fail(t, errClaims.Error()) + + return + } + + // Act + err = authService.Validate(*token.AccessToken, nil, &extendNamespace, nil) // validate against the extend namespace + + // Assert + assert.Nil(t, err) + assert.Equal(t, claims.ExtendNamespace, extendNamespace) +}