{"id":13563023,"url":"https://github.com/descope/go-sdk","last_synced_at":"2026-05-11T12:06:03.767Z","repository":{"id":59111805,"uuid":"490606911","full_name":"descope/go-sdk","owner":"descope","description":"Go library used to integrate with Descope","archived":false,"fork":false,"pushed_at":"2026-03-06T22:10:25.000Z","size":1760,"stargazers_count":51,"open_issues_count":8,"forks_count":10,"subscribers_count":15,"default_branch":"main","last_synced_at":"2026-03-07T02:24:43.204Z","etag":null,"topics":["authentication","authorization","descope","go","go-sdk","golang","golang-sdk","sdk"],"latest_commit_sha":null,"homepage":"https://docs.descope.com","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/descope.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-05-10T08:24:30.000Z","updated_at":"2026-03-06T22:08:29.000Z","dependencies_parsed_at":"2025-12-31T20:01:31.884Z","dependency_job_id":null,"html_url":"https://github.com/descope/go-sdk","commit_stats":null,"previous_names":["descope/descope-go-sdk"],"tags_count":45,"template":false,"template_full_name":null,"purl":"pkg:github/descope/go-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/descope%2Fgo-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/descope%2Fgo-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/descope%2Fgo-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/descope%2Fgo-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/descope","download_url":"https://codeload.github.com/descope/go-sdk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/descope%2Fgo-sdk/sbom","scorecard":{"id":335975,"data":{"date":"2025-08-11","repo":{"name":"github.com/descope/go-sdk","commit":"ec33a938e394ac835bee7b4742f14cf06890078b"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":6.7,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":9,"reason":"Found 21/22 approved changesets -- score normalized to 9","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":10,"reason":"23 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: jobLevel 'checks' permission set to 'write': .github/workflows/ci.yml:30","Info: jobLevel 'contents' permission set to 'read': .github/workflows/ci.yml:28","Info: jobLevel 'pull-requests' permission set to 'read': .github/workflows/ci.yml:29","Warn: no topLevel permission defined: .github/workflows/ci.yml:1"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Pinned-Dependencies","score":6,"reason":"dependency not pinned by hash detected -- score normalized to 6","details":["Warn: goCommand not pinned by hash: scripts/build/ci/build_test.sh:10","Warn: downloadThenRun not pinned by hash: scripts/lint/lint.sh:71","Info:   3 out of   3 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 goCommand dependencies pinned","Info:   0 out of   1 downloadThenRun dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":8,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'main'","Info: 'force pushes' disabled on branch 'main'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'main'","Info: 'stale review dismissal' is required to merge on branch 'main'","Warn: required approving review count is 1 on branch 'main'","Warn: codeowners review is required - but no codeowners file found in repo","Info: 'last push approval' is required to merge on branch 'main'","Info: status check found to merge onto on branch 'main'","Info: PRs are required in order to make changes on branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":7,"reason":"3 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2025-3487 / GHSA-hcg3-q754-cr77","Warn: Project is vulnerable to: GO-2025-3503 / GHSA-qxp5-gwg8-xv66","Warn: Project is vulnerable to: GO-2025-3595 / GHSA-vvgc-356p-c3xw"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":10,"reason":"SAST tool is run on all commits","details":["Info: all commits (29) are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T04:44:08.786Z","repository_id":59111805,"created_at":"2025-08-18T04:44:08.786Z","updated_at":"2025-08-18T04:44:08.786Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30357614,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T21:41:54.280Z","status":"ssl_error","status_checked_at":"2026-03-10T21:40:59.357Z","response_time":106,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","authorization","descope","go","go-sdk","golang","golang-sdk","sdk"],"created_at":"2024-08-01T13:01:14.392Z","updated_at":"2026-05-11T12:06:03.749Z","avatar_url":"https://github.com/descope.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"[![CI](https://github.com/descope/go-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/descope/go-sdk/actions/workflows/ci.yml)\n\n# Descope SDK for Go\n\nThe Descope SDK for Go provides convenient access to the Descope user management and authentication API\nfor a backend written in Go. You can read more on the [Descope Website](https://descope.com).\n\n## Requirements\n\nThe SDK supports Go version 1.18 and above.\n\n## Installing the SDK\n\nInstall the package with:\n\n```bash\ngo get -u github.com/descope/go-sdk\n```\n\n## Setup\n\nA Descope `Project ID` is required to initialize the SDK. Find it on the\n[project page in the Descope Console](https://app.descope.com/settings/project).\n\n```go\nimport \"github.com/descope/go-sdk/descope/client\"\n\n// Initialized after setting the DESCOPE_PROJECT_ID env var\ndescopeClient, err := client.New()\n\n// ** Or directly **\ndescopeClient, err := client.NewWithConfig(\u0026client.Config{ProjectID: projectID})\n```\n\n### Auth Management Key\n\nIt is possible to disable public access to any the Descope authentication method APIs via the\nthe Descope console. In these cases, private access is still available by using an `Management Key`. This management\nkey needs to have the correct access control, either `Authentication` or `Full Access`, for the project\nor the entire company. Create one in the [Descope Console](https://app.descope.com/settings/company/managementkeys),\nand provide it as the `AuthManagementKey` in the Descope client initialization (see below).\n\n**Note**: the authentication management key can, and probably should, be a different management key than\nthe one provided for [management API usage](#setup-1).\n\n```go\nimport \"github.com/descope/go-sdk/descope/client\"\n\n// Initialized after setting the DESCOPE_PROJECT_ID and the DESCOPE_AUTH_MANAGEMENT_KEY env vars\ndescopeClient, err := client.New()\n\n// ** Or directly **\ndescopeClient, err := client.NewWithConfig(\u0026client.Config{\n    ProjectID: \"project-ID\",\n    AuthManagementKey: \"auth-management-key\",\n})\n```\n\n### JWT Validation Leeway\n\nBy default, the SDK uses a 5-second leeway (time skew) when validating JWT tokens to account for minor clock synchronization differences between systems.\n\n```go\nimport \"github.com/descope/go-sdk/descope/client\"\nimport \"time\"\n\n// Configure custom JWT validation leeway (e.g., 30 seconds)\ndescopeClient, err := client.NewWithConfig(\u0026client.Config{\n    ProjectID: \"project-ID\",\n    JWTLeeway: 30 * time.Second, // Increase leeway to handle larger clock differences\n})\n```\n\nIf `JWTLeeway` is not set or set to 0, the SDK will use the default 5-second leeway for backward compatibility.\n\n## Usage\n\n### Authentication Functions\n\nThese sections show how to use the SDK to perform various authentication/authorization functions:\n\n1. [OTP Authentication](#otp-authentication)\n2. [Magic Link](#magic-link)\n3. [Enchanted Link](#enchanted-link)\n4. [OAuth](#oauth)\n5. [nOTP (WhatsApp)](#notp-whatsapp)\n6. [SSO (SAML / OIDC)](#sso-saml--oidc)\n7. [TOTP Authentication](#totp-authentication)\n8. [Passwords](#passwords)\n9. [Session Validation](#session-validation)\n10. [Roles \u0026 Permission Validation](#roles--permission-validation)\n11. [Tenant selection](#tenant-selection)\n12. [Tenant User Isolation](#tenant-user-isolation)\n13. [Logging Out](#logging-out)\n14. [History](#history)\n15. [My Tenants](#my-tenants)\n\n### Management Functions\n\nThese sections show how to use the SDK to perform API management functions. Before using any of them, you will need to create a Management Key. The instructions for this can be found under [Setup](#setup-1).\n\n1. [Manage Tenants](#manage-tenants)\n2. [Manage Users](#manage-users)\n3. [Manage Access Keys](#manage-access-keys)\n4. [Manage SSO Setting](#manage-sso-setting)\n5. [Manage Permissions](#manage-permissions)\n6. [Manage Roles](#manage-roles)\n7. [Query SSO Groups](#query-sso-groups)\n8. [Manage Flows](#manage-flows)\n9. [Manage JWTs](#manage-jwts)\n10. [Impersonate](#impersonate)\n11. [Audit](#audit)\n12. [Embedded Links](#embedded-links)\n13. [Manage FGA (Fine-grained Authorization)](#manage-fga-fine-grained-authorization)\n14. [Manage Project](#manage-project)\n15. [Manage SSO Applications](#manage-sso-applications)\n16. [Manage Lists](#manage-lists)\n17. [Manage Management Keys](#manage-management-keys)\n18. [Manage Descopers](#manage-descopers)\n\nIf you wish to run any of our code samples and play with them, check out our [Code Examples](#code-examples) section.\n\nIf you're developing unit tests, see how you can use our `mocks` package in the [Unit Testing and Data Mocks](#unit-testing-and-data-mocks) section.\n\nIf you're performing end-to-end testing, check out the [Utils for your end to end (e2e) tests and integration tests](#utils-for-your-end-to-end-e2e-tests-and-integration-tests) section. You will need to use the `descopeClient` object created under [Setup](#setup-1) guide.\n\nFor rate limiting information, please refer to the [API Rate Limits](#api-rate-limits) section.\n\n### Error Handling\n\nEvery SDK function that performs a network request or calls the Descope servers might fail, and in such cases they\nreturn an `error` value with information about what went wrong. Usually the concrete type of the error value will\nbe a `*descope.Error`.\n\nA typical case of error handling might look something like this:\n\n```go\nresult, err := descopeClient.Auth.OTP().VerifyCode(ctx, descope.MethodEmail, \"desmond@descope.com\", \"123456\", nil)\nif descope.IsError(err, \"E061102\") {\n\t// check for a Descope error with a specific error code\n}\nif errors.Is(err, descope.ErrInvalidOneTimeCode) {\n\t// for common error codes, you can use golang's errors.Is function instead\n}\nif descope.IsUnauthorizedError(err) {\n\t// check for a Descope error with a generic 401 status code, rather than a specific error code\n}\nif descopeErr := descope.AsError(err); descopeErr != nil {\n\t// access the Code or Description fields directly to handle the error or write it to a logger\n}\nif err != nil {\n    // handle other error cases\n}\n```\n\n---\n\n### OTP Authentication\n\nSend a user a one-time password (OTP) using your preferred delivery method (_Email / SMS / Voice call / WhatsApp_). An email address or phone number must be provided accordingly.\n\nThe user can either `sign up`, `sign in` or `sign up or in`\n\n```go\n// Every user must have a loginID. All other user information is optional\nloginID := \"desmond@descope.com\"\nuser := \u0026descope.User{\n    Name: \"Desmond Copeland\",\n    GivenName: \"Desmond\",\n    FamilyName: \"Copeland\",\n    Phone: \"212-555-1234\",\n    Email: loginID,\n}\nmaskedAddress, err := descopeClient.Auth.OTP().SignUp(context.Background(), descope.MethodEmail, loginID, user, nil)\nif err != nil {\n    if errors.Is(err, descope.ErrUserAlreadyExists) {\n        // user already exists with this loginID\n    }\n    // handle other error cases\n}\n```\n\nThe user will receive a code using the selected delivery method. Verify that code using:\n\n```go\n// The optional `w http.ResponseWriter` adds the session and refresh cookies to the response automatically.\n// Otherwise they're available via authInfo\nauthInfo, err := descopeClient.Auth.OTP().VerifyCode(context.Background(), descope.MethodEmail, loginID, code, w)\nif err != nil {\n    if errors.Is(err, descope.ErrInvalidOneTimeCode) {\n        // the code was invalid, ask user to try again\n    }\n\tif descope.IsError(err, \"E061103\") {\n\t\t// too many wrong otp attempts\n\t}\n    if descope.IsUnauthorizedError(err) {\n        // login was not allowed for some other reason\n    }\n    // handle other error cases\n}\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n### Magic Link\n\nSend a user a Magic Link using your preferred delivery method (_Email / SMS / WhatsApp_).\nThe Magic Link will redirect the user to page where the its token needs to be verified.\nThis redirection can be configured in code, or globally in the [Descope Console](https://app.descope.com/settings/authentication/magiclink)\n\nThe user can either `sign up`, `sign in` or `sign up or in`\n\n```go\n// If configured globally, the redirect URI is optional. If provided however, it will be used\n// instead of any global configuration\nmaskedAddress, err := descopeClient.Auth.MagicLink().SignUpOrIn(context.Background(), descope.MethodEmail, \"desmond@descope.com\", \"http://myapp.com/verify-magic-link\", nil)\nif err {\n    // handle error\n}\n```\n\nTo verify a magic link, your redirect page must call the validation function on the token (`t`) parameter (`https://your-redirect-address.com/verify?t=\u003ctoken\u003e`):\n\n```go\n// The optional `w http.ResponseWriter` adds the session and refresh cookies to the response automatically.\n// Otherwise they're available via authInfo\nauthInfo, err := descopeClient.Auth.MagicLink().Verify(context.Background(), token, w)\nif err != nil {\n    // handle error\n}\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n### Enchanted Link\n\nUsing the Enchanted Link APIs enables users to sign in by clicking a link\ndelivered to their email address. The email will include 3 different links,\nand the user will have to click the right one, based on the 2-digit number that is\ndisplayed when initiating the authentication process.\n\nThis method is similar to [Magic Link](#magic-link) but differs in two major ways:\n\n-   The user must choose the correct link out of the three, instead of having just one\n    single link.\n-   This supports cross-device clicking, meaning the user can try to log in on one device,\n    like a computer, while clicking the link on another device, for instance a mobile phone.\n\nThe Enchanted Link will redirect the user to page where the its token needs to be verified.\nThis redirection can be configured in code per request, or set globally in the [Descope Console](https://app.descope.com/settings/authentication/enchantedlink).\n\nThe user can either `sign up`, `sign in` or `sign up or in`\n\n```go\n// If configured globally, the redirect URI is optional. If provided however, it will be used\n// instead of any global configuration.\nres, err := descopeClient.Auth.EnchantedLink().SignIn(context.Background(), loginID, \"http://myapp.com/verify-enchanted-link\", nil, nil)\nif err != nil {\n    // handle error\n}\nres.LinkID // should be displayed to the user so they can click the corresponding link in the email\nres.PendingRef // Used to poll for a valid session\n```\n\nAfter sending the link, you must poll to receive a valid session using the `PendingRef` from\nthe previous step. A valid session will be returned only after the user clicks the right link.\n\n```go\n// Poll for a certain number of tries / time frame\nfor i := retriesCount; i \u003e 0; i-- {\n    authInfo, err := descopeClient.Auth.EnchantedLink().GetSession(context.Background(), res.PendingRef, w)\n    if err == nil {\n        // The user successfully authenticated using the correct link\n        // The optional `w http.ResponseWriter` adds the session and refresh cookies to the response automatically.\n        // Otherwise they're available via authInfo\n        break\n    }\n    if errors.Is(err, descope.ErrEnchantedLinkUnauthorized) \u0026\u0026 i \u003e 1 {\n        // poll again after X seconds\n        time.Sleep(time.Second * time.Duration(retryInterval))\n        continue\n    }\n    if err != nil {\n        // handle error\n        break\n    }\n}\n```\n\nTo verify an enchanted link, your redirect page must call the validation function on the token (`t`) parameter (`https://your-redirect-address.com/verify?t=\u003ctoken\u003e`). Once the token is verified, the session polling will receive a valid response.\n\n```go\nif err := descopeClient.Auth.EnchantedLink().Verify(context.Background(), token); err != nil {\n    // token is invalid\n} else {\n    // token is valid\n}\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n### OAuth\n\nUsers can authenticate using their social logins, using the OAuth protocol. Configure your OAuth settings on the [Descope console](https://app.descope.com/settings/authentication/social). To start a flow call:\n\n```go\n// Choose an oauth provider out of the supported providers\n// If configured globally, the return URL is optional. If provided however, it will be used\n// instead of any global configuration.\n// Redirect the user to the returned URL to start the OAuth redirect chain\nurl, err := descopeClient.Auth.OAuth().SignUpOrIn(context.Background(), \"google\", \"https://my-app.com/handle-oauth\", \"\", nil, nil, w)\nif err != nil {\n    // handle error\n}\n```\n\nThe user will authenticate with the authentication provider, and will be redirected back to the redirect URL, with an appended `code` HTTP URL parameter. Exchange it to validate the user:\n\n```go\n// The optional `w http.ResponseWriter` adds the session and refresh cookies to the response automatically.\n// Otherwise they're available via authInfo\nauthInfo, err := descopeClient.Auth.OAuth().ExchangeToken(context.Background(), code, w)\nif err != nil {\n    // handle error\n}\n```\n\nUsers can also connect the social login account to their existing user:\n\n```go\n// A valid Refresh Token of the existing user is required and will be taken from the request header or cookies automatically.\n// If allowAllMerge is 'true' the users will be merged also if there is no common identifier between the social provider and the existing user (like email).\nurl, err := descopeClient.Auth.OAuth().UpdateUser(context.Background(), \"google\", \"https://my-app.com/handle-oauth\", \"\", true, nil, nil, w)\nif err != nil {\n    // handle error\n}\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n### nOTP (WhatsApp)\n\nUsing the nOTP (WhatsApp) APIs enables users to log in using their WhatsApp account, according to the following process:\na. The user will be redirected to WhatsApp (with a QR Code or link) with a pre-filled message containing a 16-character alphanumeric code.\nb. The user will send the message to the WhatsApp Application associated with the Descope project.\nc. Descope will receive the message, validate the code, and send an approval message back to the user.\nd. The user will be logged in after receiving the approval message.\n\nNote: The nOTP (WhatsApp) authentication method should be configured in the Descope Console before using it\n\nThe user can either `sign up`, `sign in`, or `sign up or in`:\n\n```go\nloginID := \"\" // OR phone number\nres, err := descopeClient.Auth.NOTP().SignUpOrIn(context.Background(), loginID, nil, nil)\nif err != nil {\n    // handle error\n}\n\n// The URL to redirect the user to initiate a conversation in the WhatsApp Web Application with the pre-filled message containing the code\nres.RedirectURL\n// A QR code image that can be displayed to the user to scan using their mobile device camera app to start the WhatsApp conversation\nres.Image\n// Used to poll for a valid session\nres.PendingRef\n```\n\nAfter sending the link, you must poll to receive a valid session using the `PendingRef` from the previous step. A valid session will be returned only after the user sends the message to the WhatsApp Application associated with the Project with the code\n\n```go\n// Poll for a certain number of tries / time frame\nfor i := retriesCount; i \u003e 0; i-- {\n    authInfo, err := descopeClient.Auth.NOTP().GetSession(context.Background(), res.PendingRef, w)\n    if err == nil {\n        // The user successfully authenticated\n        // The optional `w http.ResponseWriter` adds the session and refresh cookies to the response automatically.\n        // Otherwise they're available via authInfo\n        break\n    }\n    if errors.Is(err, descope.ErrNOTPUnauthorized) \u0026\u0026 i \u003e 1 {\n        // poll again after X seconds\n        time.Sleep(time.Second * time.Duration(retryInterval))\n        continue\n    }\n    if err != nil {\n        // handle error\n        break\n    }\n}\n```\n\nThe verification process is conducted using the WhatsApp application by the user sending a message with the token included in the link. After sending the message, the user will receive an approval message back, and the session polling will then receive a valid response\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n### SSO (SAML / OIDC)\n\nUsers can authenticate to a specific tenant using SAML or OIDC. Configure your SSO (SAML / OIDC) settings on the [Descope console](https://app.descope.com/settings/authentication/sso). To start a flow call:\n\n```go\n// Choose which tenant to log into\n// If configured globally, the return URL is optional. If provided however, it will be used\n// instead of any global configuration.\n// Redirect the user to the returned URL to start the SSO SAML/OIDC redirect chain\nurl, err := descopeClient.Auth.SSO().Start(\"my-tenant-ID\", \"https://my-app.com/handle-saml\", \"\", \"\", \"\", nil, nil, w)\nif err != nil {\n    // handle error\n}\n```\n\n```go\n//* Deprecated (use Auth.SSO().Start(..) instead) *//\n//\n// Choose which tenant to log into\n// If configured globally, the return URL is optional. If provided however, it will be used\n// instead of any global configuration.\n// Redirect the user to the returned URL to start the SSO/SAML redirect chain\nurl, err := descopeClient.Auth.SAML().Start(context.Background(), \"my-tenant-ID\", \"https://my-app.com/handle-saml\", \"\", nil, nil, w)\nif err != nil {\n    // handle error\n}\n```\n\nThe user will authenticate with the authentication provider configured for that tenant, and will be redirected back to the redirect URL, with an appended `code` HTTP URL parameter. Exchange it to validate the user:\n\n```go\n// The optional `w http.ResponseWriter` adds the session and refresh cookies to the response automatically.\n// Otherwise they're available via authInfo\nauthInfo, err := descopeClient.Auth.SSO().ExchangeToken(context.Background(), code, w)\nif err != nil {\n    // handle error\n}\n```\n\n```go\n//* Deprecated (use Auth.SSO().ExchangeToken(..) instead) *//\n//\n// The optional `w http.ResponseWriter` adds the session and refresh cookies to the response automatically.\n// Otherwise they're available via authInfo\nauthInfo, err := descopeClient.Auth.SAML().ExchangeToken(context.Background(), code, w)\nif err != nil {\n    // handle error\n}\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n### TOTP Authentication\n\nThe user can authenticate using an authenticator app, such as Google Authenticator.\nSign up like you would using any other authentication method. The sign up response\nwill then contain a QR code `Image` that can be displayed to the user to scan using\ntheir mobile device camera app, or the user can enter the `Key` manually or click\non the link provided by the `ProvisioningURL`.\n\nExisting users can add TOTP using the `update` function.\n\n```go\n// Every user must have a loginID. All other user information is optional\nloginID := \"desmond@descope.com\"\nuser := \u0026descope.User{\n    Name: \"Desmond Copeland\",\n    GivenName: \"Desmond\",\n    FamilyName: \"Copeland\",\n    Phone: \"212-555-1234\",\n    Email: loginID,\n}\ntotpResponse, err := descopeClient.Auth.TOTP().SignUp(context.Background(), loginID, user)\nif err != nil {\n    // handle error\n}\n// Use one of the provided options to have the user add their credentials to the authenticator\n// totpResponse.ProvisioningURL\n// totpResponse.Image\n// totpResponse.Key\n```\n\nThere are 3 different ways to allow the user to save their credentials in\ntheir authenticator app - either by clicking the provisioning URL, scanning the QR\nimage or inserting the key manually. After that, signing in is done using the code\nthe app produces.\n\n```go\n// The optional `w http.ResponseWriter` adds the session and refresh cookies to the response automatically.\n// Otherwise they're available via authInfo\nauthInfo, err := descopeClient.Auth.TOTP().SignInCode(context.Background(), loginID, code, nil, nil, w)\nif err != nil {\n    // handle error\n}\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\n#### Deleting the TOTP Seed\n\nPass the loginId to the function to remove the user's TOTP seed.\n\n```go\ntotpResponse, err := descopeClient.Management.User().RemoveTOTPSeed(context.Background(), loginID)\n```\n\n### Passwords\n\nThe user can also authenticate with a password, though it's recommended to\nprefer passwordless authentication methods if possible. Sign up requires the\ncaller to provide a valid password that meets all the requirements configured\nfor the [password authentication method](https://app.descope.com/settings/authentication/password) in the Descope console.\n\n```go\n// Every user must have a loginID. All other user information is optional\nloginID := \"desmond@descope.com\"\npassword := \"qYlvi65KaX\"\nuser := \u0026descope.User{\n    Name: \"Desmond Copeland\",\n    GivenName: \"Desmond\",\n    FamilyName: \"Copeland\",\n    Email: loginID,\n}\nauthInfo, err := descopeClient.Auth.Password().SignUp(context.Background(), loginID, user, password, nil)\nif err != nil {\n    // handle error\n}\n```\n\nThe user can later sign in using the same loginID and password.\n\n```go\n// The optional `w http.ResponseWriter` adds the session and refresh cookies to the response automatically.\n// Otherwise they're available via authInfo\nauthInfo, err := descopeClient.Auth.Password().SignIn(context.Background(), loginID, password, w)\nif err != nil {\n    // handle error\n}\n```\n\nThe session and refresh JWTs should be returned to the caller, and passed with every request in the session. Read more on [session validation](#session-validation)\n\nIn case the user needs to update their password, one of two methods are available: Resetting their password or replacing their password\n\n**Changing Passwords**\n\n_NOTE: SendPasswordReset will only work if the user has a validated email address. Otherwise password reset prompts cannot be sent._\n\nIn the [password authentication method](https://app.descope.com/settings/authentication/password) in the Descope console, it is possible to define which alternative authentication method can be used in order to authenticate the user, in order to reset and update their password.\n\n```go\n// Start the reset process by sending a password reset prompt. In this example we'll assume\n// that magic link is configured as the reset method. The optional redirect URL is used in the\n// same way as in regular magic link authentication.\nloginID := \"desmond@descope.com\"\nredirectURL := \"https://myapp.com/password-reset\"\nerr := descopeClient.Auth.Password().SendPasswordReset(context.Background(), loginID, redirectURL, nil)\n```\n\nThe magic link, in this case, must then be verified like any other magic link (see the [magic link section](#magic-link) for more details). However, after verifying the user, it is expected\nto allow them to provide a new password instead of the old one. Since the user is now authenticated, this is possible via:\n\n```go\n// The request (r) is required to make sure the user is authenticated.\nerr := descopeClient.Auth.Password().UpdateUserPassword(context.Background(), loginID, newPassword, r)\n```\n\n`UpdateUserPassword` can always be called when the user is authenticated and has a valid session.\n\nAlternatively, it is also possible to replace an existing active password with a new one.\n\n```go\n// Replaces the user's current password with a new one\nauthInfo, err := descopeClient.Auth.Password().ReplaceUserPassword(context.Background(), loginID, oldPassword, newPassword, w)\n```\n\n### Session Validation\n\nEvery secure request performed between your client and server needs to be validated. The client sends\nthe session and refresh tokens with every request, and they are validated using one of the following:\n\nWhen using cookies you can call:\n\n```go\n// Validate the session. Will return an error if expired\nif authorized, sessionToken, err := descopeClient.Auth.ValidateSessionWithRequest(r, w); !authorized {\n    // unauthorized error\n}\n\n// If ValidateSessionWithRequest raises an exception, you will need to refresh the session using\nif authorized, sessionToken, err := descopeClient.Auth.RefreshSessionWithRequest(r, w); !authorized {\n    // unauthorized error\n}\n\n// Alternatively, you could combine the two and\n// have the session validated and automatically refreshed when expired\nif authorized, sessionToken, err := descopeClient.Auth.ValidateAndRefreshSessionWithRequest(r, w); !authorized {\n    // unauthorized error\n}\n```\n\nAlternatively, tokens can be validated directly:\n\n```go\n// Validate the session. Will return an error if expired\nif authorized, sessionToken, err := descopeClient.Auth.ValidateSessionWithToken(context.Background(), sessionToken); !authorized {\n    // unauthorized error\n}\n\n// If ValidateSessionWithRequest raises an exception, you will need to refresh the session using\nif authorized, sessionToken, err := descopeClient.Auth.RefreshSessionWithToken(context.Background(), refreshToken); !authorized {\n    // unauthorized error\n}\n\n// Alternatively, you could combine the two and\n// have the session validated and automatically refreshed when expired\nif authorized, sessionToken, err := descopeClient.Auth.ValidateAndRefreshSessionWithTokens(context.Background(), sessionToken, refreshToken); !authorized {\n    // unauthorized error\n}\n```\n\nChoose the right session validation and refresh combination that suits your needs.\n\nRefreshed sessions return the same response as is returned when users first sign up / log in,\nMake sure to return the session token from the response to the client if tokens are validated directly.\n\nUsually, the tokens can be passed in and out via HTTP headers or via a cookie.\nThe implementation can defer according to your implementation. See our [examples](#code-examples) for a few examples.\n\nIf Roles \u0026 Permissions are used, validate them immediately after validating the session. See the [next section](#roles--permission-validation)\nfor more information.\n\n#### Session Validation Using Middleware\n\nAlternatively, you can validate the session using any supported builtin Go middleware (for example Chi or Mux)\ninstead of using the ValidateSessions function. This middleware will automatically detect the cookies from the\nrequest and save the current user ID in the context for further usage. On failure, it will respond with `401 Unauthorized`.\n\n```go\nimport \"github.com/descope/go-sdk/descope/sdk\"\n\n// ...\n\nr.Use(sdk.AuthenticationMiddleware(descopeClient.Auth, nil, nil))\n```\n\n### Roles \u0026 Permission Validation\n\nWhen using Roles \u0026 Permission, it's important to validate the user has the required\nauthorization immediately after making sure the session is valid. Taking the `sessionToken`\nreceived by the [session validation](#session-validation), call the following functions:\n\nFor multi-tenant uses:\n\n```go\n// You can validate specific permissions\nif !descopeClient.Auth.ValidateTenantPermissions(context.Background(), sessionToken, \"my-tenant-ID\", []string{\"Permission to validate\"}) {\n    // Deny access\n}\n\n// Or validate roles directly\nif !descopeClient.Auth.ValidateTenantRoles(context.Background(), sessionToken, \"my-tenant-ID\", []string{\"Role to validate\"}) {\n    // Deny access\n}\n\nmatchedTenantRoles := descopeClient.Auth.GetTenantRoles(context.Background(), sessionToken, \"my-tenant-ID\", []string{\"role-name1\", \"role-name2\"})\n\nmatchedTenantPermissions := descopeClient.Auth.GetTenantPermissions(context.Background(), sessionToken, \"my-tenant-ID\", []string{\"permission-name1\", \"permission-name2\"})\n```\n\nWhen not using tenants use:\n\n```go\n// You can validate specific permissions\nif !descopeClient.Auth.ValidatePermissions(context.Background(), sessionToken, []string{\"Permission to validate\"}) {\n    // Deny access\n}\n\n// Or validate roles directly\nif !descopeClient.Auth.ValidateRoles(context.Background(), sessionToken, []string{\"Role to validate\"}) {\n    // Deny access\n}\n\n// Or get the matched roles/permissions\nmatchedRoles := descopeClient.Auth.GetMatchedRoles(context.Background(), sessionToken, []string{\"role-name1\", \"role-name2\"})\n\nmatchedPermissions := descopeClient.Auth.GetMatchedPermissions(context.Background(), sessionToken, []string{\"permission-name1\", \"permission-name2\"})\n```\n\n### Tenant selection\n\nFor a user that has permissions to multiple tenants, you can set a specific tenant as the current selected one\nThis will add an extra attribute to the refresh JWT and the session JWT with the selected tenant ID\n\n```go\ntenantID := \"t1\"\ninfo, err := descopeClient.Auth.SelectTenantWithRequest(context.Background(), tenantID, r, w)\nif err != nil {\n    // failed to select a tenant\n}\n```\n\nOr alternatively, work directly with refresh token\n\n```go\ntenantID := \"t1\"\nrefreshToken := \"\u003ca valid refresh token\u003e\"\ninfo, err := descopeClient.Auth.SelectTenantWithToken(context.Background(), tenantID, refreshToken)\nif err != nil {\n    // failed to select a tenant\n}\n```\n\n### Tenant User Isolation\n\nWhen a project has Tenant User Isolation enabled, users with the same login ID in different tenants are treated as completely separate identities. To associate a sign-up or sign-in with a specific tenant in this mode, pass `TenantID` in the options struct:\n\n```go\n// Sign up alice scoped to tenant A — creates a tenant-isolated identity\nsignUpOptions := \u0026descope.SignUpOptions{TenantID: \"tenant-A\"}\nmaskedAddress, err := descopeClient.Auth.OTP().SignUp(context.Background(), descope.MethodEmail, loginID, user, signUpOptions)\nif err != nil {\n    // handle error\n}\n\n// Verify the OTP code — tenant context is carried from the sign-up step\nauthInfo, err := descopeClient.Auth.OTP().VerifyCode(context.Background(), descope.MethodEmail, loginID, code, w)\nif err != nil {\n    // handle error\n}\n```\n\nA few things to keep in mind:\n- Only the sign-up/sign-in step requires `TenantID` — the verify step carries the tenant context automatically.\n- If `TenantID` is omitted, the request uses the non-isolated (shared) identity, as if tenant isolation were not enabled.\n- This is different from [`SelectTenant`](#tenant-selection), which switches the active tenant on an already-authenticated session. `TenantID` in `LoginOptions`/`SignUpOptions` scopes identity resolution at authentication time.\n\n### Logging Out\n\nYou can log out a user from an active session by providing their `refreshToken` for that session.\nAfter calling this function, you must invalidate or remove any cookies you have created. Providing\na `http.ResponseWriter` will do this automatically.\n\n```go\n// Refresh token will be taken from the request header or cookies automatically\n// If provided, the optional `w http.ResponseWriter` will empty out the session cookies automatically.\ndescopeClient.Auth.Logout(request, w)\n```\n\nIt is also possible to sign the user out of all the devices they are currently signed-in with. Calling `logoutAll` will\ninvalidate all user's refresh tokens. After calling this function, you must invalidate or remove any cookies you have created.\n\n```go\n// Refresh token will be taken from the request header or cookies automatically\n// If provided, the optional `w http.ResponseWriter` will empty out the session cookies automatically.\ndescopeClient.Auth.LogoutAll(request, w)\n```\n\n### History\n\nYou can get the current session user history.\nThe request requires a valid refresh token.\n\n```go\n// Refresh token will be taken from the request header or cookies automatically\nloginHistoryRes, err := descopeClient.Auth.History(request)\nif err == nil {\n    for i := range loginHistoryRes {\n        fmt.Println(loginHistoryRes[i].UserID)\n        fmt.Println(loginHistoryRes[i].City)\n        fmt.Println(loginHistoryRes[i].Country)\n        fmt.Println(loginHistoryRes[i].IP)\n        fmt.Println(loginHistoryRes[i].LoginTime)\n    }\n}\n```\n\n### My Tenants\n\nYou can get the current session user tenants.\nThe request requires a valid refresh token.\nAnd either a boolean to receive the current selected tenant\nOr a list of tenant IDs that this user is part of\n\n```go\n// Refresh token will be taken from the request header or cookies automatically\ntenants, err := descopeClient.Auth.MyTenants(context.Background(), request, true, nil)\nif err == nil {\n    for i := range tenants.Tenants {\n\n    }\n}\n```\n\n## Management Functions\n\nIt is very common for some form of management or automation to be required. These can be performed\nusing the management functions. Please note that these actions are more sensitive as they are administrative\nin nature. Please use responsibly.\n\n### Setup\n\nTo use the management API you'll need a `Management Key` along with your `Project ID`.\nCreate one in the [Descope Console](https://app.descope.com/settings/company/managementkeys).\n\n```go\nimport \"github.com/descope/go-sdk/descope/client\"\n\n// Initialized after setting the DESCOPE_PROJECT_ID and the DESCOPE_MANAGEMENT_KEY env vars\ndescopeClient, err := client.New()\n\n// ** Or directly **\ndescopeClient, err := client.NewWithConfig(\u0026client.Config{\n    ProjectID: \"project-ID\",\n\n\t// ManagementKey is required to use any of the management functions\n\t// It is not used to access authentication methods with disabled public access\n    ManagementKey: \"management-key\",\n\n\t// AuthManagementKey is required to access authentication methods with disabled public access\n\t// It is not used to access any of the management functions\n\tAuthManagementKey: \"auth-management-key\",\n})\n```\n\n### Manage Tenants\n\nYou can create, update, delete or load tenants:\n\n```go\n// The self provisioning domains or optional. If given they'll be used to associate\n// Users logging in to this tenant\n\n// Creating and updating tenants takes the \u0026descope.TenantRequest type. This is an example of a \u0026descope.TenantRequest\ntenantRequest := \u0026descope.TenantRequest{}\ntenantRequest.Name = \"My Tenant\"\ntenantRequest.SelfProvisioningDomains = []string{\"domain.com\"}\ntenantRequest.CustomAttributes = map[string]any{\"mycustomattribute\": \"Test\"}\n\n// Create tenant\nerr := descopeClient.Management.Tenant().Create(context.Background(), \"My Tenant\", tenantRequest)\n\n// You can optionally set your own ID when creating a tenant\nerr := descopeClient.Management.Tenant().CreateWithID(context.Background(), \"my-custom-id\", tenantRequest)\n\n// Update will override all fields as is. Use carefully.\nerr := descopeClient.Management.Tenant().Update(context.Background(), \"my-custom-id\", \"My Tenant\", tenantRequest)\n\n// Tenant deletion cannot be undone. Use carefully.\n// Pass true to cascade value, in case you want to delete all users/keys associated only with this tenant\nerr := descopeClient.Management.Tenant().Delete(context.Background(), \"my-custom-id\", true)\n\n// Load tenant by id\ntenant, err := descopeClient.Management.Tenant().Load(context.Background(), \"my-custom-id\")\n\n// Load all tenants\nres, err := descopeClient.Management.Tenant().LoadAll(context.Background())\nif err == nil {\n    for _, tenant := range res {\n        // Do something\n    }\n}\n\n// Search tenants - takes the \u0026descope.TenantSearchOptions type. This is an example of a \u0026descope.TenantSearchOptions\nsearchOptions := \u0026descope.TenantSearchOptions{}\nsearchOptions.IDs = []string{\"my-custom-id\"}\nsearchOptions.Names = []string{\"My Tenant\"}\nsearchOptions.SelfProvisioningDomains = []string{\"domain.com\", \"company.com\"}\nsearchOptions.CustomAttributes = map[string]any{\"mycustomattribute\": \"Test\"}\nres, err := descopeClient.Management.Tenant().SearchAll(context.Background(), searchOptions)\nif err == nil {\n  for _, tenant := range res {\n        // Do something\n    }\n}\n\n// Load tenant settings by a tenant id\nsettings, err := descopeClient.Management.Tenant().GetSettings(context.Background())\n\nsettingsRequest := \u0026descope.TenantSettings{}\nsettingsRequest.SelfProvisioningDomains = []string{\"domain.com\", \"company.com\"}\nsettingsRequest.RefreshTokenExpiration = 30\nsettingsRequest.RefreshTokenExpirationUnit = \"days\"\nsettingsRequest.SessionTokenExpiration = 30\nsettingsRequest.SessionTokenExpirationUnit = \"minutes\"\nsettingsRequest.EnableInactivity = true\nsettingsRequest.InactivityTime = 2\nsettingsRequest.InactivityTimeUnit = \"days\"\n\n// update the tenant settings\nerr := descopeClient.Management.Tenant().ConfigureSettings(context.Background(), \"My Tenant\", settingsRequest)\n\n// Generate tenant admin self service link for SSO Suite (valid for 24 hours)\n// sso id can be provided for a specific sso configuration\n// email can be provided to send the link to (email's templateID can be provided as well)\nlink, err := descopeClient.Management.Tenant().GenerateSSOConfigurationLink(context.Background(), \"My Tenant\", 60 * 60 * 24, \"\", \"\", \"\")\n\n// Revoke tenant admin self service link for SSO Suite\n// sso id can be provided for a specific sso configuration\nerr := descopeClient.Management.Tenant().RevokeSSOConfigurationLink(context.Background(), \"My Tenant\", \"\")\n```\n\n### Manage Users\n\nYou can create, update, delete, logout, get user history and load users, as well as search according to filters:\n\n```go\n// A user must have a loginID, other fields are optional.\n// Roles should be set directly if no tenants exist, otherwise set\n// on a per-tenant basis.\nuserReq := \u0026descope.UserRequest{}\nuserReq.Email = \"desmond@descope.com\"\nuserReq.Name = \"Desmond Copeland\"\nuserReq.GivenName = \"Desmond\"\nuserReq.FamilyName = \"Copeland\"\nuserReq.Tenants = []*descope.AssociatedTenant{\n    {TenantID: \"tenant-ID1\", Roles: []string{\"role-name1\"}},\n    {TenantID: \"tenant-ID2\"},\n}\nuserReq.SSOAppIDs = []string{\"appId1\", \"appId2\"}\nuser, err := descopeClient.Management.User().Create(context.Background(), \"desmond@descope.com\", userReq)\n\n// Alternatively, a user can be created and invited via an email message.\n// Make sure to configure the invite URL in the Descope console prior to using this function,\n// and that an email address is provided in the information.\nuserReqInvite := \u0026descope.UserRequest{}\nuserReqInvite.Email = \"desmond@descope.com\"\nuserReqInvite.Name = \"Desmond Copeland\"\nuserReqInvite.Tenants = []*descope.AssociatedTenant{\n    {TenantID: \"tenant-ID1\", Roles: []string{\"role-name1\"}},\n    {TenantID: \"tenant-ID2\"},\n}\nuserReqInvite.SSOAppIDs = []string{\"appId1\", \"appId2\"}\n// options can be nil, and in this case, value will be taken from project settings page\noptions := \u0026descope.InviteOptions{\n\tInviteURL: \"https://sub.domain.com\",\n\t// You can inject custom data into the template.\n  \t// Note that you first need to configure custom template in Descope Console\n  \t// For example: configure {{options_k1}} in the custom template, and pass { k1: 'v1' } as templateOptions\n\tTemplatesOptions: map[string]string{\"k1\": \"v1\",},\n}\nerr := descopeClient.Management.User().Invite(context.Background(), \"desmond@descope.com\", userReqInvite, options)\n\n// Invite multiple users with InviteBatch\noptions := \u0026descope.InviteOptions{\n\tInviteURL: \"https://sub.domain.com\",\n\t// You can inject custom data into the template.\n  \t// Note that you first need to configure custom template in Descope Console\n  \t// For example: configure {{options_k1}} in the custom template, and pass { k1: 'v1' } as templateOptions\n\tTemplatesOptions: map[string]string{\"k1\": \"v1\",},\n}\nbatchUsers := []*descope.BatchUser{}\nu1 := \u0026descope.BatchUser{}\nu1.LoginID = \"one\"\nu1.Email = \"one@one.com\"\nu1.Roles = []string{\"one\"}\nu1.SSOAppIDs = []string{\"appId1\", \"appId2\"}\n\nu2 := \u0026descope.BatchUser{}\nu2.LoginID = \"two\"\nu2.Email = \"two@two.com\"\nu2.Roles = []string{\"two\"}\n\nbatchUsers = append(batchUsers, u1, u2)\nusers, err := descopeClient.Management.User().InviteBatch(context.Background(), batchUsers, options)\n\n// Import users from another service by calling CreateBatch with each user's password hash\nuser := \u0026descope.BatchUser{\n    LoginID: \"desmond@descope.com\",\n    Password: \u0026descope.BatchUserPassword{\n        Hashed: \u0026descope.BatchUserPasswordHashed{\n            Bcrypt: \u0026descope.BatchUserPasswordBcrypt{\n                Hash: \"$2a$...\",\n            },\n        },\n    },\n}\nusers, err := descopeClient.Management.User().CreateBatch(context.Background(), []*descope.BatchUser{user})\n\n// Update will override all fields as is. Use carefully.\nuserReqUpdate := \u0026descope.UserRequest{}\nuserReqUpdate.Email = \"desmond@descope.com\"\nuserReqUpdate.Name = \"Desmond Copeland\"\nuserReqUpdate.Tenants = []*descope.AssociatedTenant{\n    {TenantID: \"tenant-ID1\", Roles: []string{\"role-name1\"}},\n    {TenantID: \"tenant-ID2\"},\n}\nuserReqUpdate.SSOAppIDs = []string{\"appId3\"}\nerr := descopeClient.Management.User().Update(context.Background(), \"desmond@descope.com\", userReqUpdate)\n// If needed, users can be updated using their ID as well\nerr := descopeClient.Management.User().Update(context.Background(), \"\u003cuser-id\u003e\", userReqUpdate)\n\n// On the other hand, the patch functionality will only change selected fields as is. Use carefully.\nuserReqPath := \u0026descope.UserRequest{}\nuserReqPatch.Name = \"Desmond Copeland Jr.\"\nerr := descopeClient.Management.User().Patch(context.Background(), \"desmond@descope.com\", userReqPatch)\n// If needed, users can be patched using their ID as well\nerr := descopeClient.Management.User().Patch(context.Background(), \"\u003cuser-id\u003e\", userReqPatch)\n\n// Update loginID of a user, or remove a login ID (last login ID cannot be removed)\nerr := descopeClient.Management.User().UpdateLoginID(context.Background(), \"desmond@descope.com\", \"bane@descope.com\")\n\n// Associate SSO application for a user.\nuser, err := descopeClient.Management.User().AddSSOApps(context.Background(), \"desmond@descope.com\",[]string{\"appId1\"})\n// If needed, can be using the user ID as well\nuser, err := descopeClient.Management.User().AddSSOApps(context.Background(), \"\u003cuser-id\u003e\",[]string{\"appId1\"})\n\n// Set (associate) SSO application for a user.\nuser, err := descopeClient.Management.User().SetSSOApps(context.Background(), \"desmond@descope.com\",[]string{\"appId1\", \"appId2\"})\n// If needed, can be using the user ID as well\nuser, err := descopeClient.Management.User().SetSSOApps(context.Background(), \"\u003cuser-id\u003e\",[]string{\"appId1\", \"appId2\"})\n\n// Remove SSO application association from a user.\nuser, err := descopeClient.Management.User().RemoveSSOApps(context.Background(), \"desmond@descope.com\",[]string{\"appId2\"})\n// If needed, can be using the user ID as well\nuser, err := descopeClient.Management.User().RemoveSSOApps(context.Background(), \"\u003cuser-id\u003e\",[]string{\"appId2\"})\n\n// User deletion cannot be undone. Use carefully.\nerr := descopeClient.Management.User().Delete(context.Background(), \"desmond@descope.com\")\n// If needed, users can be loaded using their ID as well\nerr := descopeClient.Management.User().Delete(context.Background(), \"\u003cuser-id\u003e\")\n\n// Load specific user\nuserRes, err := descopeClient.Management.User().Load(context.Background(), \"desmond@descope.com\")\n// If needed, users can be loaded using their ID as well\nuserRes, err := descopeClient.Management.User().Load(context.Background(), \"\u003cuser-id\u003e\")\n\n// LoadUsers by their user ID, there is an option to decide whether to get disabled/expired users or not\nusersResp, total, err := descopeClient.Management.User().LoadUsers(context.Background(), []string{userID}, false)\nif err == nil {\n    for _, user := range usersResp {\n        // Do something\n    }\n}\n\n// Search all users, optionally according to tenant and/or role filter\n// Results can be paginated using the limit and page parameters\nusersResp, total, err := descopeClient.Management.User().SearchAll(context.Background(), \u0026descope.UserSearchOptions{TenantIDs: []string{\"my-tenant-id\"}})\nif err == nil {\n    for _, user := range usersResp {\n        // Do something\n    }\n}\n\n// Logout given user from all its devices, by login ID\n// Session types (optional string array) can be added to logout request\n// Which will cause only session that were marked with this specific type to be revoked\nerr := descopeClient.Management.User().LogoutUser(context.Background(), \"\u003clogin id\u003e\")\n\n// Logout given user from all its devices, by user ID\n// Session types (optional string array) can be added to logout request\n// Which will cause only session that were marked with this specific type to be revoked\nerr := descopeClient.Management.User().LogoutUserByUserID(context.Background(), \"\u003cuser id\u003e\")\n\n// Get users' authentication history\nloginHistoryRes, err := descopeClient.Management.User().History(context.Background(), []string{\"\u003cuser id 1\u003e\", \"\u003cuser id 2\u003e\"})\nif err == nil {\n    for i := range loginHistoryRes {\n        fmt.Println(loginHistoryRes[i].UserID)\n        fmt.Println(loginHistoryRes[i].City)\n        fmt.Println(loginHistoryRes[i].Country)\n        fmt.Println(loginHistoryRes[i].IP)\n        fmt.Println(loginHistoryRes[i].LoginTime)\n    }\n}\n```\n\n#### Set or Expire User Password\n\nYou can set a new active password for a user, which they can then use to sign in. You can also set a temporary\npassword that the user will be forced to change on the next login.\n\n```go\n// Set a temporary password for the user which they'll need to replace it on next login\nerr := descopeClient.Management.User().SetTemporaryPassword(context.Background(), \"\u003clogin-id\u003e\", \"\u003csome-password\u003e\")\n\n// Set an active password for the user which they can use to login\nerr := descopeClient.Management.User().SetActivePassword(context.Background(), \"\u003clogin-id\u003e\", \"\u003csome-password\u003e\")\n```\n\nFor a user that already has a password, you can expire it to require them to change it on the next login.\n\n```go\n// Expire the user's active password\nerr := descopeClient.Management.User().ExpirePassword(context.Background(), \"\u003clogin-id\u003e\")\n\n// Later, if the user is signing in with an expired password, the returned error will be ErrPasswordExpired\nauthInfo, err := descopeClient.Auth.Password().SignIn(context.Background(), \"\u003clogin-id\u003e\", \"\u003csome-password\u003e\", w)\nif err != nil {\n     if errors.Is(err, descope.ErrPasswordExpired) {\n        // Handle a case when the error is expired, the user should replace/reset the password\n        // Use descopeClient.Auth.Password().ReplaceUserPassword(context.Background(), \"\u003clogin-id\u003e\", \"\u003csome-password\u003e\", \"\u003cnew-password\u003e\", w)\n     }\n     // Handle other errors\n}\n```\n\n### Manage Access Keys\n\nYou can create, update, delete or load access keys, as well as search according to filters:\n\n```go\n// An access key must have a name and expireTime, other fields are optional.\n// Roles should be set directly if no tenants exist, otherwise set\n// on a per-tenant basis.\n// If userID is supplied, then authorization would be ignored, and access key would be bound to the users authorization\n// If customClaims is supplied, then those claims will be present in the JWT returned by calls to ExchangeAccessKey.\n// If description is supplied, then the access key will hold a descriptive text.\n// If permittedIPs is supplied, then we will only allow using the access key from those IP addresses or CIDR ranges.\nres, err := descopeClient.Management.AccessKey().Create(context.Background(), \"access-key-1\", \"key-description\", 0, nil, []*descope.AssociatedTenant{\n\t\t{TenantID: \"tenant-ID1\", RoleNames: []string{\"role-name1\"}},\n    \t{TenantID: \"tenant-ID2\"},\n    },\n\t\"\",\n    map[string]any{\"k1\": \"v1\"},\n\tnil, map[string]any{\"attributeName\": \"attributeValue\"})\n\n// Load specific access key\nres, err := descopeClient.Management.AccessKey().Load(context.Background(), \"access-key-id\")\n\n// Search all access keys, optionally according to tenant and/or role filter\naccessKeysResp, err := descopeClient.Management.AccessKey().SearchAll(context.Background(), \u0026descope.AccessKeysSearchOptions{TenantIDs: []string{\"my-tenant-id\"}})\nif err == nil {\n    for _, accessKey := range accessKeysResp {\n        // Do something\n    }\n}\n\n// Update access key\n// If description, roles, tenants, customClaims, permittedIPs, or customAttributes are nil, their existing values will be preserved. If you want to remove them, pass an empty slice or map.\nupdatedDescription := \"Updated description\"\nres, err := descopeClient.Management.AccessKey().Update(context.Background(), \"access-key-id\", \"updated-name\", \u0026updatedDescription, []string{\"role\"}, nil, map[string]any{\"k1\": \"v1\"}, []string{\"1.2.3.4\"}, map[string]any{\"attributeName\": \"attributeValue\"})\n\n// Access keys can be deactivated to prevent usage. This can be undone using \"activate\".\nerr := descopeClient.Management.AccessKey().Deactivate(context.Background(), \"access-key-id\")\n\n// Disabled access keys can be activated once again.\nerr := descopeClient.Management.AccessKey().Activate(context.Background(), \"access-key-id\")\n\n// Access key deletion cannot be undone. Use carefully.\nerr := descopeClient.Management.AccessKey().Delete(context.Background(), \"access-key-id\")\n```\n\nExchange the access key and provide optional access key login options:\n\n```go\nloginOptions := \u0026descope.AccessKeyLoginOptions{\n\tCustomClaims: map[string]any{\"k1\": \"v1\"},\n}\nok, token, err := descopeClient.Auth.ExchangeAccessKey(context.Background(), \"accessKey\", loginOptions)\n```\n\n### Manage SSO Setting\n\nYou can manage SSO (SAML or OIDC) settings for a specific tenant.\n\n```go\n// Load all tenant SSO settings\n// You can pass ssoID in case using multi SSO and you want to load specific SSO configuration\nssoSettings, err := descopeClient.Management.SSO().LoadSettings(context.Background(), \"tenant-id\")\n\n// You can get all configured SSO settings for a specific tenant ID (for multi SSO usage)\nallSSOSettings, err := descopeClient.Management.SSO().LoadAllSettings(context.Background(), \"tenant-id\");\n\n// Configure tenant SSO by OIDC settings\noidcSettings := \u0026descope.SSOOIDCSettings{}\n// You can pass ssoID in case using multi SSO and you want to configure specific SSO configuration\nerr = descopeClient.Management.SSO().ConfigureOIDCSettings(\"tenant-id\", oidcSettings, \"\")\n// OR\n// Load all tenant SSO settings and use them to configure OIDC settings\nssoSettings, err := cc.HC.DescopeClient().Management.SSO().LoadSettings(\"tenant-id\")\nssoSettings.Oidc.Name = \"my prOvider\"\nssoSettings.Oidc.AuthURL = authorizeEndpoint\n...\nssoSettings.Oidc.Scope = []string{\"openid\", \"profile\", \"email\"}\nerr = descopeClient.Management.SSO().ConfigureOIDCSettings(\"tenant-id\", ssoSettings.Oidc, \"\")\n\n// Configure tenant SSO by SAML settings\ntenantID := \"tenant-id\" // Which tenant this configuration is for\nidpURL := \"https://idp.com\"\nentityID := \"my-idp-entity-id\"\nidpCert := \"\u003cyour-cert-here\u003e\"\nredirectURL := \"https://my-app.com/handle-saml\" // Global redirect URL for SSO/SAML\ndomain := \"domain.com\" // Users logging in from this domain will be logged in to this tenant\nsamlSettings := \u0026descope.SSOSAMLSettings{\n\tIdpURL: idpURL,\n\tIdpEntityID: entityID,\n\tIdpCert: idpCert,\n\tAttributeMapping: \u0026descope.AttributeMapping{Email: \"myEmail\", ..},\n\tRoleMappings: []*descope.RoleMapping{{..}},\n\tDefaultSSORoles: []string{\"role1\", \"role2\"},\n\tFgaMappings: map[string]*descope.FGAGroupMapping{\n\t\t\"idp-group1\": {\n\t\t\tRelations: []*descope.FGAGroupMappingRelation{\n\t\t\t\t{Resource: \"resource1\", RelationDefinition: \"relation1\", Namespace: \"namespace1\"},\n\t\t\t\t{Resource: \"resource2\", RelationDefinition: \"relation2\", Namespace: \"namespace1\"},\n\t\t\t},\n\t\t},\n\t\t\"idp-group2\": {\n\t\t\tRelations: []*descope.FGAGroupMappingRelation{\n\t\t\t\t{Resource: \"resource3\", RelationDefinition: \"relation3\", Namespace: \"namespace3\"},\n\t\t\t},\n\t\t},\n\t},\n}\n// You can pass ssoID in case using multi SSO and you want to configure specific SSO configuration\nerr = descopeClient.Management.SSO().ConfigureSAMLSettings(context.Background(), tenantID, samlSettings, redirectURL, domain)\n\n// Alternatively, configure using an SSO SAML metadata URL\nsamlSettings := \u0026descope.SSOSAMLSettingsByMetadata{\n\tIdpMetadataURL: \"https://idp.com/my-idp-metadata\",\n\tAttributeMapping: \u0026descope.AttributeMapping{Email: \"myEmail\", ..},\n\tRoleMappings: []*descope.RoleMapping{{..}},\n\tDefaultSSORoles: []string{\"role1\", \"role2\"},\n\tFgaMappings: map[string]*descope.FGAGroupMapping{\n\t\t\"group1\": {\n\t\t\tRelations: []*descope.FGAGroupMappingRelation{\n\t\t\t\t{Resource: \"resource1\", RelationDefinition: \"relation1\", Namespace: \"namespace1\"},\n\t\t\t},\n\t\t},\n\t},\n}\n// You can pass ssoID in case using multi SSO and you want to configure specific SSO configuration\nerr = descopeClient.Management.SSO().ConfigureSAMLSettingsByMetadata(context.Background(), tenantID, samlSettings, redirectURL, domain)\n\n// You can create new SSO configuration (aka multi SSO)\nssoID := \"my-new-additional-sso-id\"\ndisplayName := \"My additional SSO configuration\"\ncreatedSSOSettings, err := descopeClient.Management.SSO().NewSettings(context.Background(), \"tenant-id\", ssoID, displayName)\n\n// To delete SSO settings, call the following method\n// You can pass ssoID in case using multi SSO and you want to delete specific SSO configuration\nerr := descopeClient.Management.SSO().DeleteSettings(context.Background(), \"tenant-id\")\n```\n\nNote: Certificates should have a similar structure to:\n\n```\n-----BEGIN CERTIFICATE-----\nCertificate contents\n-----END CERTIFICATE-----\n```\n\n### Manage Password Setting\n\nYou can manage password settings for tenants and projects.\n\n```go\n// You can get password settings for the project or for a specific tenant ID.\nsettings, err := descopeClient.Management.Password().GetSettings(context.Background(), \"tenant-id\")\n\n// You can configure the project level or tenant level password settings. The update is performed as-is\n// in an overriding manner - use carefully.\nupdatedSettings := \u0026descope.PasswordSettings{\n    Enabled:               true,\n    MinLength:             8,\n    Lowercase:             true,\n    Uppercase:             true,\n    Number:                true,\n    NonAlphanumeric:       true,\n    Expiration:            true,\n    ExpirationWeeks:       3,\n    Reuse:                 true,\n    ReuseAmount:           3,\n    Lock:                  true,\n    LockAttempts:          5,\n}\nerr := descopeClient.Management.Password().ConfigureSettings(context.Background(), \"tenant-id\", updatedSettings)\n```\n\n### Manage Permissions\n\nYou can create, update, delete or load permissions:\n\n```go\n// You can optionally set a description for a permission.\nname := \"My Permission\"\ndescription := \"Optional description to briefly explain what this permission allows.\"\nerr := descopeClient.Management.Permission().Create(context.Background(), name, description)\n\n// Update will override all fields as is. Use carefully.\nnewName := \"My Updated Permission\"\ndescription = \"A revised description\",\nerr := descopeClient.Management.Permission().Update(context.Background(), name, newName, description)\n\n// Permission deletion cannot be undone. Use carefully.\ndescopeClient.Management.Permission().Delete(context.Background(), newName)\n\n// Load all permissions\nres, err := descopeClient.Management.Permission().LoadAll(context.Background())\nif err == nil {\n    for _, permission := range res {\n        // Do something\n    }\n}\n```\n\n### Manage Roles\n\nYou can create, update, delete or load roles:\n\n```go\n// You can optionally set a description and associated permission for a roles.\nname := \"My Role\"\ndescription := \"Optional description to briefly explain what this role allows.\"\npermissionNames := []string{\"My Updated Permission\"},\ntenantID := \"\" // set here tenant ID value in order to create a role for a specific tenant\ndescopeClient.Management.Role().Create(context.Background(), name, description, permissionNames, tenantID)\n\n// Update will override all fields as is. Use carefully.\nnewName := \"My Updated Role\"\ndescription = \"A revised description\",\npermissionNames = append(permissionNames, \"Another Permission\")\ndescopeClient.Management.Role().Update(context.Background(), name, tenantID, newName, description, permissionNames)\n\n// Role deletion cannot be undone. Use carefully.\ndescopeClient.Management.Role().Delete(context.Background(), newName, tenantID)\n\n// Load all roles\nres, err := descopeClient.Management.Role().LoadAll(context.Background())\nif err == nil {\n    for _, role := range res {\n        // Do something\n    }\n}\n\n// Search roles\nres, err := descopeClient.Management.Role().Search(context.Background(), \u0026descope.RoleSearchOptions{\n\tTenantIDs: []string{\"tenant1\", \"tenant2\"},\n\tRoleNames: []string{\"name1\"},\n})\nif err == nil {\n    for _, role := range res {\n        // Do something\n    }\n}\n```\n\n### Query SSO Groups\n\nYou can query SSO groups:\n\n```go\n// Load all groups for a given tenant id\nres, err := descopeClient.Management.Group().LoadAllGroups(context.Background(), \"tenant-id\")\nif err == nil {\n    for _, group := range res {\n        // Do something\n    }\n}\n\n// Load all groups for the given user IDs (can be found in the user's JWT)\nres, err := descopeClient.Management.Group().LoadAllGroupsForMembers(context.Background(), \"tenant-id\", []string{\"user-id-1\", \"user-id-2\"}, nil)\nif err == nil {\n    for _, group := range res {\n        // Do something\n    }\n}\n\n// Load all groups for the given user's loginIDs (used for sign-in)\nres, err := descopeClient.Management.Group().LoadAllGroupsForMembers(context.Background(), \"tenant-id\", nil, []string{\"login-id-1\", \"login-id-2\"})\nif err == nil {\n    for _, group := range res {\n        // Do something\n    }\n}\n\n// Load all group's members by the given group id\nres, err := descopeClient.Management.Group().LoadAllGroupMembers(context.Background(), \"tenant-id\", \"group-id\")\nif err == nil {\n    for _, group := range res {\n        // Do something with group.members\n    }\n}\n```\n\n### Manage Flows\n\nYou can list, import and export flows and screens, or the project theme:\n\n```go\n// List all your flows\nres, err := descopeClient.Management.Flow().ListFlows(context.Background())\nif err == nil {\n    fmt.Println(res.Total)\n    fmt.Println(res.Flows[0].ID)\n}\n\n// Delete flows by ids\nerr := descopeClient.Management.Flow().DeleteFlows(context.Background(), []string{\"flow-1\", \"flow-2\"})\n\n// Export the flow and it's matching screens based on the given id\nres, err := descopeClient.Management.Flow().ExportFlow(context.Background(), \"sign-up\")\nif err == nil {\n    fmt.Println(res.Flow)\n    fmt.Println(res.Screens)\n}\n\n// Import the given flow and screens as the given id\nres, err := descopeClient.Management.Group().ImportFlow(context.Background(), \"sign-up\", flow, screens)\nif err == nil {\n    fmt.Println(res.Flow)\n    fmt.Println(res.Screens)\n}\n\n// Export the current theme of the project\nres, err := descopeClient.Management.Group().ExportTheme(context.Background())\nif err == nil {\n    fmt.Println(res)\n}\n\n// Import the given theme to the project\nres, err := descopeClient.Management.Group().ImportTheme(context.Background(), theme)\nif err == nil {\n    fmt.Println(res)\n}\n```\n\n### Manage JWTs\n\nYou can add custom claims to a valid JWT.\n\n```go\nupdatedJWT, err := descopeClient.Management.JWT().UpdateJWTWithCustomClaims(context.Background(), \"original-jwt\", map[string]any{\n    \"custom-key1\": \"custom-value1\",\n    \"custom-key2\": \"custom-value2\",\n}, 60*9)\nif err != nil {\n    // handle error\n}\n```\n\nGenerate a JWT for a user, simulating a signin request.\nrefreshDuration - a custom refresh duration in seconds for the impersonation JWT, 0 will use project configuration\n```go\nconst res, err := updatedJWT, err := descopeClient.Management.JWT().SignIn(context.Background(), \"dummy\"), 0;\nif err != nil {\n    // handle error\n}\n```\n\nGenerate a JWT for a user, simulating a signup request.\nrefreshDuration - a custom refresh duration in seconds for the impersonation JWT, 0 will use project configuration\n```go\nconst res, err := updatedJWT, err := descopeClient.Management.JWT().SignUp(context.Background(), \"dummy\", 0);\nif err != nil {\n    // handle error\n}\n```\n\nGenerate a JWT for a user, simulating a signup or in request.\nrefreshDuration - a custom refresh duration in seconds for the impersonation JWT, 0 will use project configuration\n```go\nconst res, err := updatedJWT, err := descopeClient.Management.JWT().SignUpOrIn(context.Background(), \"dummy\", 0);\nif err != nil {\n    // handle error\n}\n```\n\n### Impersonate\n\nYou can impersonate to another user\nThe impersonator user must have the `impersonation` permission in order for this request to work.\nThe response would be a refresh JWT of the impersonated user\nTenantID would be the tenant to set as DCT claim, in case set\ncustomClaims - would be extra claims that are needed on the JWT\nrefreshDuration - a custom refresh duration in seconds for the impersonation JWT, 0 will use project configuration\n\n```go\nrefreshJWT, err := descopeClient.Management.JWT().Impersonate(context.Background(), \"impersonator id\", \"login id\", true, map[string]any{\"k1\":\"v1\"}, \"T1\", 0)\nif err != nil {\n    // handle error\n}\n```\n\nAfter impersonation is done, you can call `StopImpersonation`, and get a jwt of the original actor\njwt - impersonation jwt\nTenantID would be the tenant to set as DCT claim, in case set\ncustomClaims - would be extra claims that are needed on the JWT\nrefreshDuration - a custom refresh duration in seconds for the impersonation JWT, 0 will use project configuration\n\n```go\nrefreshJWT, err := descopeClient.Management.JWT().StopImpersonation(context.Background(), jwt, map[string]any{\"k1\":\"v1\"}, \"T1\", 0)\nif err != nil {\n    // handle error\n}\n```\n\n### Embedded links\n\n```go\n// Embedded links can be created to directly receive a verifiable token without sending it.\n// This token can then be verified using the magic link 'verify' function, either directly or through a flow.\ntoken, err := descopeClient.Management.User().GenerateEmbeddedLink(context.Background(), \"desmond@descope.com\", map[string]any{\"key1\":\"value1\"}, timeout int64)\n```\n\n### Audit\n\nYou can perform an audit search for either specific values or full-text across the fields. Audit search is limited to the last 30 days.\n\n```go\n// Full text search on the last 10 days\nres, err := descopeClient.Management.Audit().Search(context.Background(), \u0026descope.AuditSearchOptions{From: time.Now().AddDate(0, 0, -10), Text: \"some-text\"})\nif err == nil {\n    fmt.Println(res)\n}\n\n// Search successful logins in the last 30 days\nres, err := descopeClient.Management.Audit().Search(context.Background(), \u0026descope.AuditSearchOptions{Actions: []string{\"LoginSucceed\"}})\nif err == nil {\n    fmt.Println(res)\n}\n```\n\nYou can also create audit event with data\n\n```go\nerr := descopeClient.Management.Audit().CreateEvent(context.Background(), \u0026descope.AuditCreateOptions{\n\tAction: \"pencil.created\",\n\tType: \"info\", // info/warn/error\n\tActorID: \"UXXX\",\n\tData: map[string]string{},\n\tTenantID: \"tenant-id\",\n})\n```\n\n### Manage FGA (Fine-grained Authorization)\n\nDescope supports full relation based access control (ReBAC) using a zanzibar like schema and operations.\nA schema is comprized of types (entities like documents, folders, orgs, etc.) and each type has relation definitions and permission to define relations to other types.\n\nA simple example for a file system like schema would be:\n\n```yaml\nmodel AuthZ 1.0\n\ntype user\n\ntype org\n  relation member: user\n  relation parent: org\n\ntype folder\n  relation parent: folder\n  relation owner: user | org#member\n  relation editor: user\n  relation viewer: user\n\n  permission can_create: owner | parent.owner\n  permission can_edit: editor | can_create\n  permission can_view: viewer | can_edit\n\ntype doc\n  relation parent: folder\n  relation owner: user | org#member\n  relation editor: user\n  relation viewer: user\n\n  permission can_create: owner | parent.owner\n  permission can_edit: editor | can_create\n  permission can_view: viewer | can_edit\n```\n\nDescope SDK allows you to fully manage the schema and relations as well as perform simple (and not so simple) checks regarding the existence of relations.\n\n```go\n// Save schema\nerr := descopeClient.Management.FGA().SaveSchema(context.Background(), schema)\n\n// Create a relation between a resource and user\nerr := descopeClient.Management.FGA().CreateRelations(context.Background(), []*descope.FGARelation {\n    {\n\t\tResource: \"some-doc\",\n\t\tResourceType: \"doc\",\n\t\tRelation: \"owner\",\n\t\tTarget: \"u1\",\n\t\tTargetType: \"user\"\n    },\n})\n\n// Check if target has a relevant relation\n// The answer should be true because an owner can also view\nrelations, err := descopeClient.Management.FGA().Check(context.Background(), []*descope.FGARelation{\n    {\n\t\tResource: \"some-doc\",\n\t\tResourceType: \"doc\",\n\t\tRelation: \"can_view\",\n\t\tTarget: \"u1\",\n\t\tTargetType: \"user\"\n    }\n})\n\n// Supply a context map for CEL condition evaluation (ABAC). The backend may\n// also have attributes of its own; this map is merged on top for evaluation.\nchecks, err := descopeClient.Management.FGA().CheckWithContext(\n    context.Background(),\n    []*descope.FGARelation{{Resource: \"some-doc\", ResourceType: \"doc\", Relation: \"can_view\", Target: \"u1\", TargetType: \"user\"}},\n    map[string]any{\"ip\": \"10.0.0.1\", \"role\": \"admin\"},\n)\ninfo := checks[0].Info\nif info.Conditional \u0026\u0026 len(info.MissingContext) \u003e 0 {\n    // supply the missing context variables and retry\n}\nif info.ConditionalErr != \"\" {\n    // CEL evaluation failed; checks[0].Allowed is false\n}\n```\n\nResponse times of repeated FGA `Check` calls, especially in high volume scenarios, can be reduced to sub-millisecond scales by re-directing the calls to a Descope FGA Cache Proxy running in the same backend cluster as your application.\nAfter setting up the proxy server via the Descope provided Docker image, set the `FGACacheURL` config property to be equal to the proxy URL to enable its use in the SDK, as shown in the example below:\n\n```\n\tdescopeClient, err := client.NewWithConfig(\u0026client.Config{\n\t\t...\n\t\tFGACacheURL:         \"https://10.0.0.4\", // example FGA Cache Proxy URL, running inside the same backend cluster\n\t\t...\n\t})\n```\n\nIf you are implementing SSO mappings with FGA, you can use the following functions to retrieve the mappable schema and search for specific resources:\n\n```go\n// Load the mappable schema for a specific tenant, used for SSO FGA mappings.\n// This schema describes which resource types and relations can be mapped from IDP groups. Note not all the possible\n// resources are returned in this call since the list is capped. Use `SearchMappableResources` to find more resources.\noptions := \u0026descope.FGAMappableResourcesOptions{ResourcesLimit: 100} // optional limit for resources\nmappableSchema, err := descopeClient.Management.FGA().LoadMappableSchema(context.Background(), \"my-tenant-id\", options)\n\n// Search for specific resources that can be mapped for FGA SSO mappings based on the provided queries.\nresourcesQueries := []*descope.FGAMappableResourcesQuery{\n\t{Type: \"folder\", Queries: []string{\"f-\"}},\n\t{Type: \"doc\", Queries: []string{\"doc1\", \"doc2\"}},\n}\noptions := \u0026descope.FGAMappableResourcesOptions{ResourcesLimit: 50} // optional limit for resources\nmappableResources, err := descopeClient.Management.FGA().SearchMappableResources(context.Background(), \"my-tenant-id\", resourcesQueries, options)\n```\n\n### Manage Project\n\nYou can update a project's name, as well as clone the current project to create a new one:\n\n```go\n// Update project name\ndescopeClient.Management.Project().UpdateName(context.Background(), \"project-name\")\n\n// Set project tags (free text)\ndescopeClient.Management.Project().UpdateTags(context.Background(), []string{\"marketing\", \"health\"})\n\n// Clone the current project to a new one\n// Note that this action is supported only with a pro license or above.\nres, err := descopeClient.Management.Project().Clone(context.Background(), \"new-project-name\", \"\")\nif err == nil {\n\tfmt.Println(cloneRes)\n}\n\n// Delete the current project. Kindly note that following calls on the `descopeClient` are\n// most likely to fail because the current project has been deleted\nerr := descopeClient.Management.Project().Delete(context.Background())\n```\n\nWith using a company management key you can get a list of all the projects in the company:\n\n```go\nprojects, err := descopeClient.Management.Project().ListProjects(context.Background())\nfor _, p := range projects {\n\tfmt.Println(p.Name)\n}\n```\n\nYou can manage your project's settings and configurations by exporting a snapshot.\n\n```go\n// Exports the current state of the project\nexportRes, err := descopeClient.Management.Project().ExportSnapshot(context.Background(), nil)\nif err != nil {\n\t// unexpected failure\n}\nfiles := exportRes.Files\n```\n\nYou can also import previously exported snapshots into the same project or a different one.\n\n```go\n// Validate that an exported snapshot can be imported into the current project\nvalidateReq := \u0026descope.ValidateSnapshotRequest{Files: files}\nvalidateRes, err := descopeClient.Management.Project().ValidateSnapshot(context.Background(), validateReq)\nif err != nil {\n\t// unexpected failure\n}\nif !res.Ok {\n\t// validation failed, check Failures and MissingSecrets to fix this\n}\n\n// Get additional secrets if validation said we were missing any\ninputSecrets := ...\n\n// Import the previously exported snapshot into the current project\nimportReq := \u0026descope.ImportSnapshotRequest{Files: files, InputSecrets: inputSecrets}\nerr := descopeClient.Management.Project().ImportSnapshot(context.Background(), importReq)\nif err != nil {\n\t// handle import failure\n}\n```\n\n### Manage SSO Applications\n\nYou can create, update, delete or load sso applications:\n\n```go\n// Create OIDC SSO application\nreq := \u0026descope.OIDCApplicationRequest{Name: \"My OIDC App\", Enabled: true, LoginPageURL: \"http://dummy.com\"}\nappID, err = descopeClient.Management.SSOApplication().CreateOIDCApplication(context.Background(), req)\n\n//Create SAML SSO application\nreq := \u0026descope.SAMLApplicationRequest{\n\tID:               samlAppID,\n\tName:             \"samlApp\",\n\tEnabled:          true,\n\tLoginPageURL:     \"http://dummy.com\",\n\tEntityID:         \"eId11\",\n\tAcsURL:           \"http://dummy.com/acs\",\n\tCertificate:      \"cert\",\n\tAttributeMapping: []descope.SAMLIDPAttributeMappingInfo{{Name: \"attrName1\", Type: \"attrType1\", Value: \"attrValue1\"}},\n\tGroupsMapping: []descope.SAMLIDPGroupsMappingInfo{\n\t\t{\n\t\t\tName:       \"grpName1\",\n\t\t\tType:       \"grpType1\",\n\t\t\tFilterType: \"grpFilterType1\",\n\t\t\tValue:      \"grpValue1\",\n\t\t\tRoles:      []descope.SAMLIDPRoleGroupMappingInfo{{ID: \"rl1\", Name: \"rlName1\"}},\n\t\t},\n\t},\n\tLogoutRedirectURL: \"http://dummy.com/logout\",\n}\nappID, err = descopeClient.Management.SSOApplication().CreateSAMLApplication(context.Background(), req)\n\n// Create WS-Fed SSO application\nreq := \u0026descope.WSFedApplicationRequest{\n\tID:               wsFedAppID,\n\tName:             \"wsFedApp\",\n\tEnabled:          true,\n\tLoginPageURL:     \"http://dummy.com\",\n\tRealm:            \"realm\",\n\tReplyURL:         \"http://dummy.com/reply\",\n\tAttributeMapping: []descope.SAMLIDPAttributeMappingInfo{{Name: \"attrName1\", Type: \"attrType1\", Value: \"attrValue1\"}},\n\tGroupsMapping: []descope.SAMLIDPGroupsMappingInfo{\n\t\t{\n\t\t\tName:       \"grpName1\",\n\t\t\tType:       \"grpType1\",\n\t\t\tFilterType: \"grpFilterType1\",\n\t\t\tValue:      \"grpValue1\",\n\t\t\tRoles:      []descope.SAMLIDPRoleGroupMappingInfo{{ID: \"rl1\", Name: \"rlName1\"}},\n\t\t},\n\t},\n\tLogoutRedirectURL: \"http://dummy.com/logout\",\n}\nappID, err = descopeClient.Management.SSOApplication().CreateWSFedApplication(context.Background(), req)\n\n// Update OIDC SSO application\n// Update will override all fields as is. Use carefully.\nerr = tc.DescopeClient().Management.SSOApplication().UpdateOIDCApplication(context.TODO(),\n\t\u0026descope.OIDCApplicationRequest{ID: oidcAppID, Name: \"oidcNewAppName\"\n})\n\n// Update SAML SSO application\n// Update will override all fields as is. Use carefully.\nreq = \u0026descope.SAMLApplicationRequest{\n\tID: samlAppID, Name: \"samlNewAppName\",\n\tEnabled:      false,\n\tLoginPageURL: \"http://dummyyyy.com\",\n\tEntityID:     \"eId22\",\n\tAcsURL:       \"http://dummy.com/acs\",\n\tCertificate:  \"cert\",\n}\nerr = tc.DescopeClient().Management.SSOApplication().UpdateSAMLApplication(context.Background(), req)\n\n// Update WS-Fed SSO application\n// Update will override all fields as is. Use carefully.\nreq = \u0026descope.WSFedApplicationRequest{\n\tID: wsFedAppID, Name: \"wsFedNewAppName\",\n\tEnabled:      false,\n\tLoginPageURL: \"http://dummyyyy.com\",\n\tRealm:        \"realm\",\n\tReplyURL:     \"http://dummy.com/reply\",\n}\nerr = tc.DescopeClient().Management.SSOApplication().UpdateWSFedApplication(context.Background(), req)\n\n// Load SSO application by id\napp, err = tc.DescopeClient().Management.SSOApplication().Load(context.Background(), \"appId\")\n\n// Load all SSO applications\napps, err = tc.DescopeClient().Management.SSOApplication().LoadAll(context.Background())\n\n// SSO application deletion cannot be undone. Use carefully.\ndescopeClient.DescopeClient().Management.SSOApplication().Delete(context.Background(), \"appId\")\n```\n\n### Manage Third Party Applications\n\nYou can create, update, delete or load third party applications, while also search and delete existing consents related to any third party application:\n\n```go\n// Create third party application\nreq := \u0026descope.ThirdPartyApplicationRequest{\n\tName: \"My OIDC App\",\n\tLogo: \"data:image/jpeg;base64...\",\n\tLoginPageURL: \"http://dummy.com\",\n\tPermissionsScopes: []*descope.ThirdPartyApplicationScope{\n\t\t{Name: \"read\", Description: \"Read all\", Values: []string{\"Support\"}},\n\t},\n\tAttributesScopes: []*descope.ThirdPartyApplicationScope{\n\t\t{Name: \"base\", Description: \"Basic attribute requirements\", Values: []string{\"email\", \"phone\"}},\n\t},\n}\nappID, secret, err = descopeClient.Management.ThirdPartyApplication().CreateApplication(context.Background(), req)\n\n// Update a third party application by id\n// Update will override all fields as is. Use carefully.\nerr = tc.DescopeClient().Management.ThirdPartyApplication().UpdateApplication(context.TODO(), \u0026descope.ThirdPartyApplicationRequest{ID: \"my-id\", Name: \"my new name\"})\n\n// Load third party application by id\napp, err = tc.DescopeClient().Management.ThirdPartyApplication().LoadApplication(context.Background(), \"appId\")\n\n// Load all third party applications with options for pagination\napps, total, err = tc.DescopeClient().Management.ThirdPartyApplication().LoadAllApplications(context.Background(), \u0026descope.ThirdPartyApplicationSearchOptions{\n    Page: 0, \n    Limit: 100\n})\n\n// Delete a third party application.\n// Deletion cannot be undone. Use carefully.\nerr = descopeClient.DescopeClient().Management.ThirdPartyApplication().DeleteApplication(context.Background(), \"appId\")\n\n// Search third party applications consents by pages using a filter options, such as application id, user id, etc.\nconsents, total, err = descopeClient.DescopeClient().Management.ThirdPartyApplication().SearchConsents(context.Background(), \u0026descope.ThirdPartyApplicationConsentSearchOptions{\n\tAppID: \"appId\"\n})\n\n// Delete third party applications consents by filter options, such as application id, consent ids or user ids.\nerr = descopeClient.DescopeClient().Management.ThirdPartyApplication().DeleteConsents(context.Background(),  \u0026descope.ThirdPartyApplicationConsentDeleteOptions{\n\tUserIDs: string{\"my-user\"}\n})\n\n```\n\n### Manage Outbound Applications\n\nYou can create, update, delete, or load outbound applications:\n\n```go\n// Create an outbound application\napp, err := descopeClient.Management.OutboundApplication().CreateApplication(context.Background(), \u0026descope.OutboundApp{\n    Name: \"My Outbound App\",\n    Description: \"Description\",\n    // ... other fields ...\n})\n\n// Update an outbound application\n// Leave secret as nil, to not update it\napp, err = descopeClient.Management.OutboundApplication().UpdateApplication(context.Background(), \u0026descope.OutboundApp{\n    ID: \"app-id\",\n    Name: \"Updated Name\",\n    // ... other fields ...\n}, \u0026secret)\n\n// Delete an outbound application\nerr := descopeClient.Management.OutboundApplication().DeleteApplication(context.Background(), \"app-id\")\n\n// Load an outbound application by id\napp, err := descopeClient.Management.OutboundApplication().LoadApplication(context.Background(), \"app-id\")\n\n// Load all outbound applications\napps, err := descopeClient.Management.OutboundApplication().LoadAllApplications(context.Background())\n```\n\n### Manage Lists\n\nYou can create, update, delete, or load lists, as well as perform IP-specific operations:\n\n```go\n// Create a new list\nlistReq := \u0026descope.ListRequest{\n    Name:        \"Blocked IPs\",\n    Description: \"List of blocked IP addresses\",\n    Type:        descope.ListTypeIPs, // Can be ListTypeTexts, ListTypeIPs, or ListTypeJSON\n    Data:        []string{\"192.168.1.1\", \"10.0.0.1\"},\n}\nlist, err := descopeClient.Management.List().Create(context.Background(), listReq)\n\n// Create a JSON list\njsonListReq := \u0026descope.ListRequest{\n    Name:        \"Config Data\",\n    Description: \"Configuration settings\",\n    Type:        descope.ListTypeJSON,\n    Data:        map[string]any{\"key\": \"value\", \"setting\": 123},\n}\nlist, err := descopeClient.Management.List().Create(context.Background(), jsonListReq)\n\n// Update an existing list\n// Update will override all fields as is. Use carefully.\nupdateReq := \u0026descope.ListRequest{\n    Name:        \"Updated Blocked IPs\",\n    Description: \"Updated description\",\n    Type:        descope.ListTypeIPs,\n    Data:        []string{\"192.168.1.1\", \"10.0.0.1\", \"172.16.0.1\"},\n}\nlist, err := descopeClient.Management.List().Update(context.Background(), \"list-id\", updateReq)\n\n// Delete a list\n// List deletion cannot be undone. Use carefully.\nerr := descopeClient.Management.List().Delete(context.Background(), \"list-id\")\n\n// Load a list by ID\nlist, err := descopeClient.Management.List().Load(context.Background(), \"list-id\")\n\n// Load a list by name\nlist, err := descopeClient.Management.List().LoadByName(context.Background(), \"Blocked IPs\")\n\n// Load all lists in the project\nlists, err := descopeClient.Management.List().LoadAll(context.Background())\nif err == nil {\n    for _, list := range lists {\n        // Do something\n    }\n}\n\n// Import multiple lists\nlistsToImport := []*descope.List{\n    {\n        ID:          \"list-1\",\n        Name:        \"List 1\",\n        Type:        descope.ListTypeIPs,\n        Data:        []string{\"192.168.1.1\"},\n    },\n    {\n        ID:          \"list-2\",\n        Name:        \"List 2\",\n        Type:        descope.ListTypeTexts,\n        Data:        []string{\"item1\", \"item2\"},\n    },\n}\nerr := descopeClient.Management.List().Import(context.Background(), listsToImport)\n\n// Add IP addresses to an IP list\n// The list must be of type \"ips\". Duplicates are automatically ignored.\nerr := descopeClient.Management.List().AddIPs(context.Background(), \"list-id\", []string{\"203.0.113.1\", \"198.51.100.1\"})\n\n// Remove IP addresses from an IP list\n// The list must be of type \"ips\". Non-existent IPs are silently ignored.\nerr := descopeClient.Management.List().RemoveIPs(context.Background(), \"list-id\", []string{\"192.168.1.1\"})\n\n// Check if an IP exists in an IP list\n// The list must be of type \"ips\".\nexists, err := descopeClient.Management.List().CheckIP(context.Background(), \"list-id\", \"192.168.1.1\")\nif err == nil \u0026\u0026 exists {\n    // IP is in the list\n}\n\n// Add text items to a text list\n// The list must be of type \"texts\". Duplicates are automatically ignored.\nerr := descopeClient.Management.List().AddTexts(context.Background(), \"list-id\", []string{\"item1\", \"item2\"})\n\n// Remove text items from a text list\n// The list must be of type \"texts\". Non-existent texts are silently ignored.\nerr := descopeClient.Management.List().RemoveTexts(context.Background(), \"list-id\", []string{\"item1\"})\n\n// Check if a text exists in a text list\n// The list must be of type \"texts\".\nexists, err := descopeClient.Management.List().CheckText(context.Background(), \"list-id\", \"item1\")\nif err == nil \u0026\u0026 exists {\n    // Text is in the list\n}\n\n// Clear all data from a list\n// The list metadata (name, description, type) is preserved.\nerr := descopeClient.Management.List().Clear(context.Background(), \"list-id\")\n```\n\n### Manage Management Keys\n\nYou can create, update, delete, get, or search management keys:\n\n```go\n// Create a new management key.\n// The name is required, other fields are optional.\n// expiresIn is the expiration time in seconds (0 for no expiration).\n// permittedIPs is an optional list of IP addresses or CIDR ranges allowed to use this key.\n// reBac specifies the role-based access control configuration for the key.\nreBac := \u0026descope.MgmtKeyReBac{\n    CompanyRoles: []string{\"company-full-access\"},\n    ProjectRoles: []*descope.MgmtKeyProjectRole{\n        {\n            ProjectIDs: []string{\"project-id-1\", \"project-id-2\"},\n            Roles:      []string{\"project-admin\"},\n        },\n    },\n    TagRoles: []*descope.MgmtKeyTagRole{\n        {\n            Tags:  []string{\"production\"},\n            Roles: []string{\"read-only\"},\n        },\n    },\n}\nkey, cleartext, err := descopeClient.Management.ManagementKey().Create(\n    context.Background(),\n    \"My Management Key\",\n    \"Optional description\",\n    0, // expires in seconds (0 = no expiration)\n    []string{\"10.0.0.1\", \"192.168.1.0/24\"}, // optional permitted IPs\n    reBac,\n)\n// Save the cleartext token securely — it will not be returned again!\n\n// Get a management key by ID\nkey, err := descopeClient.Management.ManagementKey().Get(context.Background(), \"key-id\")\n\n// Search all management keys\nkeys, err := descopeClient.Management.ManagementKey().Search(context.Background(), nil)\nif err == nil {\n    for _, key := range keys {\n        // Do something\n    }\n}\n\n// Update an existing management key.\n// IMPORTANT: All parameters will override existing values. Use carefully.\nupdatedKey, err := descopeClient.Management.ManagementKey().Update(\n    context.Background(),\n    \"key-id\",\n    \"Updated Key Name\",\n    \"Updated description\",\n    []string{\"10.0.0.2\"},\n    descope.MgmtKeyActive, // MgmtKeyActive or MgmtKeyInactive\n)\n\n// Delete management keys by IDs.\n// IMPORTANT: This action is irreversible. Use carefully.\ntotal, err := descopeClient.Management.ManagementKey().Delete(context.Background(), []string{\"key-id-1\", \"key-id-2\"})\n```\n\n### Manage Descopers\n\nYou can create, update, delete, get, or list descopers (users who have access to the Descope console):\n\n```go\n// Create one or more descopers. Each descoper must have a LoginID.\n// Optionally set attributes (DisplayName, Email, Phone) and RBAC configuration.\n// Set SendInvite to true to send an invitation email.\ndescopersToCreate := []*descope.DescoperCreate{\n    {\n        LoginID: \"user@example.com\",\n        Attributes: \u0026descope.DescoperAttributes{\n            DisplayName: \"John Doe\",\n            Email:       \"user@example.com\",\n            Phone:       \"+1234567890\",\n        },\n        SendInvite: true,\n        ReBac: \u0026descope.DescoperRBAC{\n            // Set exactly one of: IsCompanyAdmin, Projects, or Tags\n            Projects: []*descope.DescoperProjectRole{\n                {\n                    ProjectIDs: []string{\"project-id-1\"},\n                    Role:       descope.DescoperRoleAdmin, // Admin | Developer | Support | Auditor\n                },\n            },\n        },\n    },\n}\ndescopers, total, err := descopeClient.Management.Descoper().Create(context.Background(), descopersToCreate)\n\n// Get a specific descoper by ID\ndescoper, err := descopeClient.Management.Descoper().Get(context.Background(), \"descoper-id\")\n\n// Update a descoper's attributes and/or RBAC configuration\n// Note: all fields that are set will override existing values\nupdatedDescoper, err := descopeClient.Management.Descoper().Update(\n    context.Background(),\n    \"descoper-id\",\n    \u0026descope.DescoperAttributes{DisplayName: \"Updated Name\"},\n    \u0026descope.DescoperRBAC{IsCompanyAdmin: true},\n)\n\n// List all descopers\ndescopers, total, err := descopeClient.Management.Descoper().List(context.Background(), nil)\nif err == nil {\n    for _, descoper := range descopers {\n        // Do something\n    }\n}\n\n// Delete a descoper. Descoper deletion cannot be undone. Use carefully.\nerr := descopeClient.Management.Descoper().Delete(context.Background(), \"descoper-id\")\n```\n\n## Code Examples\n\nYou can find various usage examples in the [examples folder](https://github.com/descope/go-sdk/blob/main/examples).\n\n### Setup\n\nTo run the examples, set your `Project ID` by setting the `DESCOPE_PROJECT_ID` env var or directly\nin the sample code.\nFind your Project ID in the [Descope console](https://app.descope.com/settings/project).\n\n```bash\nexport DESCOPE_PROJECT_ID=\u003cProjectID\u003e\n```\n\n### Run an example\n\n1. Run this command in your project to build the examples.\n\n    ```bash\n    make build\n    ```\n\n2. Run a specific example\n\n    ```bash\n    # Gin web app\n    make run-gin-example\n\n       # Gorilla Mux web app\n       make run-example\n    ```\n\n### Using Visual Studio Code\n\nTo run Run and Debug using Visual Studio Code \"Run Example: Gorilla Mux Web App\" or \"Run Example: Gin Web App\"\n\nThe examples run on TLS at the following URL: [https://localhost:8085](https://localhost:8085).\n\n## Unit Testing and Data Mocks\n\nSimplify your unit testing by using our mocks package for testing your app without the need of going out to Descope services. By that, you can simply mock responses and errors and have assertion for the incoming data of each SDK method. You can find all mocks [here](https://github.com/descope/go-sdk/blob/main/descope/tests/mocks).\n\nMock usage examples:\n\n-   [Authentication](https://github.com/descope/go-sdk/blob/main/descope/tests/mocks/auth/authenticationmock_test.go)\n-   [Management](https://github.com/descope/go-sdk/blob/main/descope/tests/mocks/mgmt/managementmock_test.go)\n\nIn the following snippet we mocked the Descope Authentication and Management SDKs, and have assertions to check the actual inputs passed to the SDK:\n\n```go\nupdateJWTWithCustomClaimsCalled := false\nvalidateSessionResponse := \"test1\"\nupdateJWTWithCustomClaimsResponse := \"test2\"\napi := DescopeClient{\n    Auth: \u0026mocksauth.MockAuthentication{\n        MockSession: mocksauth.MockSession{\n            ValidateSessionResponseSuccess: false,\n            ValidateSessionResponse:        \u0026descope.Token{JWT: validateSessionResponse},\n            ValidateSessionError:           descope.ErrPublicKey,\n        },\n    },\n    Management: \u0026mocksmgmt.MockManagement{\n        MockJWT: \u0026mocksmgmt.MockJWT{\n            UpdateJWTWithCustomClaimsResponse: updateJWTWithCustomClaimsResponse,\n            UpdateJWTWithCustomClaimsAssert: func(jwt string, customClaims map[string]any) {\n                updateJWTWithCustomClaimsCalled = true\n                assert.EqualValues(t, \"some jwt\", jwt)\n            },\n        },\n    },\n}\nok, info, err := api.Auth.ValidateAndRefreshSessionWithRequest(nil, nil)\nassert.False(t, ok)\nassert.NotEmpty(t, info)\nassert.EqualValues(t, validateSessionResponse, info.JWT)\nassert.ErrorIs(t, err, descope.ErrPublicKey)\n\nres, err := api.Management.JWT().UpdateJWTWithCustomClaims(\"some jwt\", nil)\nrequire.NoError(t, err)\nassert.True(t, updateJWTWithCustomClaimsCalled)\nassert.EqualValues(t, updateJWTWithCustomClaimsResponse, res)\n```\n\n### Utils for your end to end (e2e) tests and integration tests\n\nTo ease your e2e tests, we exposed dedicated management methods,\nthat way, you don't need to use 3rd party messaging services in order to receive sign-in/up Email, SMS, Voice call or WhatsApp, and avoid the need of parsing the code and token from them.\n\n```go\n// User for test can be created, this user will be able to generate code/link without\n// the need of 3rd party messaging services.\n// Test user must have a loginID, other fields are optional.\n// Roles should be set directly if no tenants exist, otherwise set\n// on a per-tenant basis.\nuser, err := descopeClient.Management.User().CreateTestUser(context.Background(), \"desmond@descope.com\", \"desmond@descope.com\", \"\", \"Desmond Copeland\", nil, []*descope.AssociatedTenant{\n    {TenantID: \"tenant-ID1\", RoleNames: []string{\"role-name1\"}},\n    {TenantID: \"tenant-ID2\"},\n})\n\n// Search all test users, optionally according to tenant and/or role filter\n// Results can be paginated using the limit and page parameters\nusersResp, total, err := descopeClient.Management.User().SearchAllTestUsers(context.Background(), \u0026descope.UserSearchOptions{TenantIDs: []string{\"my-tenant-id\"}})\nif err == nil {\n    for _, user := range usersResp {\n        // Do something\n    }\n}\n\n// Now test user got created, and this user will be available until you delete it,\n// you can use any management operation for test user CRUD.\n// You can also delete all test users.\nerr = descopeClient.Management.User().DeleteAllTestUsers(context.Background())\n\n// OTP code can be generated for test user, for example:\ncode, err := descopeClient.Management.User().GenerateOTPForTestUser(context.Background(), descope.MethodEmail, \"desmond@descope.com\", nil)\n// Now you can verify the code is valid (using descopeClient.Auth.OTP().VerifyCode for example)\n\n// Same as OTP, magic link can be generated for test user, for example:\nlink, err := descopeClient.Management.User().GenerateMagicLinkForTestUser(context.Background(), descope.MethodEmail, \"desmond@descope.com\", \"\", nil)\n// Now you can verify the link is valid (using descopeClient.Auth.MagicLink().Verify for example)\n\n// Enchanted link can be generated for test user, for example:\nlink, pendingRef, err := descopeClient.Management.User().GenerateEnchantedLinkForTestUser(context.Background(), \"desmond@descope.com\", \"\", nil)\n// Now you can verify the link is valid (using descopeClient.Auth.EnchantedLink().Verify for example)\n// *descope.LoginOptions can be provided to provide custom claims to the generated jwt.\n\n// Note 1: The generate code/link methods, work only for test users, will not work for regular users.\n// Note 2: In case of testing sign-in / sign-up methods with test users, need to make sure to generate the code prior calling the sign-in / sign-up methods (such as: descopeClient.Auth.MagicLink().SignUpOrIn)\n\n```\n\n# API Rate Limits\n\nHandle API rate limits by comparing the error to the ErrRateLimitExceeded error, which includes the Info map with the key \"RateLimitExceededRetryAfter.\" This key indicates how many seconds until the next valid API call can take place.\n\n```go\nerr := descopeClient.Auth.MagicLink().SignUpOrIn(context.Background(), descope.MethodEmail, \"desmond@descope.com\", \"http://myapp.com/verify-magic-link\", nil)\nif err != nil {\n    if errors.Is(err, descope.ErrRateLimitExceeded) {\n        if rateLimitErr, ok := err.(*descope.Error); ok {\n            if retryAfterSeconds, ok := rateLimitErr.Info[descope.ErrorInfoKeys.RateLimitExceededRetryAfter].(int); ok {\n                // This variable indicates how many seconds until the next valid API call can take place.\n            }\n        }\n    }\n     // handle other error cases\n}\n```\n\n## Learn More\n\nTo learn more please see the [Descope Documentation and API reference page](https://docs.descope.com/).\n\n## Contact Us\n\nIf you need help you can email [Descope Support](mailto:support@descope.com)\n\n## License\n\nThe Descope SDK for Go is licensed for use under the terms and conditions of the [MIT license Agreement](https://github.com/descope/go-sdk/blob/main/LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdescope%2Fgo-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdescope%2Fgo-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdescope%2Fgo-sdk/lists"}