Skip to content

Commit

Permalink
Fix data race on fetchKeys (#459)
Browse files Browse the repository at this point in the history
Add mytenants call
+ test
  • Loading branch information
aviadl authored Sep 11, 2024
1 parent ec0f514 commit d13b19c
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 1 deletion.
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ These sections show how to use the SDK to perform various authentication/authori
11. [Tenant selection](#tenant-selection)
12. [Logging Out](#logging-out)
13. [History](#history)
14. [My Tenants](#my-tenants)

## Management Functions

Expand Down Expand Up @@ -648,7 +649,7 @@ The request requires a valid refresh token.

```go
// Refresh token will be taken from the request header or cookies automatically
loginHistoryRes, err := descopeClient.Auth.History(request, r)
loginHistoryRes, err := descopeClient.Auth.History(request)
if err == nil {
for i := range loginHistoryRes {
fmt.Println(loginHistoryRes[i].UserID)
Expand All @@ -660,6 +661,23 @@ if err == nil {
}
```

### My Tenants

You can get the current session user tenants.
The request requires a valid refresh token.
And either a boolean to receive the current selected tenant
Or a list of tenant IDs that this user is part of

```go
// Refresh token will be taken from the request header or cookies automatically
tenants, err := descopeClient.Auth.MyTenants(context.Background(), request, true, nil)
if err == nil {
for i := range tenants.Tenants {

}
}
```

## Management Functions

It is very common for some form of management or automation to be required. These can be performed
Expand Down
6 changes: 6 additions & 0 deletions descope/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ var (
refresh: "auth/refresh",
selectTenant: "auth/tenant/select",
me: "auth/me",
meTenants: "auth/me/tenants",
history: "auth/me/history",
}
)
Expand All @@ -220,6 +221,7 @@ type endpoints struct {
refresh string
selectTenant string
me string
meTenants string
history string
}

Expand Down Expand Up @@ -594,6 +596,10 @@ func (e *endpoints) Me() string {
return path.Join(e.version, e.me)
}

func (e *endpoints) MeTenants() string {
return path.Join(e.version, e.meTenants)
}

func (e *endpoints) History() string {
return path.Join(e.version, e.history)
}
Expand Down
34 changes: 34 additions & 0 deletions descope/internal/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,40 @@ func (auth *authenticationService) Me(request *http.Request) (*descope.UserRespo
return auth.extractUserResponse(httpResponse.BodyStr)
}

func (auth *authenticationService) MyTenants(ctx context.Context, request *http.Request, dct bool, tenantIDs []string) (*descope.TenantsResponse, error) {
if request == nil {
return nil, utils.NewInvalidArgumentError("request")
}

if dct && len(tenantIDs) > 0 {
return nil, utils.NewInvalidArgumentError("Only one of dct or tenant ids should be provided")
}

_, refreshToken := provideTokens(request)
if refreshToken == "" {
logger.LogDebug("Unable to find tokens from cookies")
return nil, descope.ErrRefreshToken.WithMessage("Unable to find tokens from cookies")
}

_, err := auth.validateJWT(refreshToken)
if err != nil {
logger.LogDebug("Invalid refresh token")
return nil, descope.ErrRefreshToken.WithMessage("Invalid refresh token")
}

httpResponse, err := auth.client.DoPostRequest(ctx, api.Routes.MeTenants(), map[string]any{"dct": dct, "ids": tenantIDs}, &api.HTTPRequest{}, refreshToken)
if err != nil {
return nil, err
}
res := descope.TenantsResponse{}
err = utils.Unmarshal([]byte(httpResponse.BodyStr), &res)
if err != nil {
logger.LogError("Unable to parse tenant response", err)
return nil, err
}
return &res, nil
}

func (auth *authenticationService) History(request *http.Request) ([]*descope.UserHistoryResponse, error) {
if request == nil {
return nil, utils.NewInvalidArgumentError("request")
Expand Down
64 changes: 64 additions & 0 deletions descope/internal/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,70 @@ func TestMeEmptyResponse(t *testing.T) {
assert.Nil(t, user)
}

func TestTenants(t *testing.T) {
a, err := newTestAuth(nil, func(r *http.Request) (*http.Response, error) {
m := &map[string]any{}
readBody(r, m)
assert.EqualValues(t, map[string]any{"dct": true, "ids": nil}, *m)
res := descope.TenantsResponse{Tenants: []descope.MeTenant{{ID: "a"}}}
bs, err := utils.Marshal(res)
require.NoError(t, err)
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString(string(bs)))}, nil
})
require.NoError(t, err)
request := &http.Request{Header: http.Header{}}
request.AddCookie(&http.Cookie{Name: descope.RefreshCookieName, Value: jwtRTokenValid})

tnts, err := a.MyTenants(context.Background(), request, true, nil)
require.NoError(t, err)
assert.Len(t, tnts.Tenants, 1)
}

func TestTenantsInvalidArgs(t *testing.T) {
a, err := newTestAuth(nil, func(r *http.Request) (*http.Response, error) {
res := descope.TenantsResponse{Tenants: []descope.MeTenant{{ID: "a"}}}
bs, err := utils.Marshal(res)
require.NoError(t, err)
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString(string(bs)))}, nil
})
require.NoError(t, err)
request := &http.Request{Header: http.Header{}}
request.AddCookie(&http.Cookie{Name: descope.RefreshCookieName, Value: jwtRTokenValid})

_, err = a.MyTenants(context.Background(), request, true, []string{"a"})
require.Error(t, err)
}

func TestTenantsNoRequest(t *testing.T) {
a, err := newTestAuth(nil, DoOk(nil))
require.NoError(t, err)
user, err := a.MyTenants(context.Background(), nil, true, nil)
assert.ErrorIs(t, err, descope.ErrInvalidArguments)
assert.Nil(t, user)
}

func TestTenantsNoToken(t *testing.T) {
a, err := newTestAuth(nil, DoOk(nil))
require.NoError(t, err)
request := &http.Request{Header: http.Header{}}
user, err := a.MyTenants(context.Background(), request, true, nil)
assert.ErrorIs(t, err, descope.ErrRefreshToken)
assert.ErrorContains(t, err, "Unable to find tokens")
assert.Nil(t, user)
}

func TestTenantsInvalidToken(t *testing.T) {
a, err := newTestAuth(nil, DoOk(nil))
require.NoError(t, err)
request := &http.Request{Header: http.Header{}}
request.AddCookie(&http.Cookie{Name: descope.RefreshCookieName, Value: jwtTokenExpired})

user, err := a.MyTenants(context.Background(), request, true, nil)
assert.ErrorIs(t, err, descope.ErrRefreshToken)
assert.ErrorContains(t, err, "Invalid refresh token")
assert.Nil(t, user)
}

func TestHistory(t *testing.T) {
a, err := newTestAuth(nil, func(r *http.Request) (*http.Response, error) {
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString(mockUserHistoryResponseBody))}, nil
Expand Down
6 changes: 6 additions & 0 deletions descope/sdk/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,12 @@ type Authentication interface {
// returns the user details or error if the refresh token is not valid.
Me(request *http.Request) (*descope.UserResponse, error)

// MyTenants - Use to retrieve current session user tenants. The request requires a valid refresh token.
// returns the tenant requested or error if the refresh token is not valid.
// set dct to true - if the required tenant is the one that is set on the dct claim
// or provide a list of tenant ids that user is part of
MyTenants(ctx context.Context, request *http.Request, dct bool, tenantIDs []string) (*descope.TenantsResponse, error)

// History - Use to retrieve current session user history. The request requires a valid refresh token.
// returns the user authentication history or error if the refresh token is not valid.
History(request *http.Request) ([]*descope.UserHistoryResponse, error)
Expand Down
11 changes: 11 additions & 0 deletions descope/tests/mocks/auth/authenticationmock.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,10 @@ type MockSession struct {
MeError error
MeResponse *descope.UserResponse

MyTenantsAssert func(r *http.Request, dct bool, tenantIDs []string)
MyTenantsError error
MyTenantsResponse *descope.TenantsResponse

HistoryAssert func(r *http.Request)
HistoryError error
HistoryResponse []*descope.UserHistoryResponse
Expand Down Expand Up @@ -888,6 +892,13 @@ func (m *MockSession) Me(r *http.Request) (*descope.UserResponse, error) {
return m.MeResponse, m.MeError
}

func (m *MockSession) MyTenants(_ context.Context, r *http.Request, dct bool, tenantIDs []string) (*descope.TenantsResponse, error) {
if m.MyTenantsAssert != nil {
m.MyTenantsAssert(r, dct, tenantIDs)
}
return m.MyTenantsResponse, m.MyTenantsError
}

func (m *MockSession) History(r *http.Request) ([]*descope.UserHistoryResponse, error) {
if m.HistoryAssert != nil {
m.HistoryAssert(r)
Expand Down
10 changes: 10 additions & 0 deletions descope/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,16 @@ type UserResponse struct {
SSOAppIDs []string `json:"ssoAppIds,omitempty"`
}

type MeTenant struct {
ID string `json:"id"`
Name string `json:"name"`
CustomAttributes map[string]any `json:"customAttributes,omitempty"`
}

type TenantsResponse struct {
Tenants []MeTenant `json:"tenants,omitempty"`
}

type UserHistoryResponse struct {
UserID string `json:"userId,omitempty"`
LoginTime int32 `json:"loginTime,omitempty"`
Expand Down

0 comments on commit d13b19c

Please sign in to comment.