Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

third party applications #481

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1515,6 +1515,51 @@ apps, err = tc.DescopeClient().Management.SSOApplication().LoadAll(context.Backg
descopeClient.DescopeClient().Management.SSOApplication().Delete(context.Background(), "appId")
```

### Manage Third Party Applications

You can create, update, delete or load third party applications, while also search and delete existing consents related to any third party application:

```go
// Create third party application
req := &descope.ThirdPartyApplicationRequest{
Name: "My OIDC App",
Logo: "data:image/jpeg;base64...",
LoginPageURL: "http://dummy.com",
PermissionsScopes: []*descope.ThirdPartyApplicationScope{
{Name: "read", Description: "Read all", Values: []string{"Support"}},
},
AttributesScopes: []*descope.ThirdPartyApplicationScope{
{Name: "base", Description: "Basic attribute requirements", Values: []string{"email", "phone"}},
},
}
appID, secret, err = descopeClient.Management.ThirdPartyApplication().CreateApplication(context.Background(), req)

// Update a third party application by id
// Update will override all fields as is. Use carefully.
err = tc.DescopeClient().Management.ThirdPartyApplication().UpdateApplication(context.TODO(), &descope.ThirdPartyApplicationRequest{ID: "my-id", Name: "my new name"})

// Load third party application by id
app, err = tc.DescopeClient().Management.ThirdPartyApplication().LoadApplication(context.Background(), "appId")

// Load all third party applications
apps, err = tc.DescopeClient().Management.ThirdPartyApplication().LoadAllApplications(context.Background())

// Delete a third party application.
// Deletion cannot be undone. Use carefully.
err = descopeClient.DescopeClient().Management.ThirdPartyApplication().DeleteApplication(context.Background(), "appId")

// Search third party applications consents by pages using a filter options, such as application id, user id, etc.
consents, total, err = descopeClient.DescopeClient().Management.ThirdPartyApplication().SearchConsents(context.Background(), &descope.ThirdPartyApplicationConsentSearchOptions{
AppID: "appId"
})

// Delete third party applications consents by filter options, such as application id, consent ids or user ids.
err = descopeClient.DescopeClient().Management.ThirdPartyApplication().DeleteConsents(context.Background(), &descope.ThirdPartyApplicationConsentDeleteOptions{
UserIDs: string{"my-user"}
})

```

## Code Examples

You can find various usage examples in the [examples folder](https://github.com/descope/go-sdk/blob/main/examples).
Expand Down
281 changes: 162 additions & 119 deletions descope/api/client.go

Large diffs are not rendered by default.

37 changes: 22 additions & 15 deletions descope/internal/mgmt/mgmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,30 @@ type managementBase struct {
type managementService struct {
managementBase

tenant sdk.Tenant
ssoApplication sdk.SSOApplication
user sdk.User
accessKey sdk.AccessKey
sso sdk.SSO
password sdk.PasswordManagement
jwt sdk.JWT
permission sdk.Permission
role sdk.Role
group sdk.Group
flow sdk.Flow
project sdk.Project
audit sdk.Audit
authz sdk.Authz
fga sdk.FGA
tenant sdk.Tenant
ssoApplication sdk.SSOApplication
user sdk.User
accessKey sdk.AccessKey
sso sdk.SSO
password sdk.PasswordManagement
jwt sdk.JWT
permission sdk.Permission
role sdk.Role
group sdk.Group
flow sdk.Flow
project sdk.Project
audit sdk.Audit
authz sdk.Authz
fga sdk.FGA
thirdPartyApplication sdk.ThirdPartyApplication
}

func NewManagement(conf ManagementParams, c *api.Client) *managementService {
base := managementBase{conf: &conf, client: c}
service := &managementService{managementBase: base}
service.tenant = &tenant{managementBase: base}
service.ssoApplication = &ssoApplication{managementBase: base}
service.thirdPartyApplication = &thirdPartyApplication{managementBase: base}
service.user = &user{managementBase: base}
service.accessKey = &accessKey{managementBase: base}
service.sso = &sso{managementBase: base}
Expand Down Expand Up @@ -133,6 +135,11 @@ func (mgmt *managementService) FGA() sdk.FGA {
return mgmt.fga
}

func (mgmt *managementService) ThirdPartyApplication() sdk.ThirdPartyApplication {
mgmt.ensureManagementKey()
return mgmt.thirdPartyApplication
}

func (mgmt *managementService) ensureManagementKey() {
if mgmt.conf.ManagementKey == "" {
logger.LogInfo("Management key is missing, make sure to add it in the Config struct or the environment variable \"%s\"", descope.EnvironmentVariableManagementKey) // notest
Expand Down
166 changes: 166 additions & 0 deletions descope/internal/mgmt/third_party_application.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package mgmt

import (
"context"

"github.com/descope/go-sdk/descope"
"github.com/descope/go-sdk/descope/api"
"github.com/descope/go-sdk/descope/internal/utils"
"github.com/descope/go-sdk/descope/sdk"
)

type thirdPartyApplication struct {
managementBase
}

var _ sdk.ThirdPartyApplication = &thirdPartyApplication{}

func (s *thirdPartyApplication) CreateApplication(ctx context.Context, appRequest *descope.ThirdPartyApplicationRequest) (id string, secret string, err error) {
if appRequest == nil {
return "", "", utils.NewInvalidArgumentError("appRequest")
}
if appRequest.Name == "" {
return "", "", utils.NewInvalidArgumentError("appRequest.Name")
}

req := makeCreateUpdateThirdPartyApplicationRequest(appRequest)
httpRes, err := s.client.DoPostRequest(ctx, api.Routes.ManagementThirdPartyApplicationCreate(), req, nil, s.conf.ManagementKey)
if err != nil {
return "", "", err
}
res := &struct {
ID string `json:"id"`
Cleartext string `json:"cleartext"`
}{}
if err = utils.Unmarshal([]byte(httpRes.BodyStr), res); err != nil {
return "", "", err
}
return res.ID, res.Cleartext, nil
}

func (s *thirdPartyApplication) UpdateApplication(ctx context.Context, appRequest *descope.ThirdPartyApplicationRequest) error {
if appRequest == nil {
return utils.NewInvalidArgumentError("appRequest")
}
if appRequest.ID == "" {
return utils.NewInvalidArgumentError("appRequest.id")
}
if appRequest.Name == "" {
return utils.NewInvalidArgumentError("appRequest.Name")
}

req := makeCreateUpdateThirdPartyApplicationRequest(appRequest)
_, err := s.client.DoPostRequest(ctx, api.Routes.ManagementThirdPartyApplicationUpdate(), req, nil, s.conf.ManagementKey)
return err
}

func (s *thirdPartyApplication) DeleteApplication(ctx context.Context, id string) error {
if id == "" {
return utils.NewInvalidArgumentError("id")
}
req := map[string]any{"id": id}
_, err := s.client.DoPostRequest(ctx, api.Routes.ManagementThirdPartyApplicationDelete(), req, nil, s.conf.ManagementKey)
return err
}

func (s *thirdPartyApplication) LoadApplication(ctx context.Context, id string) (*descope.ThirdPartyApplication, error) {
if id == "" {
return nil, utils.NewInvalidArgumentError("id")
}
req := &api.HTTPRequest{
QueryParams: map[string]string{"id": id},
}
res, err := s.client.DoGetRequest(ctx, api.Routes.ManagementThirdPartyApplicationLoad(), req, s.conf.ManagementKey)
if err != nil {
return nil, err
}
return unmarshalLoadThirdPartyApplicationResponse(res)
}

func (s *thirdPartyApplication) LoadAllApplications(ctx context.Context) ([]*descope.ThirdPartyApplication, error) {
res, err := s.client.DoGetRequest(ctx, api.Routes.ManagementThirdPartyApplicationLoadAll(), nil, s.conf.ManagementKey)
if err != nil {
return nil, err
}
return unmarshalLoadAllThirdPartyApplicationsResponse(res)
}

func (s *thirdPartyApplication) DeleteConsents(ctx context.Context, consentRequest *descope.ThirdPartyApplicationConsentDeleteOptions) error {
if consentRequest == nil {
return utils.NewInvalidArgumentError("consentRequest")
}

req := map[string]any{
"consentIds": consentRequest.ConsentIDs,
"appId": consentRequest.AppID,
"userIds": consentRequest.UserIDs,
}
_, err := s.client.DoPostRequest(ctx, api.Routes.ManagementThirdPartyApplicationDeleteConsent(), req, nil, s.conf.ManagementKey)
if err != nil {
return err
}
return nil
}

func (s *thirdPartyApplication) SearchConsents(ctx context.Context, consentRequest *descope.ThirdPartyApplicationConsentSearchOptions) ([]*descope.ThirdPartyApplicationConsent, int, error) {
if consentRequest == nil {
return nil, 0, utils.NewInvalidArgumentError("consentRequest")
}

req := map[string]any{
"consentId": consentRequest.ConsentID,
"appId": consentRequest.AppID,
"userId": consentRequest.UserID,
"page": consentRequest.Page,
}
res, err := s.client.DoPostRequest(ctx, api.Routes.ManagementThirdPartyApplicationSearchConsents(), req, nil, s.conf.ManagementKey)
if err != nil {
return nil, 0, err
}
return unmarshalApplicationConsentsResponse(res)
}

func makeCreateUpdateThirdPartyApplicationRequest(appRequest *descope.ThirdPartyApplicationRequest) map[string]any {
return map[string]any{
"id": appRequest.ID,
"name": appRequest.Name,
"description": appRequest.Description,
"logo": appRequest.Logo,
"loginPageUrl": appRequest.LoginPageURL,
"approvedCallbackUrls": appRequest.ApprovedCallbackUrls,
"permissionsScopes": appRequest.PermissionsScopes,
"attributesScopes": appRequest.AttributesScopes,
}
}

func unmarshalLoadThirdPartyApplicationResponse(res *api.HTTPResponse) (*descope.ThirdPartyApplication, error) {
var appRes *descope.ThirdPartyApplication
err := utils.Unmarshal([]byte(res.BodyStr), &appRes)
if err != nil {
return nil, err
}
return appRes, nil
}

func unmarshalLoadAllThirdPartyApplicationsResponse(res *api.HTTPResponse) ([]*descope.ThirdPartyApplication, error) {
appsRes := struct {
Apps []*descope.ThirdPartyApplication
}{}
err := utils.Unmarshal([]byte(res.BodyStr), &appsRes)
if err != nil {
return nil, err
}
return appsRes.Apps, nil
}

func unmarshalApplicationConsentsResponse(res *api.HTTPResponse) ([]*descope.ThirdPartyApplicationConsent, int, error) {
appsRes := struct {
Consents []*descope.ThirdPartyApplicationConsent
Total int
}{}
err := utils.Unmarshal([]byte(res.BodyStr), &appsRes)
if err != nil {
return nil, 0, err
}
return appsRes.Consents, appsRes.Total, nil
}
Loading
Loading