Skip to content

Commit

Permalink
Impersonate with custom claims (#491)
Browse files Browse the repository at this point in the history
* Impersonate with custom claims
And with selected tenant
+ test
related to descope/etc#8724

* Fix readme
  • Loading branch information
aviadl authored Jan 2, 2025
1 parent 5306a4d commit a6f5956
Show file tree
Hide file tree
Showing 5 changed files with 15 additions and 10 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1273,9 +1273,10 @@ if err != nil {
You can impersonate to another user
The impersonator user must have the `impersonation` permission in order for this request to work.
The response would be a refresh JWT of the impersonated user

TenantID would be the tenant to set as DCT claim, in case set
customClaims - would be extra claims that are needed on the JWT
```go
refreshJWT, err := descopeClient.Management.JWT().Impersonate(context.Background(), "impersonator id", "login id", true)
refreshJWT, err := descopeClient.Management.JWT().Impersonate(context.Background(), "impersonator id", "login id", true, map[string]any{"k1":"v1"}, "T1")
if err != nil {
// handle error
}
Expand Down
4 changes: 3 additions & 1 deletion descope/internal/mgmt/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (j *jwt) UpdateJWTWithCustomClaims(ctx context.Context, jwt string, customC
return jRes.JWT, nil
}

func (j *jwt) Impersonate(ctx context.Context, impersonatorID string, loginID string, validateConcent bool) (string, error) {
func (j *jwt) Impersonate(ctx context.Context, impersonatorID string, loginID string, validateConcent bool, customClaims map[string]any, tenantID string) (string, error) {
if loginID == "" {
return "", utils.NewInvalidArgumentError("loginID")
}
Expand All @@ -50,6 +50,8 @@ func (j *jwt) Impersonate(ctx context.Context, impersonatorID string, loginID st
"loginId": loginID,
"impersonatorId": impersonatorID,
"validateConsent": validateConcent,
"customClaims": customClaims,
"selectedTenant": tenantID,
}
res, err := j.client.DoPostRequest(ctx, api.Routes.ManagementImpersonate(), req, nil, j.conf.ManagementKey)
if err != nil {
Expand Down
8 changes: 5 additions & 3 deletions descope/internal/mgmt/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ func TestImpersonate(t *testing.T) {
require.EqualValues(t, impID, req["impersonatorId"])
require.EqualValues(t, loginID, req["loginId"])
require.EqualValues(t, true, req["validateConsent"])
require.EqualValues(t, "t1", req["selectedTenant"])
require.EqualValues(t, map[string]any{"k1": "v1"}, req["customClaims"])

}, map[string]interface{}{"jwt": expectedJWT}))
jwtRes, err := mgmt.JWT().Impersonate(context.Background(), impID, loginID, true)
jwtRes, err := mgmt.JWT().Impersonate(context.Background(), impID, loginID, true, map[string]any{"k1": "v1"}, "t1")
require.NoError(t, err)
require.EqualValues(t, expectedJWT, jwtRes)
}
Expand All @@ -71,7 +73,7 @@ func TestImpersonateMissingLoginID(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoOk(func(_ *http.Request) {
called = true
}))
jwtRes, err := mgmt.JWT().Impersonate(context.Background(), "test", "", true)
jwtRes, err := mgmt.JWT().Impersonate(context.Background(), "test", "", true, map[string]any{"k1": "v1"}, "t1")
require.Error(t, err)
require.False(t, called)
require.Empty(t, jwtRes)
Expand All @@ -82,7 +84,7 @@ func TestImpersonateMissingImpersonator(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoOk(func(_ *http.Request) {
called = true
}))
jwtRes, err := mgmt.JWT().Impersonate(context.Background(), "", "test", true)
jwtRes, err := mgmt.JWT().Impersonate(context.Background(), "", "test", true, map[string]any{"k1": "v1"}, "t1")
require.Error(t, err)
require.False(t, called)
require.Empty(t, jwtRes)
Expand Down
2 changes: 1 addition & 1 deletion descope/sdk/mgmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ type JWT interface {
// Impersonate another user
// The impersonator user must have `impersonation` permission in order for this request to work
// The response would be a refresh JWT of the impersonated user
Impersonate(ctx context.Context, impersonatorID string, loginID string, validateConcent bool) (string, error)
Impersonate(ctx context.Context, impersonatorID string, loginID string, validateConcent bool, customClaims map[string]any, tenantID string) (string, error)
}

// Provides functions for managing permissions in a project.
Expand Down
6 changes: 3 additions & 3 deletions descope/tests/mocks/mgmt/managementmock.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ type MockJWT struct {
UpdateJWTWithCustomClaimsResponse string
UpdateJWTWithCustomClaimsError error

ImpersonateAssert func(impersonatorID string, loginID string, validateConcent bool)
ImpersonateAssert func(impersonatorID string, loginID string, validateConcent bool, customClaims map[string]any, tenantID string)
ImpersonateResponse string
ImpersonateError error
}
Expand All @@ -105,9 +105,9 @@ func (m *MockJWT) UpdateJWTWithCustomClaims(_ context.Context, jwt string, custo
return m.UpdateJWTWithCustomClaimsResponse, m.UpdateJWTWithCustomClaimsError
}

func (m *MockJWT) Impersonate(_ context.Context, impersonatorID string, loginID string, validateConcent bool) (string, error) {
func (m *MockJWT) Impersonate(_ context.Context, impersonatorID string, loginID string, validateConcent bool, customClaims map[string]any, tenantID string) (string, error) {
if m.ImpersonateAssert != nil {
m.ImpersonateAssert(impersonatorID, loginID, validateConcent)
m.ImpersonateAssert(impersonatorID, loginID, validateConcent, customClaims, tenantID)
}
return m.ImpersonateResponse, m.ImpersonateError
}
Expand Down

0 comments on commit a6f5956

Please sign in to comment.