From 66bc93c4b2f3fc987a763bbf0a258d62f385681c Mon Sep 17 00:00:00 2001 From: Garth <244253+xgp@users.noreply.github.com> Date: Wed, 22 Nov 2023 13:35:36 +0000 Subject: [PATCH] Deploy website - based on 2c56f43bb86c3f9fdfe0933a74c3f82877f31ffd --- 404.html | 6 +++--- access/index.html | 6 +++--- api/add-idp-mapper/index.html | 6 +++--- api/add-organization-invitation/index.html | 6 +++--- api/add-organization-member/index.html | 6 +++--- api/category/attributes/index.html | 6 +++--- api/category/events/index.html | 6 +++--- api/category/identity-providers/index.html | 6 +++--- api/category/organization-domains/index.html | 6 +++--- api/category/organization-invitations/index.html | 6 +++--- api/category/organization-memberships/index.html | 6 +++--- api/category/organization-roles/index.html | 6 +++--- api/category/organizations/index.html | 6 +++--- api/category/users/index.html | 6 +++--- api/check-organization-membership/index.html | 6 +++--- api/check-user-organization-role/index.html | 6 +++--- api/create-event/index.html | 6 +++--- api/create-idp/index.html | 6 +++--- api/create-magic-link/index.html | 6 +++--- api/create-organization-role/index.html | 6 +++--- api/create-organization/index.html | 6 +++--- api/create-portal-link/index.html | 6 +++--- api/create-realm-attribute/index.html | 6 +++--- api/create-webhook/index.html | 6 +++--- api/delete-idp-mapper/index.html | 6 +++--- api/delete-idp/index.html | 6 +++--- api/delete-organization-role/index.html | 6 +++--- api/delete-organization/index.html | 6 +++--- api/delete-realm-attribute/index.html | 6 +++--- api/delete-webhook/index.html | 6 +++--- api/get-idp-mapper/index.html | 6 +++--- api/get-idp-mappers/index.html | 6 +++--- api/get-idp/index.html | 6 +++--- api/get-idps/index.html | 6 +++--- api/get-me/index.html | 6 +++--- api/get-organization-by-id/index.html | 6 +++--- api/get-organization-domain/index.html | 6 +++--- api/get-organization-domains/index.html | 6 +++--- api/get-organization-invitations/index.html | 6 +++--- api/get-organization-memberships-count/index.html | 6 +++--- api/get-organization-memberships/index.html | 6 +++--- api/get-organization-role/index.html | 6 +++--- api/get-organization-roles/index.html | 6 +++--- api/get-organizations-count/index.html | 6 +++--- api/get-organizations/index.html | 6 +++--- api/get-realm-attribute-by-key/index.html | 6 +++--- api/get-realm-attributes/index.html | 6 +++--- api/get-user-organization-roles/index.html | 6 +++--- api/get-webhook-by-id/index.html | 6 +++--- api/get-webhooks/index.html | 6 +++--- api/grant-user-organization-role/index.html | 6 +++--- api/import-idp-json/index.html | 6 +++--- .../index.html | 6 +++--- api/list-organizations-for-the-given-user/index.html | 6 +++--- api/phase-two-admin-rest-api/index.html | 6 +++--- api/remove-organization-invitation/index.html | 6 +++--- api/remove-organization-member/index.html | 6 +++--- api/revoke-user-organization-role/index.html | 6 +++--- api/update-idp-mapper/index.html | 6 +++--- api/update-idp/index.html | 6 +++--- api/update-organization-role/index.html | 6 +++--- api/update-organization/index.html | 6 +++--- api/update-realm-attribute-by-key/index.html | 6 +++--- api/update-webhook/index.html | 6 +++--- api/verify-domain/index.html | 6 +++--- assets/css/{styles.4ecce774.css => styles.72c9e936.css} | 2 +- assets/js/c4f5d8e4.17f06b3c.js | 2 ++ ...3e.js.LICENSE.txt => c4f5d8e4.17f06b3c.js.LICENSE.txt} | 0 assets/js/c4f5d8e4.92f7193e.js | 2 -- ...{runtime~main.6e05562f.js => runtime~main.628f9366.js} | 2 +- blog/archive/index.html | 6 +++--- blog/connect/index.html | 6 +++--- blog/customizing-login-pages/index.html | 6 +++--- blog/dedicated-launch/index.html | 6 +++--- blog/events/index.html | 6 +++--- blog/free-realm/index.html | 6 +++--- blog/index.html | 6 +++--- .../index.html | 6 +++--- blog/instant-user-managemenet-and-sso-for-nuxt/index.html | 6 +++--- .../index.html | 6 +++--- blog/instant-user-managemenet-and-sso-for-vue/index.html | 6 +++--- blog/keycloak/index.html | 6 +++--- blog/licensing-change/index.html | 6 +++--- blog/magic-link/index.html | 6 +++--- blog/orgs/index.html | 6 +++--- blog/page/2/index.html | 6 +++--- blog/page/3/index.html | 6 +++--- blog/secure-django/index.html | 6 +++--- blog/securing-apps-with-keycloak/index.html | 6 +++--- blog/self-service/index.html | 6 +++--- blog/set-up-email/index.html | 6 +++--- blog/set-up-magic-links/index.html | 6 +++--- blog/sso-setup/index.html | 6 +++--- blog/tags/cockroach/index.html | 6 +++--- blog/tags/connect/index.html | 6 +++--- blog/tags/django/index.html | 6 +++--- blog/tags/email/index.html | 6 +++--- blog/tags/frameworks/index.html | 6 +++--- blog/tags/index.html | 6 +++--- blog/tags/keycloak/index.html | 6 +++--- blog/tags/keycloak/page/2/index.html | 6 +++--- blog/tags/license/index.html | 6 +++--- blog/tags/login/index.html | 6 +++--- blog/tags/magic-links/index.html | 6 +++--- blog/tags/nextjs/index.html | 6 +++--- blog/tags/nuxt/index.html | 6 +++--- blog/tags/open-source/index.html | 6 +++--- blog/tags/oss/index.html | 6 +++--- blog/tags/phase-two/index.html | 6 +++--- blog/tags/phase-two/page/2/index.html | 6 +++--- blog/tags/phase-two/page/3/index.html | 6 +++--- blog/tags/react/index.html | 6 +++--- blog/tags/reactjs/index.html | 6 +++--- blog/tags/release/index.html | 6 +++--- blog/tags/sso/index.html | 6 +++--- blog/tags/themes/index.html | 6 +++--- blog/tags/tutorial/index.html | 6 +++--- blog/tags/vue/index.html | 6 +++--- blog/tags/vuejs/index.html | 6 +++--- blog/tags/wizard/index.html | 6 +++--- blog/welcome/index.html | 6 +++--- blog/wizard/index.html | 6 +++--- carousel/index.html | 6 +++--- docs/about/index.html | 6 +++--- docs/admin-portal/foo/index.html | 6 +++--- docs/admin-portal/index.html | 6 +++--- docs/admin-portal/portal-link/index.html | 6 +++--- docs/affiliate/index.html | 6 +++--- docs/api-description/index.html | 6 +++--- docs/api/authentication/index.html | 6 +++--- docs/api/index.html | 6 +++--- docs/api/sdks/index.html | 6 +++--- docs/api/service-accounts/index.html | 6 +++--- docs/audit-logs/access/index.html | 6 +++--- docs/audit-logs/admin/index.html | 6 +++--- docs/audit-logs/api/index.html | 6 +++--- docs/audit-logs/index.html | 6 +++--- docs/audit-logs/system/index.html | 6 +++--- docs/audit-logs/webhooks/index.html | 6 +++--- docs/authentication/complex-flows/index.html | 6 +++--- docs/authentication/index.html | 6 +++--- docs/authentication/magic-links/index.html | 6 +++--- docs/authentication/otps/index.html | 6 +++--- docs/authentication/social-login/index.html | 6 +++--- docs/authentication/sso/index.html | 6 +++--- docs/authentication/understanding-flows/index.html | 6 +++--- docs/authentication/username-password/index.html | 6 +++--- docs/authentication/webauthn/index.html | 6 +++--- docs/careers/index.html | 6 +++--- docs/getting-started/configuration/index.html | 6 +++--- docs/getting-started/customizing-ui/index.html | 6 +++--- docs/getting-started/email/index.html | 6 +++--- docs/getting-started/index.html | 6 +++--- docs/getting-started/launch-checklist/index.html | 6 +++--- docs/getting-started/sample/index.html | 6 +++--- docs/hosting/connect/index.html | 6 +++--- docs/hosting/index.html | 6 +++--- docs/hosting/kubernetes/index.html | 6 +++--- docs/introduction/documentation/index.html | 6 +++--- docs/introduction/index.html | 6 +++--- docs/introduction/keycloak/index.html | 6 +++--- docs/introduction/open-source/index.html | 6 +++--- docs/organizations/attributes/index.html | 6 +++--- docs/organizations/identity-providers/index.html | 6 +++--- docs/organizations/index.html | 6 +++--- docs/organizations/invitations/index.html | 6 +++--- docs/organizations/membership/index.html | 6 +++--- docs/organizations/portal/index.html | 6 +++--- docs/organizations/roles/index.html | 6 +++--- docs/privacy/index.html | 6 +++--- docs/securing-applications/django/index.html | 6 +++--- docs/securing-applications/index.html | 6 +++--- docs/securing-applications/javascript/index.html | 6 +++--- docs/securing-applications/next/index.html | 6 +++--- docs/securing-applications/nuxt/index.html | 6 +++--- docs/securing-applications/react/index.html | 6 +++--- docs/securing-applications/vue/index.html | 6 +++--- docs/self-service/dedicated-clusters/index.html | 6 +++--- docs/self-service/deployments/index.html | 6 +++--- docs/self-service/index.html | 6 +++--- docs/self-service/your-organization/index.html | 6 +++--- docs/service-agreement/index.html | 6 +++--- docs/sla/index.html | 6 +++--- docs/sso/index.html | 6 +++--- docs/sso/magic-link/index.html | 6 +++--- docs/sso/setup/index.html | 6 +++--- docs/sso/sso-without-auth/index.html | 6 +++--- docs/sso/wizards/index.html | 6 +++--- docs/terms/index.html | 6 +++--- docs/user-migration/api/index.html | 6 +++--- docs/user-migration/index.html | 6 +++--- index.html | 8 ++++---- product/adminportal/index.html | 6 +++--- product/identity/index.html | 6 +++--- product/onprem/index.html | 6 +++--- product/organizations/index.html | 6 +++--- product/sso/index.html | 6 +++--- search/index.html | 6 +++--- 198 files changed, 584 insertions(+), 584 deletions(-) rename assets/css/{styles.4ecce774.css => styles.72c9e936.css} (66%) create mode 100644 assets/js/c4f5d8e4.17f06b3c.js rename assets/js/{c4f5d8e4.92f7193e.js.LICENSE.txt => c4f5d8e4.17f06b3c.js.LICENSE.txt} (100%) delete mode 100644 assets/js/c4f5d8e4.92f7193e.js rename assets/js/{runtime~main.6e05562f.js => runtime~main.628f9366.js} (99%) diff --git a/404.html b/404.html index 03f48c176..1708ab735 100644 --- a/404.html +++ b/404.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/access/index.html b/access/index.html index b433dba92..9d6fa51f0 100644 --- a/access/index.html +++ b/access/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/api/add-idp-mapper/index.html b/api/add-idp-mapper/index.html index 0455dd154..d30b599c1 100644 --- a/api/add-idp-mapper/index.html +++ b/api/add-idp-mapper/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Add a mapper to identity provider

Add a mapper to identity provider

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • alias string required
Request Body required
  • config object
  • id string
  • identityProviderAlias string
  • identityProviderMapper string
  • name string
Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/add-organization-invitation/index.html b/api/add-organization-invitation/index.html index 0c105cbec..0ffc6f164 100644 --- a/api/add-organization-invitation/index.html +++ b/api/add-organization-invitation/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Create an invitation to an organization

Create an invitation to an organization

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Request Body required
  • email string
  • send boolean
  • inviterId string
  • redirectUri string
  • roles string[]
Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/add-organization-member/index.html b/api/add-organization-member/index.html index 2a310d7d2..57b253435 100644 --- a/api/add-organization-member/index.html +++ b/api/add-organization-member/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Add an organization member

Add the specified user to the specified organization as a member

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • userId string required

    user id

Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/category/attributes/index.html b/api/category/attributes/index.html index 130ed3da4..4700e3e2b 100644 --- a/api/category/attributes/index.html +++ b/api/category/attributes/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/api/category/events/index.html b/api/category/events/index.html index b73e95df1..97aa3add2 100644 --- a/api/category/events/index.html +++ b/api/category/events/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/api/category/identity-providers/index.html b/api/category/identity-providers/index.html index 7e6c13cf2..400be8d2c 100644 --- a/api/category/identity-providers/index.html +++ b/api/category/identity-providers/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/api/category/organization-domains/index.html b/api/category/organization-domains/index.html index 57dfcadf7..9c0f590d1 100644 --- a/api/category/organization-domains/index.html +++ b/api/category/organization-domains/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/api/category/organization-invitations/index.html b/api/category/organization-invitations/index.html index 15a0a4d5e..ad1791462 100644 --- a/api/category/organization-invitations/index.html +++ b/api/category/organization-invitations/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/api/category/organization-memberships/index.html b/api/category/organization-memberships/index.html index 7b073c912..9b62fbd50 100644 --- a/api/category/organization-memberships/index.html +++ b/api/category/organization-memberships/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/api/category/organization-roles/index.html b/api/category/organization-roles/index.html index 2a6532bbe..e2267be24 100644 --- a/api/category/organization-roles/index.html +++ b/api/category/organization-roles/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/api/category/organizations/index.html b/api/category/organizations/index.html index 0d21359cf..5d04a730a 100644 --- a/api/category/organizations/index.html +++ b/api/category/organizations/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/api/category/users/index.html b/api/category/users/index.html index ae76f783a..bce984ce5 100644 --- a/api/category/users/index.html +++ b/api/category/users/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content
- + \ No newline at end of file diff --git a/api/check-organization-membership/index.html b/api/check-organization-membership/index.html index 508a8b03e..4eba6fc70 100644 --- a/api/check-organization-membership/index.html +++ b/api/check-organization-membership/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Check if a user is a member of an organization

Check if a user is a member of an organization

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • userId string required

    user id

Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/check-user-organization-role/index.html b/api/check-user-organization-role/index.html index 3b4d040e5..66c2500db 100644 --- a/api/check-user-organization-role/index.html +++ b/api/check-user-organization-role/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Check if a user has an organization role

Check if a user has an organization role

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • name string required

    organization role name

  • userId string required

    user id

Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/create-event/index.html b/api/create-event/index.html index 6ee1cbb60..da1bbed25 100644 --- a/api/create-event/index.html +++ b/api/create-event/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Create a new audit log event

Create a new audit log event

Path Parameters
  • realm string required

    realm name (not id!)

Request Body required

JSON body

  • uid string
  • time integer
  • realmId string
  • organizationId string
  • type string
  • representation string
  • operationType string
  • resourcePath string
  • resourceType string
  • error string
  • authDetails object
  • realmId string
  • clientId string
  • userId string
  • ipAddress string
  • username string
  • sessionId string
  • details object
Responses

Event received

Loading...
- + \ No newline at end of file diff --git a/api/create-idp/index.html b/api/create-idp/index.html index 9b5eaf6c1..19b2255d9 100644 --- a/api/create-idp/index.html +++ b/api/create-idp/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Create a new identity provider for this organization

Create a new identity provider for this organization

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Request Body required

JSON body

  • addReadTokenRoleOnCreate boolean
  • alias string
  • config object
  • displayName string
  • enabled boolean
  • firstBrokerLoginFlowAlias string
  • internalId string
  • linkOnly boolean
  • postBrokerLoginFlowAlias string
  • providerId string
  • storeToken boolean
  • trustEmail boolean
Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/create-magic-link/index.html b/api/create-magic-link/index.html index fa0b9664f..af86d4d22 100644 --- a/api/create-magic-link/index.html +++ b/api/create-magic-link/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Create a magic link to log in a user

Path Parameters
  • realm string required

    realm name (not id!)

Request Body required

JSON body

  • email string required
  • client_id string required
  • redirect_uri string required
  • expiration_seconds integer
  • force_create boolean
  • send_email boolean
Responses

Magic Link created

Loading...
- + \ No newline at end of file diff --git a/api/create-organization-role/index.html b/api/create-organization-role/index.html index 1d906cf70..195ab97ba 100644 --- a/api/create-organization-role/index.html +++ b/api/create-organization-role/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Create a new role for this organization

Create a new role for this organization

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Request Body required
  • id string
  • name string
  • description string
Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/create-organization/index.html b/api/create-organization/index.html index 81536b553..6087baa6e 100644 --- a/api/create-organization/index.html +++ b/api/create-organization/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Create a new organization

Create a new organization

Path Parameters
  • realm string required

    realm name (not id!)

Request Body required
  • id string
  • name string
  • displayName string
  • url string
  • realm string
  • domains string[]
  • attributes object
  • property name* object
  • Array [
  • string
  • ]
Responses

success

Response Headers
  • Location string

    URI indicating the ID of the new resource.

Loading...
- + \ No newline at end of file diff --git a/api/create-portal-link/index.html b/api/create-portal-link/index.html index ce1003206..d200ce248 100644 --- a/api/create-portal-link/index.html +++ b/api/create-portal-link/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Create a link for this organizations admin portal. This link encodes an action token on behalf of the organization's default admin user, or the user that is optionally specified in this request. The user specified must be a member of this organization, and have full organization admin roles.

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Request Body
  • userId string
Responses

success


Schema
  • user string
  • link string
  • redirect string
Loading...
- + \ No newline at end of file diff --git a/api/create-realm-attribute/index.html b/api/create-realm-attribute/index.html index 73955af29..105f6baea 100644 --- a/api/create-realm-attribute/index.html +++ b/api/create-realm-attribute/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Create a new realm attribute

Create a new realm attribute

Path Parameters
  • realm string required

    realm name (not id!)

Request Body required

JSON body

  • name string
  • value string
  • realm string
Responses

Attribute created

Loading...
- + \ No newline at end of file diff --git a/api/create-webhook/index.html b/api/create-webhook/index.html index c75f6146e..36531080e 100644 --- a/api/create-webhook/index.html +++ b/api/create-webhook/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Create a new webhook

Create a new webhook

Path Parameters
  • realm string required

    realm name (not id!)

Request Body required

JSON body

  • attributes object
  • id string
  • enabled boolean
  • url string
  • secret string
  • createdBy string
  • createdAt string
  • realm string
  • eventTypes string[]
Responses

Webhook created

Loading...
- + \ No newline at end of file diff --git a/api/delete-idp-mapper/index.html b/api/delete-idp-mapper/index.html index 5a47debc8..dafad73e1 100644 --- a/api/delete-idp-mapper/index.html +++ b/api/delete-idp-mapper/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Delete a mapper for the identity provider

Delete a mapper for the identity provider

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • alias string required
  • id string required

    Mapper id

Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/delete-idp/index.html b/api/delete-idp/index.html index 2dfa916e4..dde526b78 100644 --- a/api/delete-idp/index.html +++ b/api/delete-idp/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Delete the identity provider

Delete the identity provider

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • alias string required

    Identity Provider alias

Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/delete-organization-role/index.html b/api/delete-organization-role/index.html index 038915292..a3d40aa3c 100644 --- a/api/delete-organization-role/index.html +++ b/api/delete-organization-role/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Delete this organization role

Delete this organization role

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • name string required

    organization role name

Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/delete-organization/index.html b/api/delete-organization/index.html index c5585ffb9..d8678d3c1 100644 --- a/api/delete-organization/index.html +++ b/api/delete-organization/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Delete the organization

Delete the organization

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/delete-realm-attribute/index.html b/api/delete-realm-attribute/index.html index 7d9ada07c..469216945 100644 --- a/api/delete-realm-attribute/index.html +++ b/api/delete-realm-attribute/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Delete the realm attribute

Delete the realm attribute

Path Parameters
  • realm string required

    realm name (not id!)

  • attributeKey string required

    attribute key

Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/delete-webhook/index.html b/api/delete-webhook/index.html index 2aa3e4ae1..81dd3430c 100644 --- a/api/delete-webhook/index.html +++ b/api/delete-webhook/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Delete the webhook

Delete the webhook

Path Parameters
  • realm string required

    realm name (not id!)

  • webhookId string required

    webhook id

Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/get-idp-mapper/index.html b/api/get-idp-mapper/index.html index 975b206b1..c15382df2 100644 --- a/api/get-idp-mapper/index.html +++ b/api/get-idp-mapper/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get mapper by id for the identity provider

Get mapper by id for the identity provider

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • alias string required
  • id string required

    Mapper id

Responses

success


Schema
  • config object
  • id string
  • identityProviderAlias string
  • identityProviderMapper string
  • name string
Loading...
- + \ No newline at end of file diff --git a/api/get-idp-mappers/index.html b/api/get-idp-mappers/index.html index 08c742261..968aa50e2 100644 --- a/api/get-idp-mappers/index.html +++ b/api/get-idp-mappers/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get mappers for identity provider

Get mappers for identity provider

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • alias string required
Responses

success


Schema
  • Array [
  • config object
  • id string
  • identityProviderAlias string
  • identityProviderMapper string
  • name string
  • ]
Loading...
- + \ No newline at end of file diff --git a/api/get-idp/index.html b/api/get-idp/index.html index 90775af22..ca6899e69 100644 --- a/api/get-idp/index.html +++ b/api/get-idp/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get identity provider for this organization by alias

Get identity provider for this organization by alias

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • alias string required

    Identity Provider alias

Responses

success


Schema
  • addReadTokenRoleOnCreate boolean
  • alias string
  • config object
  • displayName string
  • enabled boolean
  • firstBrokerLoginFlowAlias string
  • internalId string
  • linkOnly boolean
  • postBrokerLoginFlowAlias string
  • providerId string
  • storeToken boolean
  • trustEmail boolean
Loading...
- + \ No newline at end of file diff --git a/api/get-idps/index.html b/api/get-idps/index.html index 269603ae1..9839f52eb 100644 --- a/api/get-idps/index.html +++ b/api/get-idps/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get identity providers for this organization

Get identity providers for this organization

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Responses

success


Schema
  • Array [
  • addReadTokenRoleOnCreate boolean
  • alias string
  • config object
  • displayName string
  • enabled boolean
  • firstBrokerLoginFlowAlias string
  • internalId string
  • linkOnly boolean
  • postBrokerLoginFlowAlias string
  • providerId string
  • storeToken boolean
  • trustEmail boolean
  • ]
Loading...
- + \ No newline at end of file diff --git a/api/get-me/index.html b/api/get-me/index.html index e46f7c6a1..448b9d160 100644 --- a/api/get-me/index.html +++ b/api/get-me/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get orgs and roles for authenticated user

Get a list of all organizations that the user is a member and their roles in those organizations. Similar idea to /userinfo in OIDC.

Path Parameters
  • realm string required

    realm name (not id!)

Responses

success


Schema
  • property name* object (MyOrganizationRepresentation)
  • name string
  • displayName string
  • url string
  • attributes object
  • property name* object
  • Array [
  • string
  • ]
  • roles string[]
Loading...
- + \ No newline at end of file diff --git a/api/get-organization-by-id/index.html b/api/get-organization-by-id/index.html index 64e92fed6..624848ab1 100644 --- a/api/get-organization-by-id/index.html +++ b/api/get-organization-by-id/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get organization by id

Get organization by id

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Responses

success


Schema
  • id string
  • name string
  • displayName string
  • url string
  • realm string
  • domains string[]
  • attributes object
  • property name* object
  • Array [
  • string
  • ]
Loading...
- + \ No newline at end of file diff --git a/api/get-organization-domain/index.html b/api/get-organization-domain/index.html index 3b5ae2ba5..d557b1660 100644 --- a/api/get-organization-domain/index.html +++ b/api/get-organization-domain/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get details for a domain owned by an organization

Get details for a domain owned by an organization

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • domainName string required

    domain name

Responses

success


Schema
  • domain_name string
  • verified boolean
  • record_key string
  • record_value string
  • type string
Loading...
- + \ No newline at end of file diff --git a/api/get-organization-domains/index.html b/api/get-organization-domains/index.html index 406dfb90e..8eb6c9607 100644 --- a/api/get-organization-domains/index.html +++ b/api/get-organization-domains/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get details for all domains owned by an organization

Get details for all domains owned by an organization

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Responses

success


Schema
  • Array [
  • domain_name string
  • verified boolean
  • record_key string
  • record_value string
  • type string
  • ]
Loading...
- + \ No newline at end of file diff --git a/api/get-organization-invitations/index.html b/api/get-organization-invitations/index.html index b689dd5eb..848a648d2 100644 --- a/api/get-organization-invitations/index.html +++ b/api/get-organization-invitations/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get organization invitations

Get a paginated list of invitations to an organization, using an optional search query for email address.

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Query Parameters
  • search string
  • first int32
  • max int32
Responses

success


Schema
  • Array [
  • id string
  • email string
  • inviterId string
  • organizationId string
  • roles string[]
  • ]
Loading...
- + \ No newline at end of file diff --git a/api/get-organization-memberships-count/index.html b/api/get-organization-memberships-count/index.html index cf8bf89c6..16a8d4eec 100644 --- a/api/get-organization-memberships-count/index.html +++ b/api/get-organization-memberships-count/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get organization members count

Get total number of members of a given organization

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Responses

success


Schema
  • integer int32
Loading...
- + \ No newline at end of file diff --git a/api/get-organization-memberships/index.html b/api/get-organization-memberships/index.html index 0f2ca16dc..76723ce4d 100644 --- a/api/get-organization-memberships/index.html +++ b/api/get-organization-memberships/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get organization memberships

Get a paginated list of users who are a member of the specified organization.

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Query Parameters
  • search string
  • first int32
  • max int32
Responses

success


Schema
  • Array [
  • attributes object
  • createdTimestamp int64
  • email string
  • emailVerified boolean
  • enabled boolean
  • firstName string
  • groups string[]
  • id string
  • lastName string
  • username string
  • ]
Loading...
- + \ No newline at end of file diff --git a/api/get-organization-role/index.html b/api/get-organization-role/index.html index b32572d0c..aebec6fa5 100644 --- a/api/get-organization-role/index.html +++ b/api/get-organization-role/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get role for this organization by name

Get role for this organization by name

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • name string required

    organization role name

Responses

success


Schema
  • id string
  • name string
  • description string
Loading...
- + \ No newline at end of file diff --git a/api/get-organization-roles/index.html b/api/get-organization-roles/index.html index 9ce4c5e4b..35573be56 100644 --- a/api/get-organization-roles/index.html +++ b/api/get-organization-roles/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get roles for this organization

Get roles for this organization

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Responses

success


Schema
  • Array [
  • id string
  • name string
  • description string
  • ]
Loading...
- + \ No newline at end of file diff --git a/api/get-organizations-count/index.html b/api/get-organizations-count/index.html index cee19448c..2262da6e7 100644 --- a/api/get-organizations-count/index.html +++ b/api/get-organizations-count/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get organizations count

Get a count of organizations using an optional search query.

Path Parameters
  • realm string required

    realm name (not id!)

Query Parameters
  • search string
Responses

success


Schema
  • integer int32
Loading...
- + \ No newline at end of file diff --git a/api/get-organizations/index.html b/api/get-organizations/index.html index 40c98ef04..2e6853534 100644 --- a/api/get-organizations/index.html +++ b/api/get-organizations/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get organizations

Get a paginated list of organizations using optional search query parameters.

Path Parameters
  • realm string required

    realm name (not id!)

Query Parameters
  • search string

    search by name

  • first int32
  • max int32
  • q string

    search by attributes using the format k1:v1,k2:v2

Responses

success


Schema
  • Array [
  • id string
  • name string
  • displayName string
  • url string
  • realm string
  • domains string[]
  • attributes object
  • property name* object
  • Array [
  • string
  • ]
  • ]
Loading...
- + \ No newline at end of file diff --git a/api/get-realm-attribute-by-key/index.html b/api/get-realm-attribute-by-key/index.html index 03267736a..b0aa1c40e 100644 --- a/api/get-realm-attribute-by-key/index.html +++ b/api/get-realm-attribute-by-key/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get realm attribute by key

Get realm attribute by key

Path Parameters
  • realm string required

    realm name (not id!)

  • attributeKey string required

    attribute key

Responses

success


Schema
  • name string
  • value string
  • realm string
Loading...
- + \ No newline at end of file diff --git a/api/get-realm-attributes/index.html b/api/get-realm-attributes/index.html index b38c2b00d..b5673c92c 100644 --- a/api/get-realm-attributes/index.html +++ b/api/get-realm-attributes/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get realm attributes

Get a list of attributes for this realm

Path Parameters
  • realm string required

    realm name (not id!)

Responses

success


Schema
  • Array [
  • property name* object (RealmAttributeRepresentation)
  • name string
  • value string
  • realm string
  • ]
Loading...
- + \ No newline at end of file diff --git a/api/get-user-organization-roles/index.html b/api/get-user-organization-roles/index.html index 03760866b..765d27f86 100644 --- a/api/get-user-organization-roles/index.html +++ b/api/get-user-organization-roles/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get users with this organization role

Get users with this organization role

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • name string required

    organization role name

Responses

success


Schema
  • Array [
  • attributes object
  • createdTimestamp int64
  • email string
  • emailVerified boolean
  • enabled boolean
  • firstName string
  • groups string[]
  • id string
  • lastName string
  • username string
  • ]
Loading...
- + \ No newline at end of file diff --git a/api/get-webhook-by-id/index.html b/api/get-webhook-by-id/index.html index 6ac9c77a3..2abdca7be 100644 --- a/api/get-webhook-by-id/index.html +++ b/api/get-webhook-by-id/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get webhook by id

Get webhook by id

Path Parameters
  • realm string required

    realm name (not id!)

  • webhookId string required

    webhook id

Responses

success


Schema
  • attributes object
  • id string
  • enabled boolean
  • url string
  • secret string
  • createdBy string
  • createdAt string
  • realm string
  • eventTypes string[]
Loading...
- + \ No newline at end of file diff --git a/api/get-webhooks/index.html b/api/get-webhooks/index.html index d90e1acf6..7026b2487 100644 --- a/api/get-webhooks/index.html +++ b/api/get-webhooks/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Get webhooks

Get a list of webhooks for this realm

Path Parameters
  • realm string required

    realm name (not id!)

Responses

success


Schema
  • Array [
  • attributes object
  • id string
  • enabled boolean
  • url string
  • secret string
  • createdBy string
  • createdAt string
  • realm string
  • eventTypes string[]
  • ]
Loading...
- + \ No newline at end of file diff --git a/api/grant-user-organization-role/index.html b/api/grant-user-organization-role/index.html index 4ce20973e..f3e4641d8 100644 --- a/api/grant-user-organization-role/index.html +++ b/api/grant-user-organization-role/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Grant a user an organization role

Grant the specified user to the specified organization role

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

  • name string required

    organization role name

  • userId string required

    user id

Responses

success

Loading...
- + \ No newline at end of file diff --git a/api/import-idp-json/index.html b/api/import-idp-json/index.html index 8e71f7e18..4aa8afa7c 100644 --- a/api/import-idp-json/index.html +++ b/api/import-idp-json/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
Skip to main content

Import identity provider from uploaded JSON file

Import identity provider from uploaded JSON file

Path Parameters
  • realm string required

    realm name (not id!)

  • orgId string required

    organization id

Responses

success


Schema
    Loading...
    - + \ No newline at end of file diff --git a/api/list-organization-roles-for-the-given-user-and-org/index.html b/api/list-organization-roles-for-the-given-user-and-org/index.html index d906f3994..acdb89b80 100644 --- a/api/list-organization-roles-for-the-given-user-and-org/index.html +++ b/api/list-organization-roles-for-the-given-user-and-org/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content

    List organization roles for the given user and org

    List organization roles for the given user and org

    Path Parameters
    • realm string required

      realm name (not id!)

    • userId string required

      user id

    • orgId string required

      organization id

    Responses

    success


    Schema
    • Array [
    • id string
    • name string
    • description string
    • ]
    Loading...
    - + \ No newline at end of file diff --git a/api/list-organizations-for-the-given-user/index.html b/api/list-organizations-for-the-given-user/index.html index d57813bfc..bab628677 100644 --- a/api/list-organizations-for-the-given-user/index.html +++ b/api/list-organizations-for-the-given-user/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content

    List organizations for the given user

    List organizations for the given user

    Path Parameters
    • realm string required

      realm name (not id!)

    • userId string required

      user id

    Responses

    success


    Schema
    • Array [
    • id string
    • name string
    • displayName string
    • url string
    • realm string
    • domains string[]
    • attributes object
    • property name* object
    • Array [
    • string
    • ]
    • ]
    Loading...
    - + \ No newline at end of file diff --git a/api/phase-two-admin-rest-api/index.html b/api/phase-two-admin-rest-api/index.html index 44367f610..632169b22 100644 --- a/api/phase-two-admin-rest-api/index.html +++ b/api/phase-two-admin-rest-api/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content
    Version: v1

    Phase Two Admin REST API

    This is a REST API reference for the Phase Two Keycloak custom resources. These are extensions to the standard Keycloak Admin REST API.

    Base URI format

    Paths specified in the documentation are relative to the the base URI.

    • Format: https://<host>:<port>/auth/realms
    • Example: https://app.phasetwo.io/auth/realms

    Authentication

    Authentication is achieved by using the Authentication: Bearer <token> header in all requests. This is either the access token received from a normal authentication, or by a request directly to the OpenID Connect token endpoint.

    It is recommended that you use a Keycloak Admin Client, such as this one for Javascript, as they take care of authentication, getting an access token, and refreshing it when it expires.

    Client credentials grant example

    POST /auth/realms/test-realm/protocol/openid-connect/token
    Host: app.phasetwo.io
    Accept: application/json
    Content-type: application/x-www-form-urlencoded

    grant_type=client_credentials&client_id=admin-cli&client_secret=fd649804-3a74-4d69-acaa-8f065c6b7da1

    Password grant example

    POST /auth/realms/test-realm/protocol/openid-connect/token
    Host: app.phasetwo.io
    Accept: application/json
    Content-type: application/x-www-form-urlencoded

    grant_type=password&username=uname@foo.com&password=pwd123AZY&client_id=admin-cli

    SDKs

    Modern API libraries are available for several common languages. These are available as open source at the links below, or you can choose to generate your own using our OpenAPI spec file.

    LanguageLibrary
    Java (and other JVM langs)https://github.com/p2-inc/phasetwo-java
    JavaScript/TypeScripthttps://github.com/p2-inc/phasetwo-js
    Pythonhttps://github.com/p2-inc/phasetwo-python

    Authentication


    Security Scheme Type:http
    HTTP Authorization Scheme:bearer
    - + \ No newline at end of file diff --git a/api/remove-organization-invitation/index.html b/api/remove-organization-invitation/index.html index f94307fe0..7a808728a 100644 --- a/api/remove-organization-invitation/index.html +++ b/api/remove-organization-invitation/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content

    Remove a pending invitation

    Remove a pending invitation

    Path Parameters
    • realm string required

      realm name (not id!)

    • orgId string required

      organization id

    • invitationId string required

      invitation id

    Responses

    success

    Loading...
    - + \ No newline at end of file diff --git a/api/remove-organization-member/index.html b/api/remove-organization-member/index.html index b7e0a20ac..a4b7d8e58 100644 --- a/api/remove-organization-member/index.html +++ b/api/remove-organization-member/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content

    Remove an organization member

    Remove the specified user from the specified organization as a member

    Path Parameters
    • realm string required

      realm name (not id!)

    • orgId string required

      organization id

    • userId string required

      user id

    Responses

    success

    Loading...
    - + \ No newline at end of file diff --git a/api/revoke-user-organization-role/index.html b/api/revoke-user-organization-role/index.html index 4bef6b127..0808db7fa 100644 --- a/api/revoke-user-organization-role/index.html +++ b/api/revoke-user-organization-role/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content

    Revoke an organization role from a user

    Revoke the specified organization role from the specified user

    Path Parameters
    • realm string required

      realm name (not id!)

    • orgId string required

      organization id

    • name string required

      organization role name

    • userId string required

      user id

    Responses

    success

    Loading...
    - + \ No newline at end of file diff --git a/api/update-idp-mapper/index.html b/api/update-idp-mapper/index.html index ce177dea5..64e269a5e 100644 --- a/api/update-idp-mapper/index.html +++ b/api/update-idp-mapper/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content

    Update a mapper for the identity provider

    Update a mapper for the identity provider

    Path Parameters
    • realm string required

      realm name (not id!)

    • orgId string required

      organization id

    • alias string required
    • id string required

      Mapper id

    Request Body required
    • config object
    • id string
    • identityProviderAlias string
    • identityProviderMapper string
    • name string
    Responses

    success

    Loading...
    - + \ No newline at end of file diff --git a/api/update-idp/index.html b/api/update-idp/index.html index 82d993465..9bfc0f5b8 100644 --- a/api/update-idp/index.html +++ b/api/update-idp/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content

    Update identity provider for this organization by alias

    Update identity provider for this organization by alias

    Path Parameters
    • realm string required

      realm name (not id!)

    • orgId string required

      organization id

    • alias string required

      Identity Provider alias

    Request Body required
    • addReadTokenRoleOnCreate boolean
    • alias string
    • config object
    • displayName string
    • enabled boolean
    • firstBrokerLoginFlowAlias string
    • internalId string
    • linkOnly boolean
    • postBrokerLoginFlowAlias string
    • providerId string
    • storeToken boolean
    • trustEmail boolean
    Responses

    success

    Loading...
    - + \ No newline at end of file diff --git a/api/update-organization-role/index.html b/api/update-organization-role/index.html index 10080f4c2..fb19d32f7 100644 --- a/api/update-organization-role/index.html +++ b/api/update-organization-role/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content

    Update role for this organization

    Update role for this organization

    Path Parameters
    • realm string required

      realm name (not id!)

    • orgId string required

      organization id

    • name string required

      organization role name

    Request Body required
    • id string
    • name string
    • description string
    Responses

    success

    Loading...
    - + \ No newline at end of file diff --git a/api/update-organization/index.html b/api/update-organization/index.html index cc397d983..b6f26d53b 100644 --- a/api/update-organization/index.html +++ b/api/update-organization/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content

    Update this organization by id

    Update this organization by id

    Path Parameters
    • realm string required

      realm name (not id!)

    • orgId string required

      organization id

    Request Body required
    • id string
    • name string
    • displayName string
    • url string
    • realm string
    • domains string[]
    • attributes object
    • property name* object
    • Array [
    • string
    • ]
    Responses

    success

    Loading...
    - + \ No newline at end of file diff --git a/api/update-realm-attribute-by-key/index.html b/api/update-realm-attribute-by-key/index.html index 39f2d3e6c..dc5457c87 100644 --- a/api/update-realm-attribute-by-key/index.html +++ b/api/update-realm-attribute-by-key/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content

    Update realm attribute by key

    Update realm attribute by key

    Path Parameters
    • realm string required

      realm name (not id!)

    • attributeKey string required

      attribute key

    Request Body required
    • name string
    • value string
    • realm string
    Responses

    success

    Loading...
    - + \ No newline at end of file diff --git a/api/update-webhook/index.html b/api/update-webhook/index.html index 6dd39cbcd..069b5859c 100644 --- a/api/update-webhook/index.html +++ b/api/update-webhook/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content

    Update this webhook by id

    Update this webhook by id

    Path Parameters
    • realm string required

      realm name (not id!)

    • webhookId string required

      webhook id

    Request Body required
    • attributes object
    • id string
    • enabled boolean
    • url string
    • secret string
    • createdBy string
    • createdAt string
    • realm string
    • eventTypes string[]
    Responses

    success

    Loading...
    - + \ No newline at end of file diff --git a/api/verify-domain/index.html b/api/verify-domain/index.html index c683eec31..f5f0be69c 100644 --- a/api/verify-domain/index.html +++ b/api/verify-domain/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Skip to main content

    Start domain verification

    Initiate a verification check for the domain name owned by this organization

    Path Parameters
    • realm string required

      realm name (not id!)

    • orgId string required

      organization id

    • domainName string required

      domain name

    Responses

    success

    Loading...
    - + \ No newline at end of file diff --git a/assets/css/styles.4ecce774.css b/assets/css/styles.72c9e936.css similarity index 66% rename from assets/css/styles.4ecce774.css rename to assets/css/styles.72c9e936.css index 494f47bb4..525d242c9 100644 --- a/assets/css/styles.4ecce774.css +++ b/assets/css/styles.72c9e936.css @@ -1 +1 @@ -.col,.container{padding:0 var(--ifm-spacing-horizontal);width:100%}.markdown>h2,.markdown>h3,.markdown>h4,.markdown>h5,.markdown>h6{margin-bottom:calc(var(--ifm-heading-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown li,body{word-wrap:break-word}body,ol ol,ol ul,ul ol,ul ul{margin:0}pre,table{overflow:auto}blockquote,pre{margin:0 0 var(--ifm-spacing-vertical)}.breadcrumbs__link,.button{transition-timing-function:var(--ifm-transition-timing-default)}.button,code{vertical-align:middle}.button--outline.button--active,.button--outline:active,.button--outline:hover,:root{--ifm-button-color:var(--ifm-font-color-base-inverse)}.menu__link:hover,a{transition:color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.navbar--dark,:root{--ifm-navbar-link-hover-color:var(--ifm-color-primary)}:root,html[data-theme=dark]{--ifm-color-emphasis-500:var(--ifm-color-gray-500)}.toggleButton_gllP,html{-webkit-tap-highlight-color:transparent}.tableTheme_A_3f,table{border-collapse:collapse}*,.DocSearch-Container,.DocSearch-Container *,.carousel *{box-sizing:border-box}:root{--ifm-color-scheme:light;--ifm-dark-value:10%;--ifm-darker-value:15%;--ifm-darkest-value:30%;--ifm-light-value:15%;--ifm-lighter-value:30%;--ifm-lightest-value:50%;--ifm-contrast-background-value:90%;--ifm-contrast-foreground-value:70%;--ifm-contrast-background-dark-value:70%;--ifm-contrast-foreground-dark-value:90%;--ifm-color-primary:#3578e5;--ifm-color-secondary:#ebedf0;--ifm-color-success:#00a400;--ifm-color-info:#54c7ec;--ifm-color-warning:#ffba00;--ifm-color-danger:#fa383e;--ifm-color-primary-dark:#306cce;--ifm-color-primary-darker:#2d66c3;--ifm-color-primary-darkest:#2554a0;--ifm-color-primary-light:#538ce9;--ifm-color-primary-lighter:#72a1ed;--ifm-color-primary-lightest:#9abcf2;--ifm-color-primary-contrast-background:#ebf2fc;--ifm-color-primary-contrast-foreground:#102445;--ifm-color-secondary-dark:#d4d5d8;--ifm-color-secondary-darker:#c8c9cc;--ifm-color-secondary-darkest:#a4a6a8;--ifm-color-secondary-light:#eef0f2;--ifm-color-secondary-lighter:#f1f2f5;--ifm-color-secondary-lightest:#f5f6f8;--ifm-color-secondary-contrast-background:#fdfdfe;--ifm-color-secondary-contrast-foreground:#474748;--ifm-color-success-dark:#009400;--ifm-color-success-darker:#008b00;--ifm-color-success-darkest:#007300;--ifm-color-success-light:#26b226;--ifm-color-success-lighter:#4dbf4d;--ifm-color-success-lightest:#80d280;--ifm-color-success-contrast-background:#e6f6e6;--ifm-color-success-contrast-foreground:#003100;--ifm-color-info-dark:#4cb3d4;--ifm-color-info-darker:#47a9c9;--ifm-color-info-darkest:#3b8ba5;--ifm-color-info-light:#6ecfef;--ifm-color-info-lighter:#87d8f2;--ifm-color-info-lightest:#aae3f6;--ifm-color-info-contrast-background:#eef9fd;--ifm-color-info-contrast-foreground:#193c47;--ifm-color-warning-dark:#e6a700;--ifm-color-warning-darker:#d99e00;--ifm-color-warning-darkest:#b38200;--ifm-color-warning-light:#ffc426;--ifm-color-warning-lighter:#ffcf4d;--ifm-color-warning-lightest:#ffdd80;--ifm-color-warning-contrast-background:#fff8e6;--ifm-color-warning-contrast-foreground:#4d3800;--ifm-color-danger-dark:#e13238;--ifm-color-danger-darker:#d53035;--ifm-color-danger-darkest:#af272b;--ifm-color-danger-light:#fb565b;--ifm-color-danger-lighter:#fb7478;--ifm-color-danger-lightest:#fd9c9f;--ifm-color-danger-contrast-background:#ffebec;--ifm-color-danger-contrast-foreground:#4b1113;--ifm-color-white:#fff;--ifm-color-black:#000;--ifm-color-gray-0:var(--ifm-color-white);--ifm-color-gray-100:#f5f6f7;--ifm-color-gray-200:#ebedf0;--ifm-color-gray-300:#dadde1;--ifm-color-gray-400:#ccd0d5;--ifm-color-gray-500:#bec3c9;--ifm-color-gray-600:#8d949e;--ifm-color-gray-700:#606770;--ifm-color-gray-800:#444950;--ifm-color-gray-900:#1c1e21;--ifm-color-gray-1000:var(--ifm-color-black);--ifm-color-emphasis-0:var(--ifm-color-gray-0);--ifm-color-emphasis-100:var(--ifm-color-gray-100);--ifm-color-emphasis-200:var(--ifm-color-gray-200);--ifm-color-emphasis-300:var(--ifm-color-gray-300);--ifm-color-emphasis-400:var(--ifm-color-gray-400);--ifm-color-emphasis-600:var(--ifm-color-gray-600);--ifm-color-emphasis-700:var(--ifm-color-gray-700);--ifm-color-emphasis-800:var(--ifm-color-gray-800);--ifm-color-emphasis-900:var(--ifm-color-gray-900);--ifm-color-emphasis-1000:var(--ifm-color-gray-1000);--ifm-color-content:var(--ifm-color-emphasis-900);--ifm-color-content-inverse:var(--ifm-color-emphasis-0);--ifm-color-content-secondary:#525860;--ifm-background-color:transparent;--ifm-background-surface-color:var(--ifm-color-content-inverse);--ifm-global-border-width:1px;--ifm-global-radius:0.4rem;--ifm-hover-overlay:rgba(0,0,0,.05);--ifm-font-color-base:var(--ifm-color-content);--ifm-font-color-base-inverse:var(--ifm-color-content-inverse);--ifm-font-color-secondary:var(--ifm-color-content-secondary);--ifm-font-family-base:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--ifm-font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--ifm-font-size-base:100%;--ifm-font-weight-light:300;--ifm-font-weight-normal:400;--ifm-font-weight-semibold:500;--ifm-font-weight-bold:700;--ifm-font-weight-base:var(--ifm-font-weight-normal);--ifm-line-height-base:1.65;--ifm-global-spacing:1rem;--ifm-spacing-vertical:var(--ifm-global-spacing);--ifm-spacing-horizontal:var(--ifm-global-spacing);--ifm-transition-fast:200ms;--ifm-transition-slow:400ms;--ifm-transition-timing-default:cubic-bezier(0.08,0.52,0.52,1);--ifm-global-shadow-lw:0 1px 2px 0 rgba(0,0,0,.1);--ifm-global-shadow-md:0 5px 40px rgba(0,0,0,.2);--ifm-global-shadow-tl:0 12px 28px 0 rgba(0,0,0,.2),0 2px 4px 0 rgba(0,0,0,.1);--ifm-z-index-dropdown:100;--ifm-z-index-fixed:200;--ifm-z-index-overlay:400;--ifm-container-width:1140px;--ifm-container-width-xl:1320px;--ifm-code-background:#f6f7f8;--ifm-code-border-radius:var(--ifm-global-radius);--ifm-code-font-size:90%;--ifm-code-padding-horizontal:0.1rem;--ifm-code-padding-vertical:0.1rem;--ifm-pre-background:var(--ifm-code-background);--ifm-pre-border-radius:var(--ifm-code-border-radius);--ifm-pre-color:inherit;--ifm-pre-line-height:1.45;--ifm-pre-padding:1rem;--ifm-heading-color:inherit;--ifm-heading-margin-top:0;--ifm-heading-margin-bottom:var(--ifm-spacing-vertical);--ifm-heading-font-family:var(--ifm-font-family-base);--ifm-heading-font-weight:var(--ifm-font-weight-bold);--ifm-heading-line-height:1.25;--ifm-h1-font-size:2rem;--ifm-h2-font-size:1.5rem;--ifm-h3-font-size:1.25rem;--ifm-h4-font-size:1rem;--ifm-h5-font-size:0.875rem;--ifm-h6-font-size:0.85rem;--ifm-image-alignment-padding:1.25rem;--ifm-leading-desktop:1.25;--ifm-leading:calc(var(--ifm-leading-desktop)*1rem);--ifm-list-left-padding:2rem;--ifm-list-margin:1rem;--ifm-list-item-margin:0.25rem;--ifm-list-paragraph-margin:1rem;--ifm-table-cell-padding:0.75rem;--ifm-table-background:transparent;--ifm-table-stripe-background:rgba(0,0,0,.03);--ifm-table-border-width:1px;--ifm-table-border-color:var(--ifm-color-emphasis-300);--ifm-table-head-background:inherit;--ifm-table-head-color:inherit;--ifm-table-head-font-weight:var(--ifm-font-weight-bold);--ifm-table-cell-color:inherit;--ifm-link-color:var(--ifm-color-primary);--ifm-link-decoration:none;--ifm-link-hover-color:var(--ifm-link-color);--ifm-link-hover-decoration:underline;--ifm-paragraph-margin-bottom:var(--ifm-leading);--ifm-blockquote-font-size:var(--ifm-font-size-base);--ifm-blockquote-border-left-width:2px;--ifm-blockquote-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-blockquote-padding-vertical:0;--ifm-blockquote-shadow:none;--ifm-blockquote-color:var(--ifm-color-emphasis-800);--ifm-blockquote-border-color:var(--ifm-color-emphasis-300);--ifm-hr-background-color:var(--ifm-color-emphasis-500);--ifm-hr-height:1px;--ifm-hr-margin-vertical:1.5rem;--ifm-scrollbar-size:7px;--ifm-scrollbar-track-background-color:#f1f1f1;--ifm-scrollbar-thumb-background-color:silver;--ifm-scrollbar-thumb-hover-background-color:#a7a7a7;--ifm-alert-background-color:inherit;--ifm-alert-border-color:inherit;--ifm-alert-border-radius:var(--ifm-global-radius);--ifm-alert-border-width:0px;--ifm-alert-border-left-width:5px;--ifm-alert-color:var(--ifm-font-color-base);--ifm-alert-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-alert-padding-vertical:var(--ifm-spacing-vertical);--ifm-alert-shadow:var(--ifm-global-shadow-lw);--ifm-avatar-intro-margin:1rem;--ifm-avatar-intro-alignment:inherit;--ifm-avatar-photo-size:3rem;--ifm-badge-background-color:inherit;--ifm-badge-border-color:inherit;--ifm-badge-border-radius:var(--ifm-global-radius);--ifm-badge-border-width:var(--ifm-global-border-width);--ifm-badge-color:var(--ifm-color-white);--ifm-badge-padding-horizontal:calc(var(--ifm-spacing-horizontal)*0.5);--ifm-badge-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-breadcrumb-border-radius:1.5rem;--ifm-breadcrumb-spacing:0.5rem;--ifm-breadcrumb-color-active:var(--ifm-color-primary);--ifm-breadcrumb-item-background-active:var(--ifm-hover-overlay);--ifm-breadcrumb-padding-horizontal:0.8rem;--ifm-breadcrumb-padding-vertical:0.4rem;--ifm-breadcrumb-size-multiplier:1;--ifm-breadcrumb-separator:url('data:image/svg+xml;utf8,');--ifm-breadcrumb-separator-filter:none;--ifm-breadcrumb-separator-size:0.5rem;--ifm-breadcrumb-separator-size-multiplier:1.25;--ifm-button-background-color:inherit;--ifm-button-border-color:var(--ifm-button-background-color);--ifm-button-border-width:var(--ifm-global-border-width);--ifm-button-font-weight:var(--ifm-font-weight-bold);--ifm-button-padding-horizontal:1.5rem;--ifm-button-padding-vertical:0.375rem;--ifm-button-size-multiplier:1;--ifm-button-transition-duration:var(--ifm-transition-fast);--ifm-button-border-radius:calc(var(--ifm-global-radius)*var(--ifm-button-size-multiplier));--ifm-button-group-spacing:2px;--ifm-card-background-color:var(--ifm-background-surface-color);--ifm-card-border-radius:calc(var(--ifm-global-radius)*2);--ifm-card-horizontal-spacing:var(--ifm-global-spacing);--ifm-card-vertical-spacing:var(--ifm-global-spacing);--ifm-toc-border-color:var(--ifm-color-emphasis-300);--ifm-toc-link-color:var(--ifm-color-content-secondary);--ifm-toc-padding-vertical:0.5rem;--ifm-toc-padding-horizontal:0.5rem;--ifm-dropdown-background-color:var(--ifm-background-surface-color);--ifm-dropdown-font-weight:var(--ifm-font-weight-semibold);--ifm-dropdown-link-color:var(--ifm-font-color-base);--ifm-dropdown-hover-background-color:var(--ifm-hover-overlay);--ifm-footer-background-color:var(--ifm-color-emphasis-100);--ifm-footer-color:inherit;--ifm-footer-link-color:var(--ifm-color-emphasis-700);--ifm-footer-link-hover-color:var(--ifm-color-primary);--ifm-footer-link-horizontal-spacing:0.5rem;--ifm-footer-padding-horizontal:calc(var(--ifm-spacing-horizontal)*2);--ifm-footer-padding-vertical:calc(var(--ifm-spacing-vertical)*2);--ifm-footer-title-color:inherit;--ifm-footer-logo-max-width:min(30rem,90vw);--ifm-hero-background-color:var(--ifm-background-surface-color);--ifm-hero-text-color:var(--ifm-color-emphasis-800);--ifm-menu-color:var(--ifm-color-emphasis-700);--ifm-menu-color-active:var(--ifm-color-primary);--ifm-menu-color-background-active:var(--ifm-hover-overlay);--ifm-menu-color-background-hover:var(--ifm-hover-overlay);--ifm-menu-link-padding-horizontal:0.75rem;--ifm-menu-link-padding-vertical:0.375rem;--ifm-menu-link-sublist-icon:url('data:image/svg+xml;utf8,');--ifm-menu-link-sublist-icon-filter:none;--ifm-navbar-background-color:var(--ifm-background-surface-color);--ifm-navbar-height:3.75rem;--ifm-navbar-item-padding-horizontal:0.75rem;--ifm-navbar-item-padding-vertical:0.25rem;--ifm-navbar-link-color:var(--ifm-font-color-base);--ifm-navbar-link-active-color:var(--ifm-link-color);--ifm-navbar-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-navbar-padding-vertical:calc(var(--ifm-spacing-vertical)*0.5);--ifm-navbar-shadow:var(--ifm-global-shadow-lw);--ifm-navbar-search-input-background-color:var(--ifm-color-emphasis-200);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-800);--ifm-navbar-search-input-placeholder-color:var(--ifm-color-emphasis-500);--ifm-navbar-search-input-icon:url('data:image/svg+xml;utf8,');--ifm-navbar-sidebar-width:83vw;--ifm-pagination-border-radius:var(--ifm-global-radius);--ifm-pagination-color-active:var(--ifm-color-primary);--ifm-pagination-font-size:1rem;--ifm-pagination-item-active-background:var(--ifm-hover-overlay);--ifm-pagination-page-spacing:0.2em;--ifm-pagination-padding-horizontal:calc(var(--ifm-spacing-horizontal)*1);--ifm-pagination-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-pagination-nav-border-radius:var(--ifm-global-radius);--ifm-pagination-nav-color-hover:var(--ifm-color-primary);--ifm-pills-color-active:var(--ifm-color-primary);--ifm-pills-color-background-active:var(--ifm-hover-overlay);--ifm-pills-spacing:0.125rem;--ifm-tabs-color:var(--ifm-font-color-secondary);--ifm-tabs-color-active:var(--ifm-color-primary);--ifm-tabs-color-active-border:var(--ifm-tabs-color-active);--ifm-tabs-padding-horizontal:1rem;--ifm-tabs-padding-vertical:1rem}.badge--danger,.badge--info,.badge--primary,.badge--secondary,.badge--success,.badge--warning{--ifm-badge-border-color:var(--ifm-badge-background-color)}.button--link,.button--outline{--ifm-button-background-color:transparent}html{-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--ifm-background-color);color:var(--ifm-font-color-base);color-scheme:var(--ifm-color-scheme);font:var(--ifm-font-size-base)/var(--ifm-line-height-base) var(--ifm-font-family-base);scroll-behavior:smooth}body{color:var(--main)}iframe{border:0;color-scheme:auto}.container{margin:0 auto;max-width:var(--ifm-container-width)}.container--fluid{max-width:inherit}.row{display:flex;flex-wrap:wrap;margin:0 calc(var(--ifm-spacing-horizontal)*-1)}.list_eTzJ article:last-child,.margin-bottom--none,.margin-vert--none,.markdown>:last-child{margin-bottom:0!important}.margin-top--none,.margin-vert--none,.tabItem_kzG2{margin-top:0!important}.row--no-gutters{margin-left:0;margin-right:0}.margin-horiz--none,.margin-right--none,.tabItem_AM8E:last-child,.tabItem_VIbn:last-child,.tabItem_es3Q:last-child,.tabItem_fIhq:last-child{margin-right:0!important}.row--no-gutters>.col{padding-left:0;padding-right:0}.row--align-top{align-items:flex-start}.row--align-bottom{align-items:flex-end}.menuExternalLink_NmtK,.row--align-center{align-items:center}.row--align-stretch{align-items:stretch}.row--align-baseline{align-items:baseline}.col{--ifm-col-width:100%;flex:1 0;margin-left:0;max-width:var(--ifm-col-width)}.padding-bottom--none,.padding-vert--none{padding-bottom:0!important}.padding-top--none,.padding-vert--none,.pt0{padding-top:0!important}.discriminatorTabsContainer,.padding-horiz--none,.padding-left--none{padding-left:0!important}.padding-horiz--none,.padding-right--none{padding-right:0!important}.col[class*=col--]{flex:0 0 var(--ifm-col-width)}.col--1{--ifm-col-width:8.33333%}.col--offset-1{margin-left:8.33333%}.col--2{--ifm-col-width:16.66667%}.col--offset-2{margin-left:16.66667%}.col--3{--ifm-col-width:25%}.col--offset-3{margin-left:25%}.col--4{--ifm-col-width:33.33333%}.col--offset-4{margin-left:33.33333%}.col--5{--ifm-col-width:41.66667%}.col--offset-5{margin-left:41.66667%}.col--6{--ifm-col-width:50%}.col--offset-6{margin-left:50%}.col--7{--ifm-col-width:58.33333%}.col--offset-7{margin-left:58.33333%}.col--8{--ifm-col-width:66.66667%}.col--offset-8{margin-left:66.66667%}.col--9{--ifm-col-width:75%}.col--offset-9{margin-left:75%}.col--10{--ifm-col-width:83.33333%}.col--offset-10{margin-left:83.33333%}.col--11{--ifm-col-width:91.66667%}.col--offset-11{margin-left:91.66667%}.col--12{--ifm-col-width:100%}.col--offset-12{margin-left:100%}.margin-horiz--none,.margin-left--none{margin-left:0!important}.margin--none{margin:0!important}.margin-bottom--xs,.margin-vert--xs{margin-bottom:.25rem!important}.margin-top--xs,.margin-vert--xs{margin-top:.25rem!important}.margin-horiz--xs,.margin-left--xs{margin-left:.25rem!important}.margin-horiz--xs,.margin-right--xs{margin-right:.25rem!important}.margin--xs{margin:.25rem!important}.margin-bottom--sm,.margin-vert--sm{margin-bottom:.5rem!important}.margin-top--sm,.margin-vert--sm{margin-top:.5rem!important}.margin-horiz--sm,.margin-left--sm{margin-left:.5rem!important}.margin-horiz--sm,.margin-right--sm{margin-right:.5rem!important}.margin--sm{margin:.5rem!important}.margin-bottom--md,.margin-vert--md{margin-bottom:1rem!important}.margin-top--md,.margin-vert--md{margin-top:1rem!important}.margin-horiz--md,.margin-left--md{margin-left:1rem!important}.margin-horiz--md,.margin-right--md{margin-right:1rem!important}.margin--md{margin:1rem!important}.margin-bottom--lg,.margin-vert--lg{margin-bottom:2rem!important}.margin-top--lg,.margin-vert--lg{margin-top:2rem!important}.margin-horiz--lg,.margin-left--lg{margin-left:2rem!important}.margin-horiz--lg,.margin-right--lg{margin-right:2rem!important}.margin--lg{margin:2rem!important}.margin-bottom--xl,.margin-vert--xl{margin-bottom:5rem!important}.margin-top--xl,.margin-vert--xl{margin-top:5rem!important}.margin-horiz--xl,.margin-left--xl{margin-left:5rem!important}.margin-horiz--xl,.margin-right--xl{margin-right:5rem!important}.margin--xl{margin:5rem!important}.padding--none{padding:0!important}.padding-bottom--xs,.padding-vert--xs{padding-bottom:.25rem!important}.padding-top--xs,.padding-vert--xs{padding-top:.25rem!important}.padding-horiz--xs,.padding-left--xs{padding-left:.25rem!important}.padding-horiz--xs,.padding-right--xs{padding-right:.25rem!important}.padding--xs{padding:.25rem!important}.padding-bottom--sm,.padding-vert--sm{padding-bottom:.5rem!important}.padding-top--sm,.padding-vert--sm{padding-top:.5rem!important}.padding-horiz--sm,.padding-left--sm{padding-left:.5rem!important}.padding-horiz--sm,.padding-right--sm{padding-right:.5rem!important}.padding--sm{padding:.5rem!important}.padding-bottom--md,.padding-vert--md{padding-bottom:1rem!important}.padding-top--md,.padding-vert--md{padding-top:1rem!important}.padding-horiz--md,.padding-left--md{padding-left:1rem!important}.padding-horiz--md,.padding-right--md{padding-right:1rem!important}.padding--md{padding:1rem!important}.padding-bottom--lg,.padding-vert--lg{padding-bottom:2rem!important}.padding-top--lg,.padding-vert--lg{padding-top:2rem!important}.padding-horiz--lg,.padding-left--lg{padding-left:2rem!important}.padding-horiz--lg,.padding-right--lg{padding-right:2rem!important}.padding--lg{padding:2rem!important}.padding-bottom--xl,.padding-vert--xl{padding-bottom:5rem!important}.padding-top--xl,.padding-vert--xl{padding-top:5rem!important}.padding-horiz--xl,.padding-left--xl{padding-left:5rem!important}.padding-horiz--xl,.padding-right--xl{padding-right:5rem!important}.padding--xl{padding:5rem!important}code{background-color:var(--ifm-code-background);border:.1rem solid rgba(0,0,0,.1);border-radius:var(--ifm-code-border-radius);font-family:var(--ifm-font-family-monospace);font-size:var(--ifm-code-font-size);padding:var(--ifm-code-padding-vertical) var(--ifm-code-padding-horizontal)}a code{color:inherit}pre{background-color:var(--ifm-pre-background);border-radius:var(--ifm-pre-border-radius);color:var(--ifm-pre-color);font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace);padding:var(--ifm-pre-padding)}pre code{background-color:transparent;border:none;font-size:100%;line-height:inherit;padding:0}kbd{background-color:var(--ifm-color-emphasis-0);border:1px solid var(--ifm-color-emphasis-400);border-radius:.2rem;box-shadow:inset 0 -1px 0 var(--ifm-color-emphasis-400);color:var(--ifm-color-emphasis-800);font:80% var(--ifm-font-family-monospace);padding:.15rem .3rem}h1,h2,h3,h4,h5,h6{color:var(--ifm-heading-color);font-family:var(--ifm-heading-font-family);font-weight:var(--ifm-heading-font-weight);line-height:var(--ifm-heading-line-height);margin:var(--ifm-heading-margin-top) 0 var(--ifm-heading-margin-bottom) 0}h1{font-size:var(--ifm-h1-font-size)}h2{font-size:var(--ifm-h2-font-size)}h3{font-size:var(--ifm-h3-font-size)}h4{font-size:var(--ifm-h4-font-size)}h5{font-size:var(--ifm-h5-font-size)}h6{font-size:var(--ifm-h6-font-size)}img{max-width:100%}img[align=right]{padding-left:var(--image-alignment-padding)}img[align=left]{padding-right:var(--image-alignment-padding)}.markdown{--ifm-h1-vertical-rhythm-top:3;--ifm-h2-vertical-rhythm-top:2;--ifm-h3-vertical-rhythm-top:1.5;--ifm-heading-vertical-rhythm-top:1.25;--ifm-h1-vertical-rhythm-bottom:1.25;--ifm-heading-vertical-rhythm-bottom:1}.markdown:after,.markdown:before{content:"";display:table}.markdown:after{clear:both}.markdown h1:first-child{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-h1-vertical-rhythm-bottom)*var(--ifm-leading));--ifm-h1-font-size:2.5rem}.markdown>h2{--ifm-h2-font-size:2rem;margin-top:calc(var(--ifm-h2-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h3{--ifm-h3-font-size:1.5rem;margin-top:calc(var(--ifm-h3-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h4,.markdown>h5,.markdown>h6{margin-top:calc(var(--ifm-heading-vertical-rhythm-top)*var(--ifm-leading))}.markdown>p,.markdown>pre,.markdown>ul,.tabList_OQG4{margin-bottom:var(--ifm-leading)}.markdown li>p{margin-top:var(--ifm-list-paragraph-margin)}.markdown li+li{margin-top:var(--ifm-list-item-margin)}ol,ul{margin:0 0 var(--ifm-list-margin);padding-left:var(--ifm-list-left-padding)}ol ol,ul ol{list-style-type:lower-roman}ol ol ol,ol ul ol,ul ol ol,ul ul ol{list-style-type:lower-alpha}table{display:block;margin-bottom:var(--ifm-spacing-vertical)}table thead tr{border-bottom:2px solid var(--ifm-table-border-color)}table thead,table tr:nth-child(2n){background-color:var(--ifm-table-stripe-background)}table tr{background-color:var(--ifm-table-background);border-top:var(--ifm-table-border-width) solid var(--ifm-table-border-color)}table td,table th{border:var(--ifm-table-border-width) solid var(--ifm-table-border-color);padding:var(--ifm-table-cell-padding)}table th{background-color:var(--ifm-table-head-background);color:var(--ifm-table-head-color);font-weight:var(--ifm-table-head-font-weight)}table td{color:var(--ifm-table-cell-color)}strong{font-weight:var(--ifm-font-weight-bold)}a{color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration);transition:.2s}a:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration);text-decoration:none}.button:hover,.text--no-decoration,.text--no-decoration:hover,a:not([href]){text-decoration:none}p{margin:0 0 var(--ifm-paragraph-margin-bottom)}blockquote{border-left:var(--ifm-blockquote-border-left-width) solid var(--ifm-blockquote-border-color);box-shadow:var(--ifm-blockquote-shadow);color:var(--ifm-blockquote-color);font-size:var(--ifm-blockquote-font-size);padding:var(--ifm-blockquote-padding-vertical) var(--ifm-blockquote-padding-horizontal)}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{background-color:var(--ifm-hr-background-color);border:0;height:var(--ifm-hr-height);margin:var(--ifm-hr-margin-vertical) 0}.shadow--lw{box-shadow:var(--ifm-global-shadow-lw)!important}.shadow--md{box-shadow:var(--ifm-global-shadow-md)!important}.shadow--tl{box-shadow:var(--ifm-global-shadow-tl)!important}.text--primary,.wordWrapButtonEnabled_EoeP .wordWrapButtonIcon_Bwma{color:var(--ifm-color-primary)}.text--secondary{color:var(--ifm-color-secondary)}.text--success{color:var(--ifm-color-success)}.text--info{color:var(--ifm-color-info)}.text--warning{color:var(--ifm-color-warning)}.text--danger{color:var(--ifm-color-danger)}.text--center{text-align:center}.text--left{text-align:left}.text--justify{text-align:justify}.text--right{text-align:right}.text--capitalize{text-transform:capitalize}.text--lowercase{text-transform:lowercase}.admonitionHeading_tbUL,.alert__heading,.sectPreHeadline_myin,.text--uppercase{text-transform:uppercase}.text--light{font-weight:var(--ifm-font-weight-light)}.text--normal{font-weight:var(--ifm-font-weight-normal)}.menu,.text--semibold{font-weight:var(--ifm-font-weight-semibold)}.text--bold{font-weight:var(--ifm-font-weight-bold)}.text--italic{font-style:italic}.text--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text--break{word-wrap:break-word!important;word-break:break-word!important}.clean-btn{background:none;border:none;color:inherit;cursor:pointer;font-family:inherit;padding:0}.alert,.alert .close{color:var(--ifm-alert-foreground-color)}.clean-list{list-style:none;padding-left:0}.alert--primary{--ifm-alert-background-color:var(--ifm-color-primary-contrast-background);--ifm-alert-background-color-highlight:rgba(53,120,229,.15);--ifm-alert-foreground-color:var(--ifm-color-primary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-primary-dark)}.alert--secondary{--ifm-alert-background-color:var(--ifm-color-secondary-contrast-background);--ifm-alert-background-color-highlight:rgba(235,237,240,.15);--ifm-alert-foreground-color:var(--ifm-color-secondary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-secondary-dark)}.alert--success{--ifm-alert-background-color:var(--ifm-color-success-contrast-background);--ifm-alert-background-color-highlight:rgba(0,164,0,.15);--ifm-alert-foreground-color:var(--ifm-color-success-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-success-dark)}.alert--info{--ifm-alert-background-color:var(--ifm-color-info-contrast-background);--ifm-alert-background-color-highlight:rgba(84,199,236,.15);--ifm-alert-foreground-color:var(--ifm-color-info-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-info-dark)}.alert--warning{--ifm-alert-background-color:var(--ifm-color-warning-contrast-background);--ifm-alert-background-color-highlight:rgba(255,186,0,.15);--ifm-alert-foreground-color:var(--ifm-color-warning-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-warning-dark)}.alert--danger{--ifm-alert-background-color:var(--ifm-color-danger-contrast-background);--ifm-alert-background-color-highlight:rgba(250,56,62,.15);--ifm-alert-foreground-color:var(--ifm-color-danger-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-danger-dark)}.alert{--ifm-code-background:var(--ifm-alert-background-color-highlight);--ifm-link-color:var(--ifm-alert-foreground-color);--ifm-link-hover-color:var(--ifm-alert-foreground-color);--ifm-link-decoration:underline;--ifm-tabs-color:var(--ifm-alert-foreground-color);--ifm-tabs-color-active:var(--ifm-alert-foreground-color);--ifm-tabs-color-active-border:var(--ifm-alert-border-color);background-color:var(--ifm-alert-background-color);border:var(--ifm-alert-border-width) solid var(--ifm-alert-border-color);border-left-width:var(--ifm-alert-border-left-width);border-radius:var(--ifm-alert-border-radius);box-shadow:var(--ifm-alert-shadow);padding:var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal)}.alert__heading{align-items:center;display:flex;font:700 var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.5rem}.alert__icon{display:inline-flex;margin-right:.4em}.alert__icon svg{fill:var(--ifm-alert-foreground-color);stroke:var(--ifm-alert-foreground-color);stroke-width:0}.alert .close{margin:calc(var(--ifm-alert-padding-vertical)*-1) calc(var(--ifm-alert-padding-horizontal)*-1) 0 0;opacity:.75}.alert .close:focus,.alert .close:hover{opacity:1}.alert a{-webkit-text-decoration-color:var(--ifm-alert-border-color);text-decoration-color:var(--ifm-alert-border-color)}.alert a:hover{text-decoration-thickness:2px}.avatar{column-gap:var(--ifm-avatar-intro-margin);display:flex}.avatar__photo{border-radius:50%;display:block;height:var(--ifm-avatar-photo-size);overflow:hidden;width:var(--ifm-avatar-photo-size)}.avatar__photo--sm{--ifm-avatar-photo-size:2rem}.avatar__photo--lg{--ifm-avatar-photo-size:4rem}.avatar__photo--xl{--ifm-avatar-photo-size:6rem}.avatar__intro{display:flex;flex:1 1;flex-direction:column;justify-content:center;text-align:var(--ifm-avatar-intro-alignment)}.badge,.breadcrumbs__item,.breadcrumbs__link,.button,.dropdown>.navbar__link:after{display:inline-block}.avatar__name{font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base)}.avatar__subtitle{margin-top:.25rem}.avatar--vertical{--ifm-avatar-intro-alignment:center;--ifm-avatar-intro-margin:0.5rem;align-items:center;flex-direction:column}.badge{background-color:var(--ifm-badge-background-color);border:var(--ifm-badge-border-width) solid var(--ifm-badge-border-color);border-radius:var(--ifm-badge-border-radius);color:var(--ifm-badge-color);font-size:75%;font-weight:var(--ifm-font-weight-bold);line-height:1;padding:var(--ifm-badge-padding-vertical) var(--ifm-badge-padding-horizontal)}.badge--primary{--ifm-badge-background-color:var(--ifm-color-primary)}.badge--secondary{--ifm-badge-background-color:var(--ifm-color-secondary);color:var(--ifm-color-black)}.breadcrumbs__link,.button.button--secondary.button--outline:not(.button--active):not(:hover){color:var(--ifm-font-color-base)}.badge--success{--ifm-badge-background-color:var(--ifm-color-success)}.badge--info{--ifm-badge-background-color:var(--ifm-color-info)}.badge--warning{--ifm-badge-background-color:var(--ifm-color-warning)}.badge--danger{--ifm-badge-background-color:var(--ifm-color-danger)}.breadcrumbs{margin-bottom:0;padding-left:0}.breadcrumbs__item:not(:last-child):after{background:var(--ifm-breadcrumb-separator) center;content:" ";display:inline-block;filter:var(--ifm-breadcrumb-separator-filter);height:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier));margin:0 var(--ifm-breadcrumb-spacing);opacity:.5;width:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier))}.breadcrumbs__item--active .breadcrumbs__link{background:var(--ifm-breadcrumb-item-background-active);color:var(--ifm-breadcrumb-color-active)}.breadcrumbs__link{border-radius:var(--ifm-breadcrumb-border-radius);font-size:calc(1rem*var(--ifm-breadcrumb-size-multiplier));padding:calc(var(--ifm-breadcrumb-padding-vertical)*var(--ifm-breadcrumb-size-multiplier)) calc(var(--ifm-breadcrumb-padding-horizontal)*var(--ifm-breadcrumb-size-multiplier));transition-duration:var(--ifm-transition-fast);transition-property:background,color}.breadcrumbs__link:link:hover,.breadcrumbs__link:visited:hover,area.breadcrumbs__link[href]:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs__link:-webkit-any-link:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs__link:any-link:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs--sm{--ifm-breadcrumb-size-multiplier:0.8}.breadcrumbs--lg{--ifm-breadcrumb-size-multiplier:1.2}.button{background-color:var(--ifm-button-background-color);border:var(--ifm-button-border-width) solid var(--ifm-button-border-color);border-radius:var(--ifm-button-border-radius);cursor:pointer;font-size:calc(.875rem*var(--ifm-button-size-multiplier));font-weight:var(--ifm-button-font-weight);line-height:1.5;padding:calc(var(--ifm-button-padding-vertical)*var(--ifm-button-size-multiplier)) calc(var(--ifm-button-padding-horizontal)*var(--ifm-button-size-multiplier));text-align:center;transition-duration:var(--ifm-button-transition-duration);transition-property:color,background,border-color;-webkit-user-select:none;user-select:none;white-space:nowrap}.aportal_VoVb img,.carousel .slide img,.carousel .thumb img,.dropdown,.keycloak_K4jr img{vertical-align:top}.button,.button:hover{color:var(--ifm-button-color)}.button--outline{--ifm-button-color:var(--ifm-button-border-color)}.button--outline:hover{--ifm-button-background-color:var(--ifm-button-border-color)}.button--link{--ifm-button-border-color:transparent;color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}.button--link.button--active,.button--link:active,.button--link:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button.disabled,.button:disabled,.button[disabled]{opacity:.65;pointer-events:none}.button--sm{--ifm-button-size-multiplier:0.8}.button--lg{--ifm-button-size-multiplier:1.35}.button--block,.planFoot_Kh7l .btnPlan_oVpz{display:block;width:100%}.button.button--secondary{color:var(--ifm-color-gray-900)}:where(.button--primary){--ifm-button-background-color:var(--ifm-color-primary);--ifm-button-border-color:var(--ifm-color-primary)}:where(.button--primary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-primary-dark);--ifm-button-border-color:var(--ifm-color-primary-dark)}.button--primary.button--active,.button--primary:active{--ifm-button-background-color:var(--ifm-color-primary-darker);--ifm-button-border-color:var(--ifm-color-primary-darker)}:where(.button--secondary){--ifm-button-background-color:var(--ifm-color-secondary);--ifm-button-border-color:var(--ifm-color-secondary)}:where(.button--secondary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-secondary-dark);--ifm-button-border-color:var(--ifm-color-secondary-dark)}.button--secondary.button--active,.button--secondary:active{--ifm-button-background-color:var(--ifm-color-secondary-darker);--ifm-button-border-color:var(--ifm-color-secondary-darker)}:where(.button--success){--ifm-button-background-color:var(--ifm-color-success);--ifm-button-border-color:var(--ifm-color-success)}:where(.button--success):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-success-dark);--ifm-button-border-color:var(--ifm-color-success-dark)}.button--success.button--active,.button--success:active{--ifm-button-background-color:var(--ifm-color-success-darker);--ifm-button-border-color:var(--ifm-color-success-darker)}:where(.button--info){--ifm-button-background-color:var(--ifm-color-info);--ifm-button-border-color:var(--ifm-color-info)}:where(.button--info):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-info-dark);--ifm-button-border-color:var(--ifm-color-info-dark)}.button--info.button--active,.button--info:active{--ifm-button-background-color:var(--ifm-color-info-darker);--ifm-button-border-color:var(--ifm-color-info-darker)}:where(.button--warning){--ifm-button-background-color:var(--ifm-color-warning);--ifm-button-border-color:var(--ifm-color-warning)}:where(.button--warning):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-warning-dark);--ifm-button-border-color:var(--ifm-color-warning-dark)}.button--warning.button--active,.button--warning:active{--ifm-button-background-color:var(--ifm-color-warning-darker);--ifm-button-border-color:var(--ifm-color-warning-darker)}:where(.button--danger){--ifm-button-background-color:var(--ifm-color-danger);--ifm-button-border-color:var(--ifm-color-danger)}:where(.button--danger):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-danger-dark);--ifm-button-border-color:var(--ifm-color-danger-dark)}.button--danger.button--active,.button--danger:active{--ifm-button-background-color:var(--ifm-color-danger-darker);--ifm-button-border-color:var(--ifm-color-danger-darker)}.button-group{display:inline-flex;gap:var(--ifm-button-group-spacing)}.button-group>.button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.button-group>.button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.button-group--block{display:flex;justify-content:stretch}.button-group--block>.button{flex-grow:1}.card{background-color:var(--ifm-card-background-color);border-radius:var(--ifm-card-border-radius);box-shadow:var(--ifm-global-shadow-lw);display:flex;flex-direction:column;overflow:hidden}.card--full-height{height:100%}.card__image{padding-top:var(--ifm-card-vertical-spacing)}.card__image:first-child{padding-top:0}.card__body,.card__footer,.card__header{padding:var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing)}.card__body:not(:last-child),.card__footer:not(:last-child),.card__header:not(:last-child){padding-bottom:0}.card__body>:last-child,.card__footer>:last-child,.card__header>:last-child{margin-bottom:0}.card__footer{margin-top:auto}.table-of-contents{font-size:.8rem;margin-bottom:0;padding:var(--ifm-toc-padding-vertical) 0}.table-of-contents,.table-of-contents ul{list-style:none;padding-left:var(--ifm-toc-padding-horizontal)}.table-of-contents li{margin:var(--ifm-toc-padding-vertical) var(--ifm-toc-padding-horizontal)}.table-of-contents__left-border{border-left:1px solid var(--ifm-toc-border-color)}.table-of-contents__link{color:var(--ifm-toc-link-color);display:block}.table-of-contents__link--active,.table-of-contents__link--active code,.table-of-contents__link:hover,.table-of-contents__link:hover code{color:var(--ifm-color-primary);text-decoration:none}.close{color:var(--ifm-color-black);float:right;font-size:1.5rem;font-weight:var(--ifm-font-weight-bold);line-height:1;opacity:.5;padding:1rem;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.close:hover{opacity:.7}.close:focus,.theme-code-block-highlighted-line .codeLineNumber_Tfdd:before{opacity:.8}.dropdown{display:inline-flex;font-weight:var(--ifm-dropdown-font-weight);position:relative}.dropdown--hoverable:hover .dropdown__menu,.dropdown--show .dropdown__menu{opacity:1;pointer-events:all;transform:translateY(-1px);visibility:visible}#nprogress,.carousel img,.dropdown__menu,.navbar__item.dropdown .navbar__link:not([href]){pointer-events:none}.dropdown--right .dropdown__menu{left:inherit;right:0}.dropdown--nocaret .navbar__link:after{content:none!important}.dropdown__menu{background-color:var(--ifm-dropdown-background-color);border-radius:var(--ifm-global-radius);box-shadow:var(--ifm-global-shadow-md);left:0;list-style:none;max-height:80vh;min-width:10rem;opacity:0;overflow-y:auto;position:absolute;top:calc(100% - var(--ifm-navbar-item-padding-vertical) + .3rem);transform:translateY(-.625rem);transition-duration:var(--ifm-transition-fast);transition-property:opacity,transform,visibility;transition-timing-function:var(--ifm-transition-timing-default);visibility:hidden;z-index:var(--ifm-z-index-dropdown)}.menu__caret,.menu__link,.menu__list-item-collapsible{border-radius:.25rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.dropdown__link{color:var(--ifm-dropdown-link-color);display:block;margin-top:.2rem;white-space:nowrap}.dropdown__link--active,.dropdown__link:hover{background-color:var(--ifm-dropdown-hover-background-color);color:var(--ifm-dropdown-link-color);text-decoration:none}.dropdown__link--active,.dropdown__link--active:hover{--ifm-dropdown-link-color:var(--ifm-link-color)}.dropdown>.navbar__link:after{border-width:.4em .4em 0;content:"";margin-left:.3em;position:relative;top:2px;transform:translateY(-50%)}.footer{background-color:var(--ifm-footer-background-color);color:var(--ifm-footer-color);padding:var(--ifm-footer-padding-vertical) var(--ifm-footer-padding-horizontal)}.footer--dark{--ifm-footer-background-color:#303846;--ifm-footer-color:var(--ifm-footer-link-color);--ifm-footer-link-color:var(--ifm-color-secondary);--ifm-footer-title-color:var(--ifm-color-white);--ifm-footer-background-color:#2b3137}.footer__link-item{color:var(--ifm-footer-link-color);line-height:2}.footer__link-item:hover{color:var(--ifm-footer-link-hover-color)}.footer__link-separator{margin:0 var(--ifm-footer-link-horizontal-spacing)}.footer__logo{margin-top:1rem;max-width:var(--ifm-footer-logo-max-width)}.footer__title{color:var(--ifm-footer-title-color);font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base);margin-bottom:var(--ifm-heading-margin-bottom)}.apiItemContainer article>:first-child,.apiItemContainer header+*,.docItemContainer article>:first-child,.docItemContainer header+*,.docItemContainer_Djhp article>:first-child,.docItemContainer_Djhp header+*,.docItemContainer_oq1c article>:first-child,.docItemContainer_oq1c header+*,.footer__item,.formItem_WgRa:first-child,.showMoreButton_ZGo2:first-child,.showMoreButton_jDKX:first-child{margin-top:0}.admonitionContent_S0QG>:last-child,.cardContainer_fWXF :last-child,.collapsibleContent_i85q>:last-child,.featCard_EPO6 p,.footer__items,.tabItem_Ymn6>:last-child,.theme-api-markdown details p{margin-bottom:0}.codeBlockStandalone_MEMb,[type=checkbox]{padding:0}.hero{align-items:center;background-color:var(--ifm-hero-background-color);color:var(--ifm-hero-text-color);display:flex;padding:4rem 2rem}.hero--primary{--ifm-hero-background-color:var(--ifm-color-primary);--ifm-hero-text-color:var(--ifm-font-color-base-inverse)}.hero--dark{--ifm-hero-background-color:#303846;--ifm-hero-text-color:var(--ifm-color-white)}.hero__title,.title_f1Hy{font-size:3rem}.hero__subtitle{font-size:1.5rem}.menu__list{list-style:none;margin:0;padding-left:0}.menu__caret,.menu__link{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu__list .menu__list{flex:0 0 100%;margin-top:.25rem;padding-left:var(--ifm-menu-link-padding-horizontal)}.menu__list-item:not(:first-child){margin-top:.25rem}.menu__list-item--collapsed .menu__list{height:0;overflow:hidden}.details_lb9f[data-collapsed=false].isBrowser_bmU9>summary:before,.details_lb9f[open]:not(.isBrowser_bmU9)>summary:before,.menu__list-item--collapsed .menu__caret:before,.menu__list-item--collapsed .menu__link--sublist:after,.tabArrowRight_X4Xu,.tabArrowRight_oN04,.tabArrowRight_qsDv,.tabArrowRight_x8Ix{transform:rotate(90deg)}.menu__list-item-collapsible{display:flex;flex-wrap:wrap;position:relative}.menu__caret:hover,.menu__link:hover,.menu__list-item-collapsible--active,.menu__list-item-collapsible:hover{background:var(--ifm-menu-color-background-hover)}.menu__list-item-collapsible .menu__link--active,.menu__list-item-collapsible .menu__link:hover{background:none!important}.menu__caret,.menu__link{align-items:center;display:flex}.navbar-sidebar,.navbar-sidebar__backdrop{opacity:0;top:0;transition-timing-function:ease-in-out;left:0;bottom:0;visibility:hidden}.menu__link{color:var(--ifm-menu-color);flex:1;line-height:1.25}.menu__link:hover{color:var(--ifm-menu-color);text-decoration:none}.menu__caret:before,.menu__link--sublist-caret:after{height:1.25rem;transition:transform var(--ifm-transition-fast) linear;width:1.25rem;transform:rotate(180deg);content:"";filter:var(--ifm-menu-link-sublist-icon-filter)}.menu__link--sublist-caret:after{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;margin-left:auto;min-width:1.25rem}.menu__link--active,.menu__link--active:hover{color:var(--ifm-menu-color-active)}.navbar__brand,.navbar__link{color:var(--ifm-navbar-link-color)}.menu__link--active:not(.menu__link--sublist),.paramsItem:focus,.paramsItem:hover,.paramsItem_PKlE:focus,.paramsItem_PKlE:hover,.schemaItem:focus,.schemaItem:hover,.schemaItem_P8yX:focus,.schemaItem_P8yX:hover{background-color:var(--ifm-menu-color-background-active)}.menu__caret:before{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem}.navbar--dark,html[data-theme=dark]{--ifm-menu-link-sublist-icon-filter:invert(100%) sepia(94%) saturate(17%) hue-rotate(223deg) brightness(104%) contrast(98%)}.navbar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-navbar-shadow);height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}.docsWrapper_BCFX,.navbar,.navbar>.container,.navbar>.container-fluid{display:flex}.navbar--fixed-top{position:-webkit-sticky;position:sticky;top:0;z-index:var(--ifm-z-index-fixed)}.bgImg,.page-home{z-index:-1}.navbar__inner{display:flex;flex-wrap:wrap;justify-content:space-between;width:100%}.navbar__brand{align-items:center;display:flex;margin-right:1rem;min-width:0}.navbar__brand:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.announcementBarContent_xLdY,.navbar__title{flex:1 1 auto}.navbar__toggle{display:none;margin-right:.5rem}.navbar__logo{flex:0 0 auto;height:2rem;margin-right:.5rem}.docs-wrapper,.navbar__logo img,body,html{height:100%}.navbar__items{align-items:center;display:flex;flex:1;min-width:0}.navbar__items--center{flex:0 0 auto}.heroIntegrations_GxsS p,.listFeats_Pnmk p,.navbar__items--center .navbar__brand{margin:0}.navbar__items--center+.navbar__items--right{flex:1}.navbar__items--right{flex:0 0 auto;justify-content:flex-end}.navbar__items--right>:last-child{padding-right:0}.navbar__item{display:inline-block;padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.navbar__link{font-weight:var(--ifm-font-weight-semibold)}.navbar__link--active,.navbar__link:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.navbar--dark,.navbar--primary{--ifm-menu-color:var(--ifm-color-gray-300);--ifm-navbar-link-color:var(--ifm-color-gray-100);--ifm-navbar-search-input-background-color:hsla(0,0%,100%,.1);--ifm-navbar-search-input-placeholder-color:hsla(0,0%,100%,.5);color:var(--ifm-color-white)}.navbar--dark{--ifm-navbar-background-color:#242526;--ifm-menu-color-background-active:hsla(0,0%,100%,.05);--ifm-navbar-search-input-color:var(--ifm-color-white)}.navbar--primary{--ifm-navbar-background-color:var(--ifm-color-primary);--ifm-navbar-link-hover-color:var(--ifm-color-white);--ifm-menu-color-active:var(--ifm-color-white);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-500)}.navbar__search-input{-webkit-appearance:none;appearance:none;background:var(--ifm-navbar-search-input-background-color) var(--ifm-navbar-search-input-icon) no-repeat .75rem center/1rem 1rem;border:none;border-radius:2rem;color:var(--ifm-navbar-search-input-color);cursor:text;display:inline-block;font-size:.9rem;height:2rem;padding:0 .5rem 0 2.25rem;width:12.5rem}.navbar__search-input::placeholder{color:var(--ifm-navbar-search-input-placeholder-color)}.navbar-sidebar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-global-shadow-md);overflow-x:hidden;position:fixed;transform:translate3d(-100%,0,0);transition-duration:.25s;transition-property:opacity,visibility,transform;width:var(--ifm-navbar-sidebar-width)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar__items{transform:translateZ(0)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar--show .navbar-sidebar__backdrop{opacity:1;visibility:visible}.navbar-sidebar__backdrop{background-color:rgba(0,0,0,.6);position:fixed;right:0;transition-duration:.1s;transition-property:opacity,visibility}.navbar-sidebar__brand{align-items:center;box-shadow:var(--ifm-navbar-shadow);display:flex;flex:1;height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar-sidebar__items{display:flex;height:calc(100% - var(--ifm-navbar-height));transition:transform var(--ifm-transition-fast) ease-in-out}.navbar-sidebar__items--show-secondary{transform:translate3d(calc((var(--ifm-navbar-sidebar-width))*-1),0,0)}.navbar-sidebar__item{flex-shrink:0;padding:.5rem;width:calc(var(--ifm-navbar-sidebar-width))}.navbar-sidebar__back{background:var(--ifm-menu-color-background-active);font-size:15px;font-weight:var(--ifm-button-font-weight);margin:0 0 .2rem -.5rem;padding:.6rem 1.5rem;position:relative;text-align:left;top:-.5rem;width:calc(100% + 1rem)}.navbar-sidebar__close{display:flex;margin-left:auto}.pagination{column-gap:var(--ifm-pagination-page-spacing);display:flex;font-size:var(--ifm-pagination-font-size);padding-left:0}.pagination--sm{--ifm-pagination-font-size:0.8rem;--ifm-pagination-padding-horizontal:0.8rem;--ifm-pagination-padding-vertical:0.2rem}.pagination--lg{--ifm-pagination-font-size:1.2rem;--ifm-pagination-padding-horizontal:1.2rem;--ifm-pagination-padding-vertical:0.3rem}.pagination__item{display:inline-flex}.pagination__item>span{padding:var(--ifm-pagination-padding-vertical)}.pagination__item--active .pagination__link{color:var(--ifm-pagination-color-active)}.pagination__item--active .pagination__link,.pagination__item:not(.pagination__item--active):hover .pagination__link{background:var(--ifm-pagination-item-active-background)}.pagination__item--disabled,.pagination__item[disabled]{opacity:.25;pointer-events:none}.pagination__link{border-radius:var(--ifm-pagination-border-radius);color:var(--ifm-font-color-base);display:inline-block;padding:var(--ifm-pagination-padding-vertical) var(--ifm-pagination-padding-horizontal);transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination__link:hover,.sidebarItemLink_mo7H:hover{text-decoration:none}.pagination-nav{grid-gap:var(--ifm-spacing-horizontal);display:grid;gap:var(--ifm-spacing-horizontal);grid-template-columns:repeat(2,1fr)}.pagination-nav__link{border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-pagination-nav-border-radius);display:block;height:100%;line-height:var(--ifm-heading-line-height);padding:var(--ifm-global-spacing);transition:border-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination-nav__link:hover{border-color:var(--ifm-pagination-nav-color-hover);text-decoration:none}.pagination-nav__link--next{grid-column:2/3;text-align:right}.aportal_VoVb,.bodyImg_PAPi,.bodyImg_ZN7y,.bodyImg_kD9N,.contentBlockCta,.contentBlockHead,.content_knG7,.featCard_EPO6,.featInner_FP2u,.featureImg_KAsM,.footer,.heroImage_ba0c,.keycloak_K4jr,.pageHero,.pageHeroMsg,.pills--block .pills__item,.planFoot_Kh7l,.planHead_C2Ld,.planSupport_QMOZ,.sectHead_KgE8,.text-center{text-align:center}.pagination-nav__label{font-size:var(--ifm-h4-font-size);font-weight:var(--ifm-heading-font-weight);word-break:break-word}.pagination-nav__link--prev .pagination-nav__label:before{content:"« "}.pagination-nav__link--next .pagination-nav__label:after{content:" »"}.pagination-nav__sublabel{color:var(--ifm-color-content-secondary);font-size:var(--ifm-h5-font-size);font-weight:var(--ifm-font-weight-semibold);margin-bottom:.25rem}.pills__item,.tabs{font-weight:var(--ifm-font-weight-bold)}.pills{display:flex;gap:var(--ifm-pills-spacing);padding-left:0}.pills__item{border-radius:.5rem;cursor:pointer;display:inline-block;padding:.25rem 1rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pills__item--active{color:var(--ifm-pills-color-active)}.pills__item--active,.pills__item:not(.pills__item--active):hover{background:var(--ifm-pills-color-background-active)}.pills--block{justify-content:stretch}.pills--block .pills__item{flex-grow:1}.tabs{color:var(--ifm-tabs-color);display:flex;margin-bottom:0;overflow-x:auto;padding-left:0}.tabs__item{border-bottom:3px solid transparent;border-radius:var(--ifm-global-radius);cursor:pointer;display:inline-flex;padding:var(--ifm-tabs-padding-vertical) var(--ifm-tabs-padding-horizontal);transition:background-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs__item--active{border-bottom-color:var(--ifm-tabs-color-active-border);border-bottom-left-radius:0;border-bottom-right-radius:0;color:var(--ifm-tabs-color-active)}.btnCta,.btnPrimary{background-color:var(--ifm-color-primary);border:none}.tabs__item:hover{background-color:var(--ifm-hover-overlay)}.tabs--block{justify-content:stretch}.tabs--block .tabs__item{flex-grow:1;justify-content:center}html[data-theme=dark]{--ifm-color-scheme:dark;--ifm-color-emphasis-0:var(--ifm-color-gray-1000);--ifm-color-emphasis-100:var(--ifm-color-gray-900);--ifm-color-emphasis-200:var(--ifm-color-gray-800);--ifm-color-emphasis-300:var(--ifm-color-gray-700);--ifm-color-emphasis-400:var(--ifm-color-gray-600);--ifm-color-emphasis-600:var(--ifm-color-gray-400);--ifm-color-emphasis-700:var(--ifm-color-gray-300);--ifm-color-emphasis-800:var(--ifm-color-gray-200);--ifm-color-emphasis-900:var(--ifm-color-gray-100);--ifm-color-emphasis-1000:var(--ifm-color-gray-0);--ifm-background-color:#1b1b1d;--ifm-background-surface-color:#242526;--ifm-hover-overlay:hsla(0,0%,100%,.05);--ifm-color-content:#e3e3e3;--ifm-color-content-secondary:#fff;--ifm-breadcrumb-separator-filter:invert(64%) sepia(11%) saturate(0%) hue-rotate(149deg) brightness(99%) contrast(95%);--ifm-code-background:hsla(0,0%,100%,.1);--ifm-scrollbar-track-background-color:#444;--ifm-scrollbar-thumb-background-color:#686868;--ifm-scrollbar-thumb-hover-background-color:#7a7a7a;--ifm-table-stripe-background:hsla(0,0%,100%,.07);--ifm-toc-border-color:var(--ifm-color-emphasis-200);--ifm-color-primary-contrast-background:#102445;--ifm-color-primary-contrast-foreground:#ebf2fc;--ifm-color-secondary-contrast-background:#474748;--ifm-color-secondary-contrast-foreground:#fdfdfe;--ifm-color-success-contrast-background:#003100;--ifm-color-success-contrast-foreground:#e6f6e6;--ifm-color-info-contrast-background:#193c47;--ifm-color-info-contrast-foreground:#eef9fd;--ifm-color-warning-contrast-background:#4d3800;--ifm-color-warning-contrast-foreground:#fff8e6;--ifm-color-danger-contrast-background:#4b1113;--ifm-color-danger-contrast-foreground:#ffebec;--docsearch-text-color:#f5f6f7;--docsearch-container-background:rgba(9,10,17,.8);--docsearch-modal-background:#15172a;--docsearch-modal-shadow:inset 1px 1px 0 0 #2c2e40,0 3px 8px 0 #000309;--docsearch-searchbox-background:#090a11;--docsearch-searchbox-focus-background:#000;--docsearch-hit-color:#bec3c9;--docsearch-hit-shadow:none;--docsearch-hit-background:#090a11;--docsearch-key-gradient:linear-gradient(-26.5deg,#565872,#31355b);--docsearch-key-shadow:inset 0 -2px 0 0 #282d55,inset 0 0 1px 1px #51577d,0 2px 2px 0 rgba(3,4,9,.3);--docsearch-footer-background:#1e2136;--docsearch-footer-shadow:inset 0 1px 0 0 rgba(73,76,106,.5),0 -4px 8px 0 rgba(0,0,0,.2);--docsearch-logo-color:#fff;--docsearch-muted-color:#7f8497}:root{--docusaurus-progress-bar-color:var(--ifm-color-primary);--ifm-color-primary:#3fa1e3;--ifm-color-primary-dark:#2695df;--ifm-color-primary-darker:#1f8dd7;--ifm-color-primary-darkest:#1a74b1;--ifm-color-primary-light:#58ade7;--ifm-color-primary-lighter:#65b4e9;--ifm-color-primary-lightest:#ebf5fc;--ifm-navbar-height:80px;--ifm-code-font-size:95%;--ifm-heading-color:#161b1d;--ifm-navbar-background-color:hsla(0,0%,100%,.9);--ifm-navbar-link-color:#3c474e;--main:#2c353a;--success:#18cc6d;--form-outline:#e1e6ea;--form-outline-focus:#bcc5cb;--inner-width:1400px;--inner-width-narrow:1040px;--ifm-container-width:1400px;--openapi-code-dim-light:#aaaaa1!important;--color-wh:#fff;--color-bg:#f4f4fc;--color-main:#0d0f19;--color-n1:#c2cad0;--color-n5:#3c474e;--color-planbadge:#f36529;--soft-shadow-branded:0px 2px 12px rgba(248,204,241,.55);--soft-shadow-branded-strong:0px 5px 12px rgba(248,204,241,.85);--breakpoint-mid:800px;--breakpoint-lar:1200px}.btnCta,.btnPrimary{color:var(--color-wh)}#nprogress .bar{background:var(--docusaurus-progress-bar-color);height:2px;left:0;position:fixed;top:0;width:100%;z-index:1031}#nprogress .peg,.bgImg,.page-home{position:absolute}#nprogress .peg{box-shadow:0 0 10px var(--docusaurus-progress-bar-color),0 0 5px var(--docusaurus-progress-bar-color);height:100%;opacity:1;right:0;transform:rotate(3deg) translateY(-4px);width:100px}.page-bg{background-position:top;background-repeat:no-repeat;background-size:100% auto}.page-home{height:auto;left:0;top:0;width:100vw}.page-identity{background-image:url(/assets/images/gradient-short-bg-525ffc124d4fcefe4f556cfcf2d5dc55.png)}.btnCta{border-radius:8px;box-shadow:0 16px 30px rgba(0,0,0,.2);display:inline-block;font-size:16px;font-weight:550;padding:18px 24px;transition:.2s}.btnCta:hover,.btnPrimary:hover{background-color:var(--ifm-color-primary-light);color:var(--color-wh);cursor:pointer}.btnSecondary:hover,.navbar__items--right [buttontype=btnSecondary]:hover{box-shadow:inset 0 0 0 2px var(--ifm-color-primary);color:var(--ifm-color-primary-light);cursor:pointer}.btnPrimary{border-radius:8px;display:inline-block;font-size:13px;font-weight:700;padding:12px 16px;transition:.2s}.btnSecondary,.navbar__items--right [buttontype=btnSecondary]{border:none;background-color:var(--color-wh);display:inline-block;font-size:13px;font-weight:700;padding:12px 16px;transition:.2s}.btnSecondary{border-radius:8px;box-shadow:inset 0 0 0 1px var(--ifm-color-primary);color:var(--ifm-color-primary)}.btnSecondary:hover{background-color:var(--color-wh)}.btnReadMore{color:var(--ifm-color-primary);column-gap:12px;display:inline-flex;line-height:1.2em;margin:0}.btnReadMore img{transition:.25s ease-out}.btnReadMore:hover img{transform:translateX(4px)}.codeBox{background-color:var(--color-main);border-radius:16px;color:var(--color-wh);min-height:390px;padding:20px}.bgImg img,.pageHeroBgCircles img{height:auto;width:100%}.entSocialLoginBgCircles,.entSocialLoginBgImg,.keycloakBgCircles,.socialLoginBgCircles,.socialLoginBgImg{left:50%;max-width:1120px;top:-280px;transform:translateX(-50%);width:100%}.socialLoginBgCircles{top:-100px}.socialLoginBgImg{max-width:1164px;top:-220px}.entSocialLoginBgCircles{top:-310px;width:280vw;z-index:0}.entSocialLoginBgImg{top:92px;width:170vw}.enterpriseSsoBgImg{width:120vw}.plansBgImg{left:-100vw;top:-100px;width:400vw}.addToAppBgImg,.integrationsBgImg,.kubernetesBgImg,.mfaBgImg{left:-70vw;top:-320px;width:240vw}.mfaBgImg{top:-320px}.addToAppBgImg{top:-840px}.kubernetesBgImg{top:-560px}.ssoBgImb{left:0;top:-80px;width:400vw}.docusaurus-highlight-code-line{background-color:#484d5b;display:block;margin:0 calc(var(--ifm-pre-padding)*-1);padding:0 var(--ifm-pre-padding)}.footer,.pageHero,.theme-api-markdown details{background-color:transparent}.navbar .navbar__inner{margin:0 auto;position:relative;width:var(--inner-width)}.navbar .navbar__logo{margin-right:0}.navbar__items--right{column-gap:8px;display:none}.navbar__items--right [buttontype=btnSecondary]{border-radius:8px;box-shadow:inset 0 0 0 1px var(--ifm-color-primary);color:var(--ifm-color-primary)}.navbar__items--right [buttontype=btnSecondary]:hover{background-color:var(--color-wh)}.navbar__items--right [buttontype=btnPrimary]{background-color:var(--ifm-color-primary);border:none;border-radius:8px;color:var(--color-wh);display:inline-block;font-size:13px;font-weight:700;padding:12px 16px;transition:.2s}.navbar__items--right [buttontype=btnPrimary]:hover{background-color:var(--ifm-color-primary-light);box-shadow:none;color:var(--color-wh);cursor:pointer}.dropdown__menu{border-radius:16px;padding:8px}.dropdown__link{border-radius:8px;font-size:1rem;line-height:1.2em;padding:12px 16px}.dropdown__link:hover{background-color:var(--color-bg)}.dropdown>.navbar__link:after{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNiIgaGVpZ2h0PSI0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0uNS43NSAzIDMuMjUgNS41Ljc1IiBzdHJva2U9IiMxRjJDMzQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg==") 50% no-repeat;border:none;height:8px;width:10px}.pageHero{padding:40px 20px 20px;position:relative;z-index:3}.pageHeroMsg{margin:0 auto;max-width:782px}.pageHeroMsg h1{font-size:2.6rem;line-height:1.2em;margin-bottom:16px;margin-top:0}.pageHeroMsgIntro{font-size:1.125rem;line-height:1.4em}.pageHeroImg{margin-bottom:12px;width:100%}.pageHeroBgCircles{left:50%;position:absolute;top:-360px;transform:translateX(-50%);width:1120px;z-index:-1}.contentBlock{margin:80px auto;position:relative}.contentBlockHead{margin:0 auto;max-width:560px;padding:0 20px 48px}.contentBlockHead h2{font-size:1.75rem;line-height:1.2em;margin:0}.contentBlockHead p{font-size:1.125rem;line-height:1.4em;margin:0}.contentBlockHead h2+p{margin-top:24px}.contentBlockCta{padding-top:48px}.contentBlockBody{margin:0 auto;max-width:1120px;padding:0 20px}.readMore{display:flex;flex-direction:column;row-gap:24px}.readMore,.readMore:hover{color:var(--color-n5)}.readMore h3{font-size:1.375rem;line-height:1.2em;margin-bottom:24px}.devsL_xtfI,.devsR_oMCi,.readMoreL,.readMoreR,.responseSamplesTabItem_f_p_{width:100%}.readMoreImg{border-radius:16px}.footer{color:rgba(60,71,78,.5);font-size:.8rem}.footer .footer__title,[data-theme=dark] .planInner_G5WZ h5,[data-theme=dark] .planInner_G5WZ p{color:var(--main)}.container_mt6G,.footer .footer__link-item,.sidebarItemList_Yudw{font-size:.9rem}.footer__links{margin-bottom:100px}.footer__bottom{margin-bottom:30px}[data-theme=dark] .footer,[data-theme=dark] .footer .footer__title,[data-theme=dark] .footer .text--center{color:#999}.markdown img{box-shadow:0 0 2.5px;height:auto}.menu{font-size:95%;font-weight:var(--ifm-font-weight-normal);overflow-x:hidden}a.menu__link.menu__link--sublist{font-weight:var(--ifm-font-weight-bold);text-align:center}.menu__link--sublist:after{background:var(--ifm-menu-link-sublist-icon) 50%/1.2rem 1.2rem;margin-left:8px;transform:rotate(0)}.menu__list-item--collapsed .menu__link--sublist:after{transform:rotate(180deg)}.menu__list .menu__list{padding-bottom:24px;padding-left:0}:root{--openapi-required:var(--ifm-color-danger);--openapi-deprecated:var(--ifm-color-warning);--openapi-nullable:var(--ifm-color-info);--openapi-code-blue:var(--ifm-color-info);--openapi-code-red:var(--ifm-color-danger);--openapi-code-orange:var(--ifm-color-warning);--openapi-code-green:var(--ifm-color-success);--openapi-card-background-color:var(--ifm-color-gray-100);--openapi-card-border-radius:var(--ifm-pre-border-radius);--openapi-input-border:var(--ifm-color-primary);--openapi-input-background:var(--openapi-card-background-color);--bash-background-color:transparent;--bash-border-radius:none;--code-tab-logo-width:26px;--code-tab-logo-height:26px;--docusaurus-announcement-bar-height:auto;--docusaurus-collapse-button-bg:transparent;--docusaurus-collapse-button-bg-hover:rgba(0,0,0,.1);--doc-sidebar-width:300px;--doc-sidebar-hidden-width:30px;--docsearch-primary-color:#5468ff;--docsearch-text-color:#1c1e21;--docsearch-spacing:12px;--docsearch-icon-stroke-width:1.4;--docsearch-highlight-color:var(--docsearch-primary-color);--docsearch-muted-color:#969faf;--docsearch-container-background:rgba(101,108,133,.8);--docsearch-logo-color:#5468ff;--docsearch-modal-width:560px;--docsearch-modal-height:600px;--docsearch-modal-background:#f5f6f7;--docsearch-modal-shadow:inset 1px 1px 0 0 hsla(0,0%,100%,.5),0 3px 8px 0 #555a64;--docsearch-searchbox-height:56px;--docsearch-searchbox-background:#ebedf0;--docsearch-searchbox-focus-background:#fff;--docsearch-searchbox-shadow:inset 0 0 0 2px var(--docsearch-primary-color);--docsearch-hit-height:56px;--docsearch-hit-color:#444950;--docsearch-hit-active-color:#fff;--docsearch-hit-background:#fff;--docsearch-hit-shadow:0 1px 3px 0 #d4d9e1;--docsearch-key-gradient:linear-gradient(-225deg,#d5dbe4,#f8f8f8);--docsearch-key-shadow:inset 0 -2px 0 0 #cdcde6,inset 0 0 1px 1px #fff,0 1px 2px 1px rgba(30,35,90,.4);--docsearch-footer-height:44px;--docsearch-footer-background:#fff;--docsearch-footer-shadow:0 -1px 0 0 #e0e3e8,0 -3px 6px 0 rgba(69,98,155,.12);--docsearch-primary-color:var(--ifm-color-primary);--docsearch-text-color:var(--ifm-font-color-base);--docusaurus-tag-list-border:var(--ifm-color-emphasis-300)}[data-theme=dark]{--bash-background-color:#d3d3d3;--bash-border-radius:20px;--openapi-card-background-color:var(--ifm-color-gray-900)!important}.theme-api-markdown div[class^=collapsibleContent]{margin-top:0!important;padding-left:2px}.theme-api-markdown details{--docusaurus-details-decoration-color:var(--ifm-font-color-base);border:unset!important;box-shadow:unset!important;color:var(--ifm-font-color-base);margin:unset;max-width:600px;padding:unset}.theme-api-markdown details ul{font-size:14px;padding-left:0}.theme-api-markdown details li{list-style:none;padding-top:5px}.theme-api-markdown .tabs__item{padding-bottom:unset;padding-top:unset}.theme-api-markdown details>div>div{border-top:unset!important;padding-top:unset!important}.theme-api-markdown .details__demo-panel{background:var(--openapi-card-background-color);border-radius:var(--openapi-card-border-radius);margin-bottom:1rem}.theme-api-markdown .details__demo-panel>summary{cursor:pointer;padding-left:1rem;padding-top:1rem}.codeBlockTitle_Ktv7+.codeBlockContent_biex .codeBlock_bY9V,.theme-api-markdown .details__demo-panel>div>div>pre{border-top-left-radius:0;border-top-right-radius:0}.theme-api-markdown .details__demo-panel>summary::marker{content:"";display:none}.theme-api-markdown .details__demo-panel>summary::-webkit-details-marker{content:"";display:none}.theme-api-markdown .details__demo-panel>pre{margin-bottom:0;padding-top:0}.theme-api-markdown .details__request-summary>button,.theme-api-markdown .details__response-summary>button{margin-bottom:1rem;margin-right:1rem}.mimeTabsTopSection_wt53,.theme-api-markdown .details__request-summary,.theme-api-markdown .details__response-summary{align-items:center;display:flex;justify-content:space-between}.version-button div{display:block}.version-button div>button>span:after{border-color:currentcolor transparent;border-style:solid;border-width:.4em .4em 0;content:"";display:inline-block;font-size:.8rem;margin-left:.3em;position:relative;top:1px;transform:translateY(-50%)}[class^=paramsItem]:before,[class^=schemaItem]:before{border-bottom:thin solid var(--ifm-color-gray-500);content:"";display:inline-block;height:.5rem;left:0;position:absolute;top:10px;vertical-align:top;width:.7rem}.code__tab--bash:after,.code__tab--csharp:after,.code__tab--go:after,.code__tab--java:after,.code__tab--javascript:after,.code__tab--nodejs:after,.code__tab--php:after,.code__tab--powershell:after,.code__tab--python:after,.code__tab--ruby:after{height:var(--code-tab-logo-height);margin-block:auto;width:var(--code-tab-logo-width);content:""}.schemaItem{padding:5px 0 5px 1rem}.discriminatorItem,.schemaItem{border-left:thin solid var(--ifm-color-gray-500)!important;list-style:none;margin:0!important;position:relative}.discriminatorItem{padding:5px 0!important}.code__tab--go,.code__tab--python{padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--python:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/python/python-original.svg)}.code__tab--python{color:var(--ifm-color-success)}.code__tab--python.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-success)}.language-bash,.language-csharp,.language-go,.language-java,.language-javascript,.language-nodejs,.language-php,.language-powershell,.language-python,.language-ruby,.openapi-demo__code-block code{max-height:500px;overflow:auto}.code__tab--go:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/go/go-original-wordmark.svg)}.code__tab--go{color:var(--ifm-color-info)}.code__tab--go.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-info)}.code__tab--javascript:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/javascript/javascript-original.svg)}.code__tab--javascript{color:var(--ifm-color-warning);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--bash,.code__tab--ruby{color:var(--ifm-color-danger);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--javascript.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-warning)}.code__tab--bash:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/bash/bash-plain.svg);background-color:var(--bash-background-color);border-radius:var(--bash-border-radius)}.code__tab--bash.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-danger)}.code__tab--ruby:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/ruby/ruby-plain.svg)}.code__tab--ruby.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-danger)}.code__tab--csharp:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/csharp/csharp-original.svg)}.code__tab--csharp{color:var(--ifm-color-gray-500);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--csharp.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-gray-500)}.code__tab--nodejs:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/nodejs/nodejs-original.svg)}.code__tab--nodejs{color:var(--ifm-color-success);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--nodejs.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-success)}.code__tab--php:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/php/php-original.svg)}.code__tab--php{color:var(--ifm-color-gray-500);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--php.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-gray-500)}.code__tab--java:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/java/java-original.svg)}.code__tab--java{color:var(--ifm-color-warning);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--java.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-warning)}.code__tab--powershell:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/windows8/windows8-original.svg)}.code__tab--powershell{color:var(--ifm-color-info);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--powershell.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-info)}.prism-code.language-java{white-space:pre!important}.openapi__logo{width:250px}div:has(>ul.openapi-tabs__security-schemes){max-width:100%}.carousel-root,body:not(.navigation-with-keyboard) :not(input):focus{outline:0}#docusaurus-base-url-issue-banner-container,.themedImage_ToTc,[data-theme=dark] .lightToggleIcon_pyhR,[data-theme=light] .darkToggleIcon_wfgR,html[data-announcement-bar-initially-dismissed=true] .announcementBar_mb4j{display:none}.skipToContent_fXgn{background-color:var(--ifm-background-surface-color);color:var(--ifm-color-emphasis-900);left:100%;padding:calc(var(--ifm-global-spacing)/2) var(--ifm-global-spacing);position:fixed;top:1rem;z-index:calc(var(--ifm-z-index-fixed) + 1)}.skipToContent_fXgn:focus{box-shadow:var(--ifm-global-shadow-md);left:1rem}.closeButton_CVFx{line-height:0;padding:0}.content_knG7{font-size:85%;padding:5px 0}.content_knG7 a{color:inherit;text-decoration:underline}.DocSearch-Container a,.btnVideo_gcnS:hover{text-decoration:none}.announcementBar_mb4j{align-items:center;background-color:var(--ifm-color-white);border-bottom:1px solid var(--ifm-color-emphasis-100);color:var(--ifm-color-black);display:flex;height:var(--docusaurus-announcement-bar-height)}.announcementBarPlaceholder_vyr4{flex:0 0 10px}.announcementBarClose_gvF7{align-self:stretch;flex:0 0 30px}.toggle_vylO{height:2rem;width:2rem}.toggleButton_gllP{align-items:center;border-radius:50%;display:flex;height:100%;justify-content:center;transition:background var(--ifm-transition-fast);width:100%}.toggleButton_gllP:hover{background:var(--ifm-color-emphasis-200)}.toggleButtonDisabled_aARS{cursor:not-allowed}[data-theme=dark] .themedImage--dark_i4oU,[data-theme=light] .themedImage--light_HNdA{display:initial}.iconExternalLink_nPIU{margin-left:.3rem}.iconLanguage_nlXk{margin-right:5px;vertical-align:text-bottom}.navbarHideable_m1mJ{transition:transform var(--ifm-transition-fast) ease}.navbarHidden_jGov{transform:translate3d(0,calc(-100% - 2px),0)}.footerLogoLink_BH7S{opacity:.5;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.carousel .control-arrow:focus,.carousel .control-arrow:hover,.carousel .control-dots .dot.selected,.carousel .control-dots .dot:hover,.carousel:hover .slide .legend,.footerLogoLink_BH7S:hover,.hash-link:focus,.tabItem_fIhq:hover,:hover>.hash-link{opacity:1}.mainWrapper_z2l0{flex:1 0 auto}.docusaurus-mt-lg{margin-top:3rem}#__docusaurus{overflow:hidden;display:flex;flex-direction:column;min-height:100%}.sidebar_re4s{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:-webkit-sticky;position:sticky;top:calc(var(--ifm-navbar-height) + 2rem)}.sidebarItemTitle_pO2u{font-size:var(--ifm-h3-font-size);font-weight:var(--ifm-font-weight-bold)}.tabItem_AM8E,.tabItem_fIhq{font-weight:var(--ifm-font-weight-normal)}.sidebarItem__DBe{margin-top:.7rem}.sidebarItemLink_mo7H{color:var(--ifm-font-color-base);display:block}.sidebarItemLinkActive_I1ZP{color:var(--ifm-color-primary)!important}.tabItem_fIhq{align-items:center;border:1px solid var(--ifm-color-primary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-primary);display:flex;height:2.5rem;justify-content:center;margin-right:.5rem;margin-top:0!important}.tabItem_VIbn:not(.discriminatorTabActive_wwM1),.tabItem_es3Q:not(.schemaTabActive_dWHR),.tabItem_fIhq:not(.active_3R0z){opacity:.65}.responseTabsTopSection_jEoq{align-items:center;display:flex;justify-content:space-between;margin-top:2rem}.responseTabsContainer_XEhZ{align-items:center;display:flex;max-width:390px;overflow:hidden;padding-left:1rem}.discriminatorTabsListContainer_Nnai,.responseTabsListContainer_itgM{overflow-x:scroll;overflow-y:hidden;padding:0 .25rem;scroll-behavior:smooth}.responseTabsListContainer_itgM::-webkit-scrollbar{display:none}.mimeTabDot_NO24,.responseTabDot_mwcE{border-radius:50%;height:12.5px;margin-right:5px;width:12.5px}.mimeTabDotSuccess_M1bZ,.responseStatusSuccess_FIkE>.responseTabDot_mwcE{background-color:var(--ifm-color-success)}.mimeTabDotDanger_NBeV,.responseStatusDanger_hE0A>.responseTabDot_mwcE{background-color:var(--ifm-color-danger)}.mimeTabDotInfo_R3vj,.responseStatusInfo_aVn2>.responseTabDot_mwcE{background-color:var(--ifm-color-info)}.active_3R0z,.schemaTabActive_dWHR,.tabItem_AM8E:active,.tabItem_VIbn.discriminatorTabActive_wwM1{background-color:var(--ifm-color-emphasis-100)}.mimeSchemaContainer_yYbZ,.responseSchemaContainer_Sp_v{max-width:600px}.tabArrow_LyoO,.tabArrow_x5p_{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;border:none;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;min-width:1.25rem;width:1.25rem}.tabArrow_LyoO:hover,.tabArrow_WDKX:hover,.tabArrow_x5p_:hover,.tabArrow_zmvw:hover{cursor:pointer}.tabArrowLeft_Cztf,.tabArrowLeft_RQtH,.tabArrowLeft_RzDG,.tabArrowLeft_woLb{transform:rotate(270deg)}.tabItem_AM8E{align-items:center;display:flex;font-size:12px;height:2rem;justify-content:center;margin-right:.5rem;margin-top:0!important;white-space:nowrap}.mimeTabsContainer_gZbZ{align-items:center;display:flex;max-width:390px;overflow:hidden}.mimeTabsListContainer_LPoX,.schemaTabsListContainer_wmy4{overflow-x:scroll;overflow-y:hidden;scroll-behavior:smooth}.mimeTabsListContainer_LPoX::-webkit-scrollbar{display:none}.mimeTabActive_nq7U{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-tabs-color-active-border);border-bottom-left-radius:0;border-bottom-right-radius:0;color:var(--ifm-tabs-color-active)}.cardContainer_fWXF{--ifm-link-color:var(--ifm-color-emphasis-800);--ifm-link-hover-color:var(--ifm-color-emphasis-700);--ifm-link-hover-decoration:none;border:1px solid var(--ifm-color-emphasis-200);box-shadow:0 1.5px 3px 0 rgba(0,0,0,.15);transition:all var(--ifm-transition-fast) ease;transition-property:border,box-shadow}.cardContainer_fWXF:hover{border-color:var(--ifm-color-primary);box-shadow:0 3px 6px 0 rgba(0,0,0,.2)}.cardTitle_rnsV{font-size:1.2rem}.cardDescription_PWke{font-size:.8rem}.searchQueryInput_u2C7,.searchVersionInput_m0Ui{background:var(--docsearch-searchbox-focus-background);border:2px solid var(--ifm-toc-border-color);border-radius:var(--ifm-global-radius);color:var(--docsearch-text-color);font:var(--ifm-font-size-base) var(--ifm-font-family-base);margin-bottom:.5rem;padding:.8rem;transition:border var(--ifm-transition-fast) ease;width:100%}.searchQueryInput_u2C7:focus,.searchVersionInput_m0Ui:focus{border-color:var(--docsearch-primary-color);outline:0}.searchQueryInput_u2C7::placeholder{color:var(--docsearch-muted-color)}.searchResultsColumn_JPFH{font-size:.9rem;font-weight:700}.algoliaLogo_rT1R{max-width:150px}.algoliaLogoPathFill_WdUC{fill:var(--ifm-font-color-base)}.searchResultItem_Tv2o{border-bottom:1px solid var(--ifm-toc-border-color);padding:1rem 0}.searchResultItemHeading_KbCB{font-weight:400;margin-bottom:0}.searchResultItemPath_lhe1{--ifm-breadcrumb-separator-size-multiplier:1;color:var(--ifm-color-content-secondary);font-size:.8rem}.searchResultItemSummary_AEaO{font-style:italic;margin:.5rem 0 0}.loadingSpinner_XVxU{animation:1s linear infinite a;border:.4em solid #eee;border-radius:50%;border-top:.4em solid var(--ifm-color-primary);height:3rem;margin:0 auto;width:3rem}@keyframes a{to{transform:rotate(1turn)}}.loader_vvXV,.responseSamplesContainer_ITGy{margin-top:2rem}.search-result-match{background:rgba(255,215,142,.25);color:var(--docsearch-hit-color);padding:.09em 0}.backToTopButton_sjWU{background-color:var(--ifm-color-emphasis-200);border-radius:50%;bottom:1.3rem;box-shadow:var(--ifm-global-shadow-lw);height:3rem;opacity:0;position:fixed;right:1.3rem;transform:scale(0);transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default);visibility:hidden;width:3rem;z-index:calc(var(--ifm-z-index-fixed) - 1)}.backToTopButton_sjWU:after{background-color:var(--ifm-color-emphasis-1000);content:" ";display:inline-block;height:100%;-webkit-mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;width:100%}.backToTopButtonShow_xfvO{opacity:1;transform:scale(1);visibility:visible}[data-theme=dark]:root{--docusaurus-collapse-button-bg:hsla(0,0%,100%,.05);--docusaurus-collapse-button-bg-hover:hsla(0,0%,100%,.1)}.collapseSidebarButton_PEFL,.docSidebarContainer_b6E3,.heroSectionPlus_e1gH,.sidebarLogo_isFc{display:none}.docMainContainer_gTbr,.docPage__5DB{display:flex;width:100%}.formControl_yBmG{border:1px solid var(--form-outline);border-radius:3px;box-shadow:0 2px 6px var(--form-outline);flex:1 1 auto;font-size:14px;padding:12px 15px;transition:.2s}.heroIntegration_GaJq,.heroSection_dXSz{background-color:var(--color-wh);border-radius:16px}.formControl_yBmG::placeholder{color:var(--form-outline-focus)}.formControl_yBmG:focus{border-color:var(--form-outline-focus);box-shadow:0 1px 3px var(--form-outline);outline:0}.heroImage_ba0c img{height:auto;max-width:950px;width:100%}.heroSections_eYcn{display:flex;flex-direction:column;justify-content:center;margin-top:80px;row-gap:4px}.heroFeats_bnZi,.heroIntegrations_GxsS{margin-top:64px}.heroSection_dXSz{align-items:center;color:var(--color-main);column-gap:16px;display:flex;flex:1;padding:8px 16px;position:relative;row-gap:4px;transition:.25s ease-out}.heroSectionPicto_NANO{height:auto;transition:.25s ease-out;width:32px}.heroSection_dXSz:hover{box-shadow:var(--soft-shadow-branded-strong);color:var(--color-main)}.heroSection_dXSz:hover .heroSectionPicto_NANO{transform:scale(1.1)}.heroSection_dXSz p{font-size:1.1rem;font-weight:700;line-height:1.1em;margin:0}.heroFeats_bnZi{column-gap:20px;display:flex;justify-content:center}.heroFeat_fBWQ{flex:1;max-width:160px}.heroFeat_fBWQ img{margin-bottom:12px}.heroFeat_fBWQ p{line-height:1.3em}.heroIntegrationsLines_bELR{height:auto;margin:8px 0;width:80%}.heroIntegrationRow_vSEr{grid-gap:8px;column-gap:8px;display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(4,1fr)}.heroIntegrationRow_vSEr+.heroIntegrationRow_vSEr{margin-top:8px}.heroIntegration_GaJq{align-items:center;box-shadow:0 2px 6px rgba(248,204,241,.55);display:inline-flex;flex:1;height:64px;justify-content:center;max-width:150px}.featCard_EPO6,.plan_scje{background-color:var(--color-wh);border-radius:16px;box-shadow:var(--soft-shadow-branded)}.heroIntegration_GaJq img,.integration_JE4l img{height:auto;width:80px}.heroIntegrationMore_ABZQ{grid-column:1/4;max-width:inherit}.listFeats_Pnmk{list-style:none;margin:0;padding:0}.listFeats_Pnmk li{padding-left:64px;position:relative}.listFeats_Pnmk li+li{margin-top:32px}.listFeats_Pnmk h5{font-size:1rem;margin-bottom:8px}.listFeatsPicto_LatF{left:0;position:absolute}.featCards_qurD{display:flex;flex-direction:column;row-gap:12px}.featCard_EPO6{flex:1;padding:24px 32px}.blogPostFooterDetailsFull_mRVl,.carousel .slider-wrapper.axis-vertical .slider,.enterpriseSSO_fLpf,.plans_lWeT{flex-direction:column}.featCard_EPO6 h5{font-size:1rem;margin:16px 0}.devs_TdUg,.enterpriseSSO_fLpf{align-items:center;display:flex;row-gap:24px}.devs_TdUg{flex-direction:column-reverse}.aportal_VoVb img{max-width:1120px;width:100%}.keycloak_K4jr{margin-bottom:64px}.heart_Zeus{margin:0 8px}.plans_lWeT{display:flex;row-gap:32px}.plansSects_kiqc{column-gap:160px;display:flex;margin-bottom:24px;padding:0 80px}.plansBlocks_GHdj{display:flex;flex-direction:column;margin:0 auto;max-width:100%;row-gap:32px}.plan_scje{display:flex;flex:1;flex-direction:column;position:relative}.planHead_C2Ld{padding:32px 20px}.planHead_C2Ld h3{font-size:1.4rem;line-height:1.2em;margin:8px 0}.planHead_C2Ld p{line-height:1.1em;margin:0}.planBadge_qnN9{background-color:var(--color-planbadge);border-radius:30px;color:var(--color-wh);display:inline-flex;left:50%;line-height:1em;padding:3px 8px;position:absolute;top:-10px;transform:translateX(-50%);white-space:nowrap}.planFrom_mnEa{color:var(--color-n1);font-size:.86em}.planPrice_u1BA{font-size:1.2em}.planBody_fSqo{flex:1 1 auto;padding:0 20px 20px}.planFoot_Kh7l{column-gap:4px;display:flex;flex-direction:row;padding:0 20px 20px}.planFootA_VTyg{margin:0 auto}.planSupport_QMOZ{margin:auto;max-width:720px}.planSupport_QMOZ .planBody_fSqo{min-height:auto}.planSupport_QMOZ .checklist_YNT1{align-items:center;display:flex;flex-wrap:wrap;justify-content:space-around}.planSupport_QMOZ .checklist_YNT1>li+li{margin-left:1rem;margin-top:0}.buttons_pzbO,.planSupport_QMOZ .planFoot_Kh7l{justify-content:center}.featCardPicto_mQtc{height:50px}.btnVideo_gcnS{align-items:center;display:inline-flex;font-size:14px;transition:.2s}.btnVideo_gcnS img{flex:0 0 auto;margin-right:8px;transition:.25s ease-out}.btnVideo_gcnS:hover{color:var(--main)}.btnVideo_gcnS:hover img{transform:scale(1.15)}.sect_fa0Z{padding:50px 0;position:relative}.sectHead_KgE8{margin:0 auto 40px;max-width:700px}.sectPreHeadline_myin{font-size:12px;letter-spacing:2px;margin-bottom:.6rem}.sectHeadline_XdUn{line-height:1.2em;margin:0 auto 1rem;max-width:300px}.sectHeadline_XdUn img{margin:0 5px;position:relative;top:-4px;vertical-align:middle}.sectHeadline_XdUn .heart_Zeus{height:auto;width:40px}.sectOsArrow_ksPO{display:none;left:55%;position:absolute;top:130px;z-index:-1}.sectOpenSource_IMs3{padding-top:80px}.featblocks_OTQj{margin-left:auto;margin-right:auto;max-width:var(--inner-width-narrow)}.feature_nI3B{margin-bottom:50px}.featureCopy_WeZz>div{margin:0 auto;max-width:280px}.featureCopy_WeZz h3{margin-bottom:25px}.featureCopy_WeZz p{font-size:1rem;line-height:1.5em}.featureImg_KAsM img{max-width:280px}.feat_uwf3{margin:40px 0}.feat_uwf3 img{height:auto;width:200px}.feat_uwf3 h3{font-size:1.5rem;margin:20px 0}.feat_uwf3 p{font-size:1rem;line-height:1.7em}.featInner_FP2u{margin:0 auto;max-width:400px}.badgeSuccess_e6Hh{background-color:rgba(24,204,109,.15);border-radius:3px;color:var(--success);display:inline-block;font-size:14px;line-height:1em;margin-bottom:8px;padding:5px 8px}.tableTheme_A_3f tbody,.tableTheme_A_3f td,.tableTheme_A_3f thead,.tableTheme_A_3f thead th,.tableTheme_A_3f tr{border:none;background:none}.checklist_YNT1{font-size:.875rem;list-style:none;padding-left:0;text-align:left}.checklist_YNT1>li{padding-left:26px;position:relative}.checklist_YNT1>li+li{margin-top:.3rem}.checklistIcon_bRVA{height:auto;left:0;margin-top:8px;position:absolute;width:10px}.access_So_Z{padding:24px;text-align:center}.featureImage_yA8i{margin:0 auto;max-height:128px;max-width:60%}.announcement_FsS0{font-size:24px;font-weight:700;margin:0 auto;padding:48px;text-align:center}.announcementDark_tzC4{background-color:#20232a;color:var(--color-wh)}.announcementInner_RsrQ{margin:0 auto;max-width:768px}.tableThemeWrapper_WRn5{align-items:center;display:flex;justify-content:center}.tableTheme_A_3f{background:none;margin:0 auto 1rem}.tableTheme_A_3f th{font-size:1.2rem;min-width:150px}.tableTheme_A_3f tr td:first-child{font-style:italic;text-align:left}.tableTheme_A_3f td{padding:.5rem 2rem;text-align:center}.notPartOfPlan_xlGG{opacity:.5}.heroBanner_UJJx{overflow:hidden;padding:4rem 0;position:relative;text-align:center}.DocSearch-Button-Container,.buttons_pzbO,.features_keug{align-items:center;display:flex}.features_keug{padding:2rem 0;width:100%}.featureImage_yA8i{height:200px;width:200px}[data-theme=dark] .hero_syme{background-color:transparent;border-bottom:1px solid hsla(0,0%,100%,.1)}[data-theme=dark] .pricing_KaGM{background-color:hsla(0,0%,100%,.1)}[data-theme=dark] h1,[data-theme=dark] h2,[data-theme=dark] h3,[data-theme=dark] h4,[data-theme=dark] h5,[data-theme=dark] p{color:#eee}.authorCol_Hf19{flex-grow:1!important;max-width:inherit!important}.imageOnlyAuthorRow_pa_O{display:flex;flex-flow:row wrap}.imageOnlyAuthorCol_G86a{margin-left:.3rem;margin-right:.3rem}.integrationRow_KYiy{column-gap:8px;display:flex;justify-content:space-between;margin:-100px auto 0 -15%;width:130%}.integrationRow_KYiy+.integrationRow_KYiy{margin:12px auto 0;max-width:96%}.integration_JE4l{align-items:center;background-color:var(--color-wh);border-radius:16px;display:inline-flex;flex:1;height:64px;justify-content:center;max-width:150px}.integrationMore_bWBP{margin-bottom:140px;margin-top:16px;text-align:center}.carousel .control-arrow,.carousel.carousel-slider .control-arrow{background:none;border:0;cursor:pointer;font-size:32px;opacity:.4;position:absolute;top:20px;transition:.25s ease-in;z-index:2}.DocSearch-Button-Key,.bodyImg_d8QI,.carousel,.carousel .carousel,.carousel .slide,.carousel .slider,.carousel .thumbs,.floatingButton_oJlZ{position:relative}.carousel .control-arrow:before,.carousel.carousel-slider .control-arrow:before{border-bottom:8px solid transparent;border-top:8px solid transparent;content:"";display:inline-block;margin:0 5px}.carousel .control-disabled.control-arrow{cursor:inherit;display:none;opacity:0}.carousel .control-prev.control-arrow{left:0}.carousel .control-prev.control-arrow:before{border-right:8px solid #fff}.carousel .control-next.control-arrow{right:0}.carousel .control-next.control-arrow:before{border-left:8px solid #fff}.carousel{width:100%}.carousel img{display:inline-block;width:100%}.carousel .control-arrow{background:none;border:0;font-size:18px;margin-top:-13px;outline:0;top:50%}.carousel .thumbs-wrapper{margin:20px;overflow:hidden}.carousel .thumbs{list-style:none;transform:translateZ(0);transition:.15s ease-in;white-space:nowrap}.carousel .thumb{border:3px solid #fff;display:inline-block;margin-right:6px;overflow:hidden;padding:2px;transition:border .15s ease-in;white-space:nowrap}.carousel .thumb:focus{border:3px solid #ccc;outline:0}.carousel .thumb.selected,.carousel .thumb:hover{border:3px solid #333}.carousel.carousel-slider{margin:0;overflow:hidden;position:relative}.carousel.carousel-slider .control-arrow{bottom:0;color:#fff;font-size:26px;margin-top:0;padding:5px;top:0}.carousel.carousel-slider .control-arrow:hover{background:rgba(0,0,0,.2)}.carousel .slider-wrapper{margin:auto;overflow:hidden;transition:height .15s ease-in;width:100%}.carousel .slider-wrapper.axis-horizontal .slider,.carousel .slider-wrapper.axis-vertical{-ms-box-orient:horizontal;display:-moz-flex;display:flex}.carousel .slider-wrapper.axis-horizontal .slider .slide{flex-direction:column;flex-flow:column}.carousel .slider{list-style:none;margin:0;padding:0;width:100%}.carousel .slider.animated{transition:.35s ease-in-out}.carousel .slide{margin:0;min-width:100%;text-align:center}.carousel .slide img{border:0;width:100%}.carousel .slide iframe{border:0;display:inline-block;margin:0 40px 40px;width:calc(100% - 80px)}.carousel .slide .legend{background:#000;border-radius:10px;bottom:40px;color:#fff;font-size:12px;left:50%;margin-left:-45%;opacity:.25;padding:10px;position:absolute;text-align:center;transition:opacity .35s ease-in-out;width:90%}.carousel .control-dots{bottom:0;margin:10px 0;padding:0;position:absolute;text-align:center;width:100%;z-index:1}.carousel .control-dots .dot{background:#fff;border-radius:50%;box-shadow:1px 1px 2px rgba(0,0,0,.9);cursor:pointer;display:inline-block;height:8px;margin:0 8px;opacity:.3;transition:opacity .25s ease-in;width:8px}.carousel .carousel-status{color:#fff;font-size:10px;padding:5px;position:absolute;right:0;text-shadow:1px 1px 1px rgba(0,0,0,.9);top:0}.bodyImg_d8QI{text-align:center;z-index:2}.heroImg_hSI6{margin-top:20px;padding:0 20px;text-align:center}.heroImg_hSI6 img{height:auto;max-width:900px;width:100%}.codeBox_eWav{margin:0 auto;max-width:560px}.DocSearch-Button{align-items:center;background:var(--docsearch-searchbox-background);border:0;border-radius:40px;color:var(--docsearch-muted-color);cursor:pointer;display:flex;font-weight:500;height:36px;justify-content:space-between;padding:0 8px;-webkit-user-select:none;user-select:none}.DocSearch-Button:active,.DocSearch-Button:focus,.DocSearch-Button:hover{background:var(--docsearch-searchbox-focus-background);box-shadow:var(--docsearch-searchbox-shadow);color:var(--docsearch-text-color);outline:0}.DocSearch-Search-Icon{stroke-width:1.6}.DocSearch-Hit-Tree,.DocSearch-Hit-action,.DocSearch-Hit-icon,.DocSearch-Reset{stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Button .DocSearch-Search-Icon{color:var(--docsearch-text-color)}.DocSearch-Button-Placeholder{font-size:1rem;padding:0 12px 0 6px}.DocSearch-Input,.DocSearch-Link{-webkit-appearance:none;font:inherit}.DocSearch-Button-Keys{display:flex;min-width:calc(40px + .8em)}.DocSearch-Button-Key{align-items:center;background:var(--docsearch-key-gradient);border:0;border-radius:3px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);display:flex;height:18px;justify-content:center;margin-right:.4em;padding:0 0 2px;top:-1px;width:20px}.DocSearch--active{overflow:hidden!important}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:200}.DocSearch-Link{appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{appearance:none;background:0 0;border:0;color:var(--docsearch-text-color);flex:1;font-size:1.2em;height:100%;outline:0;padding:0 0 0 8px;width:80%}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Cancel,.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator,.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset{animation:.1s ease-in forwards b;-webkit-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;padding:2px;right:0}.DocSearch-Help,.DocSearch-HitsFooter,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Reset:focus,.buttonDelete_vlNf:focus,.buttonGroup_SdX7 button:focus,.buttonThin_xRd9:focus{outline:0}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:0 0}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help{font-size:.9em;margin:0;-webkit-user-select:none;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}.DocSearch-Hit--deleting{opacity:0;transition:.25s linear}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:.25s linear .25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:-webkit-sticky;position:sticky;top:0;z-index:10}.DocSearch-Hit-action-button,.DocSearch-Prefill{-webkit-appearance:none;background:none;cursor:pointer}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.buttonGroup_SdX7 button.selected_VHjy,.buttonGroup_SdX7 button:hover{background:var(--ifm-menu-color-background-active)}.DocSearch-Hit[aria-selected=true] mark{text-decoration:underline}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{appearance:none;border:0;border-radius:50%;color:inherit;padding:2px}svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon,.tocCollapsibleContent_vkbj a{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:background-color .1s ease-in}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"» "}.DocSearch-Prefill{appearance:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:0;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;-webkit-user-select:none;user-select:none;width:100%;z-index:300}.DocSearch-Commands li,.DocSearch-Commands-Key{align-items:center;display:flex}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{background:var(--docsearch-key-gradient);border:0;border-radius:2px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);height:18px;justify-content:center;margin-right:.4em;padding:0 0 1px;width:20px}.codeBlockContainer_Ckt0,.playgroundContainer_X_Ta,.playgroundContainer_l4rC{box-shadow:var(--ifm-global-shadow-lw)}@keyframes b{0%{opacity:0}to{opacity:1}}.DocSearch-Button{margin:0;transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.DocSearch-Container{z-index:calc(var(--ifm-z-index-fixed) + 1)}.iconEdit_Z9Sw{margin-right:.3em;vertical-align:sub}.playgroundContainer_X_Ta{border-radius:var(--ifm-global-radius);margin-bottom:var(--ifm-leading);overflow:hidden}.playgroundHeader_dyrN,.playgroundHeader_yAq7{background:var(--ifm-color-emphasis-200);color:var(--ifm-color-content);font-size:var(--ifm-code-font-size);font-weight:700;letter-spacing:.08rem;padding:.75rem;text-transform:uppercase}.playgroundHeader_dyrN:first-of-type,.playgroundHeader_yAq7:first-of-type{background:var(--ifm-color-emphasis-600);color:var(--ifm-color-content-inverse)}.playgroundEditor_Q6Y7,.playgroundEditor_nWOY{direction:ltr;font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace)!important}.playgroundPreview_DzOI,.playgroundPreview_iGtG{background-color:var(--ifm-pre-background);padding:1rem}.tag_zVej{border:1px solid var(--docusaurus-tag-list-border);transition:border var(--ifm-transition-fast)}.tag_zVej:hover{--docusaurus-tag-list-border:var(--ifm-link-color);text-decoration:none}.tagRegular_sFm0{border-radius:var(--ifm-global-radius);font-size:90%;padding:.2rem .5rem .3rem}.tagWithCount_h2kH{align-items:center;border-left:0;display:flex;padding:0 .5rem 0 1rem;position:relative}.tagWithCount_h2kH:after,.tagWithCount_h2kH:before{border:1px solid var(--docusaurus-tag-list-border);content:"";position:absolute;top:50%;transition:inherit}.tagWithCount_h2kH:before{border-bottom:0;border-right:0;height:1.18rem;right:100%;transform:translate(50%,-50%) rotate(-45deg);width:1.18rem}.tagWithCount_h2kH:after{border-radius:50%;height:.5rem;left:0;transform:translateY(-50%);width:.5rem}.tagWithCount_h2kH span{background:var(--ifm-color-secondary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-black);font-size:.7rem;line-height:1.2;margin-left:.3rem;padding:.1rem .4rem}.buttonGroup__atx button,.codeBlockContainer_Ckt0{background:var(--prism-background-color);color:var(--prism-color)}.tag_Nnez{display:inline-block;margin:.5rem .5rem 0 1rem}.codeBlockContainer_Ckt0{border-radius:var(--ifm-code-border-radius);margin-bottom:var(--ifm-leading)}.tags_jXut{display:inline}.tag_QGVx{display:inline-block;margin:0 .4rem .5rem 0}.codeBlockContent_biex{border-radius:inherit;direction:ltr;position:relative}.codeBlockTitle_Ktv7{border-bottom:1px solid var(--ifm-color-emphasis-300);border-top-left-radius:inherit;border-top-right-radius:inherit;font-size:var(--ifm-code-font-size);font-weight:500;padding:.75rem var(--ifm-pre-padding)}.codeBlock_bY9V{--ifm-pre-background:var(--prism-background-color);margin:0;padding:0}.codeBlockLines_e6Vv{float:left;font:inherit;min-width:100%;padding:var(--ifm-pre-padding)}.codeBlockLinesWithNumbering_o6Pm{display:table;padding:var(--ifm-pre-padding) 0}.buttonGroup__atx{column-gap:.2rem;display:flex;position:absolute;right:calc(var(--ifm-pre-padding)/2);top:calc(var(--ifm-pre-padding)/2)}.buttonGroup__atx button{align-items:center;border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-global-radius);display:flex;line-height:0;opacity:0;padding:.4rem;transition:opacity .2s ease-in-out}.buttonGroup__atx button:focus-visible,.buttonGroup__atx button:hover{opacity:1!important}.theme-code-block:hover .buttonGroup__atx button{opacity:.4}.lastUpdated_vwxv{font-size:smaller;font-style:italic;margin-top:.2rem}:where(:root){--docusaurus-highlighted-code-line-bg:#484d5b}:where([data-theme=dark]){--docusaurus-highlighted-code-line-bg:#646464}.theme-code-block-highlighted-line{background-color:var(--docusaurus-highlighted-code-line-bg);display:block;margin:0 calc(var(--ifm-pre-padding)*-1);padding:0 var(--ifm-pre-padding)}.codeLine_lJS_{counter-increment:a;display:table-row}.codeLineNumber_Tfdd{background:var(--ifm-pre-background);display:table-cell;left:0;overflow-wrap:normal;padding:0 var(--ifm-pre-padding);position:-webkit-sticky;position:sticky;text-align:right;width:1%}.codeLineNumber_Tfdd:before{content:counter(a);opacity:.4}.codeLineContent_feaV{padding-right:var(--ifm-pre-padding)}.theme-code-block:hover .copyButtonCopied_obH4{opacity:1!important}.copyButtonIcons_eSgA{height:1.125rem;position:relative;width:1.125rem}.copyButtonIcon_y97N,.copyButtonSuccessIcon_LjdS{fill:currentColor;height:inherit;left:0;opacity:inherit;position:absolute;top:0;transition:.15s;width:inherit}.copyButtonSuccessIcon_LjdS{color:#00d600;left:50%;opacity:0;top:50%;transform:translate(-50%,-50%) scale(.33)}.copyButtonCopied_obH4 .copyButtonIcon_y97N{opacity:0;transform:scale(.33)}.copyButtonCopied_obH4 .copyButtonSuccessIcon_LjdS{opacity:1;transform:translate(-50%,-50%) scale(1);transition-delay:75ms}.tocCollapsibleButton_TO0P{align-items:center;display:flex;font-size:inherit;justify-content:space-between;padding:.4rem .8rem;width:100%}.tocCollapsibleButton_TO0P:after{background:var(--ifm-menu-link-sublist-icon) 50% 50%/2rem 2rem no-repeat;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast);width:1.25rem}.tocCollapsibleButtonExpanded_MG3E:after,.tocCollapsibleExpanded_sAul{transform:none}.wordWrapButtonIcon_Bwma{height:1.2rem;width:1.2rem}.details_lb9f{--docusaurus-details-summary-arrow-size:0.38rem;--docusaurus-details-transition:transform 200ms ease;--docusaurus-details-decoration-color:grey}.details_lb9f>summary{cursor:pointer;list-style:none;padding-left:1rem;position:relative}.details_lb9f>summary::-webkit-details-marker{display:none}.details_lb9f>summary:before{border-color:transparent transparent transparent var(--docusaurus-details-decoration-color);border-style:solid;border-width:var(--docusaurus-details-summary-arrow-size);content:"";left:0;position:absolute;top:.45rem;transform:rotate(0);transform-origin:calc(var(--docusaurus-details-summary-arrow-size)/2) 50%;transition:var(--docusaurus-details-transition)}.collapsibleContent_i85q{border-top:1px solid var(--docusaurus-details-decoration-color);margin-top:1rem;padding-top:1rem}.details_b_Ee{--docusaurus-details-decoration-color:var(--ifm-alert-border-color);--docusaurus-details-transition:transform var(--ifm-transition-fast) ease;border:1px solid var(--ifm-alert-border-color);margin:0 0 var(--ifm-spacing-vertical)}.anchorWithStickyNavbar_LWe7{scroll-margin-top:calc(var(--ifm-navbar-height) + .5rem)}.anchorWithHideOnScrollNavbar_WYt5{scroll-margin-top:.5rem}.hash-link{opacity:0;padding-left:.5rem;transition:opacity var(--ifm-transition-fast);-webkit-user-select:none;user-select:none}.buttonGroup_SdX7 button,.showMoreButton_ZGo2{cursor:pointer;-webkit-user-select:none;white-space:nowrap}.hash-link:before{content:"#"}.containsTaskList_mC6p{list-style:none}:not(.containsTaskList_mC6p>li)>.containsTaskList_mC6p{padding-left:0}.tocCollapsible_ETCw{background-color:var(--ifm-menu-color-background-active);border-radius:var(--ifm-global-radius);margin:1rem 0}.tocCollapsibleContent_vkbj>ul{border-left:none;border-top:1px solid var(--ifm-color-emphasis-300);font-size:15px;padding:.2rem 0}.tocCollapsibleContent_vkbj ul li{margin:.4rem .8rem}.img_ev3q{height:auto}.breadcrumbsContainer_Z_bl{--ifm-breadcrumb-size-multiplier:0.8;margin-bottom:.8rem}.breadcrumbHomeIcon_OVgt{height:1.1rem;position:relative;top:1px;vertical-align:top;width:1.1rem}.title_kItE{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-leading)*1.25)}.admonition_LlT9{margin-bottom:1em}.admonitionHeading_tbUL{font:var(--ifm-heading-font-weight) var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.3rem}.admonitionHeading_tbUL code{text-transform:none}.admonitionIcon_kALy{display:inline-block;margin-right:.4em;vertical-align:middle}.admonitionIcon_kALy svg{fill:var(--ifm-alert-foreground-color);display:inline-block;height:1.6em;width:1.6em}.tableOfContents_bqdL{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:-webkit-sticky;position:sticky;top:calc(var(--ifm-navbar-height) + 1rem)}.code__tabs_vqEd{display:table-row-group}.buttonGroup_SdX7{background:var(--openapi-card-background-color);border-radius:var(--openapi-card-border-radius) var(--openapi-card-border-radius) 2px 2px;color:var(--ifm-pre-color);display:flex;font-family:var(--ifm-font-family-monospace);justify-content:flex-end;margin-bottom:1px;margin-top:0}.buttonGroup_SdX7 button{--margin:0.25rem;-webkit-appearance:none;appearance:none;background:0 0;border:0 solid transparent;border-radius:calc(var(--margin));color:var(--ifm-menu-color);display:block;font-size:13.3333px;font-weight:var(--ifm-font-weight-semibold);line-height:var(--ifm-pre-line-height);margin:var(--margin);margin-right:0;padding:calc(var(--ifm-button-padding-vertical)*var(--ifm-button-size-multiplier)) calc(var(--ifm-button-padding-horizontal)*var(--ifm-button-size-multiplier));text-align:center;transition:color var(--ifm-button-transition-duration) cubic-bezier(.08,.52,.52,1),background var(--ifm-button-transition-duration) cubic-bezier(.08,.52,.52,1),border-color var(--ifm-button-transition-duration) cubic-bezier(.08,.52,.52,1);user-select:none;width:100%}.buttonGroup_SdX7 button:last-child{margin-right:.25rem}.buttonGroup_SdX7 button.selected_VHjy{color:var(--ifm-menu-color-active)}.formItem_WgRa{margin-top:var(--ifm-pre-padding)}.inputBase_a3Vd,.inputBase_kdtO,.inputBase_p1em,html[data-theme=dark] .selectInput__VTP{border:none;margin-top:calc(var(--ifm-pre-padding)/2);background-color:var(--openapi-input-background);font-size:var(--ifm-code-font-size);width:100%;outline:0}.paramsRequired_UKPH,.required_cVzG{color:var(--openapi-required);font-size:var(--ifm-code-font-size)}.inputBase_kdtO{padding:12px 48px 12px var(--ifm-pre-padding)}.inputBase_kdtO,html[data-theme=dark] .selectInput__VTP{border-radius:4px;color:var(--ifm-pre-color)}html[data-theme=dark] .selectInput__VTP{background-image:url('data:image/svg+xml;charset=US-ASCII,')}.selectInput__VTP,html[data-theme=dark] .selectInput__VTP{-webkit-appearance:none;appearance:none;background-position:right var(--ifm-pre-padding) top 50%;background-repeat:no-repeat;background-size:auto auto}.selectInput__VTP{background-image:url('data:image/svg+xml;charset=US-ASCII,')}.buttonDelete_vlNf:active,.input_CV2d:focus,.input_Ru3N:focus,.selectInput__VTP:focus{box-shadow:inset 0 0 0 2px var(--openapi-input-border)}.inputBase_a3Vd,.inputBase_p1em{border-radius:4px;color:var(--ifm-pre-color);padding:12px var(--ifm-pre-padding)}.floatingButton_oJlZ button{background:var(--ifm-color-emphasis-900);border:none;border-radius:var(--ifm-global-radius);color:var(--ifm-color-emphasis-100);cursor:pointer;opacity:0;padding:.4rem .5rem;position:absolute;right:calc(var(--ifm-pre-padding)/2);transition:opacity .2s ease-in-out,visibility .2s ease-in-out,bottom .2s ease-in-out;visibility:hidden}.floatingButton_oJlZ button:focus-visible,.floatingButton_oJlZ:focus-visible button,.floatingButton_oJlZ:hover button{opacity:1;visibility:visible}.dropzone_Y7D0{border:2px dashed var(--openapi-monaco-border-color)}.dropzoneHover_IkP6,.dropzone_Y7D0{align-items:center;background-color:var(--openapi-input-background);border-radius:4px;cursor:pointer;display:inline-flex;font-size:var(--ifm-code-font-size);justify-content:center;padding:var(--ifm-pre-padding);width:100%}.dropzoneHover_IkP6,.dropzone_Y7D0:hover{background:linear-gradient(var(--openapi-dropzone-hover-shim),var(--openapi-dropzone-hover-shim)),linear-gradient(var(--ifm-color-primary),var(--ifm-color-primary));border:2px dashed var(--ifm-color-primary)}.dropzoneContent_CEnm{align-items:center;color:var(--openapi-dropzone-color);display:flex;flex-wrap:wrap;justify-content:center;margin:var(--ifm-pre-padding) 0}.dropzone_Y7D0:hover .dropzoneContent_CEnm{color:var(--ifm-pre-color)}.dropzoneHover_IkP6 .dropzoneContent_CEnm{align-items:center;color:var(--ifm-pre-color);display:flex;flex-wrap:wrap;justify-content:center;margin:var(--ifm-pre-padding) 0}.discriminatorTabsTopSection_QeEp+hr,.schemaTabsTopSection_sc6Y+hr{display:none}.filename_FaIo{flex:1;margin:0 calc(var(--ifm-pre-padding)*1.5);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.playgroundContainer_l4rC{border-radius:var(--ifm-global-radius);margin-bottom:var(--ifm-leading);margin-top:1rem;max-height:500px;overflow:auto}.tabItem_VIbn,.tabItem_es3Q{align-items:center;border:1px solid var(--ifm-color-primary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-primary);display:flex;font-size:12px;height:1.8rem;justify-content:center;margin-right:.5rem;margin-top:0!important}.tabItem_VIbn:hover,.tabItem_es3Q:hover{background-color:var(--ifm-color-emphasis-100);opacity:1}.schemaTabsTopSection_sc6Y{align-items:center;display:flex;justify-content:space-between;margin-top:1rem}.schemaTabsListContainer_wmy4::-webkit-scrollbar{display:none}.discriminatorTabLabel_dvfv,.schemaTabLabel_clV0{white-space:nowrap}.schemaTabsContainer_HVyG{align-items:center;display:flex;max-width:600px;overflow:hidden}.tabArrow_WDKX,.tabArrow_zmvw{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;border:none;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;min-width:1.25rem;padding:0 .75rem;width:1.25rem}.paramsItem_PKlE,.schemaItem_P8yX{border-left:thin solid var(--ifm-color-gray-500)!important;position:relative}.inputBase_MRMh,.showMoreButton_ZGo2{font-size:var(--ifm-code-font-size);width:100%}.marginVertical_VWja{margin-bottom:unset!important;margin-top:1rem!important}.paramsItem_PKlE{margin:0 0 0 1rem!important;margin-top:unset!important;padding-left:1rem}.schemaName_KIS9,.schemaName_MpGo{opacity:.6}.schemaItem_P8yX{list-style:none;margin:0!important;padding:5px 0 5px 1rem}.required_Wlqr{color:var(--openapi-required)}.deprecated_K3O1,.required_Wlqr{font-size:var(--ifm-code-font-size)}.deprecated_K3O1{color:var(--openapi-deprecated)}.nullable_MFJr{color:var(--openapi-nullable);font-size:var(--ifm-code-font-size)}.strikethrough_nBFj{text-decoration:line-through}.discriminatorTabsTopSection_QeEp{align-items:center;display:flex;justify-content:space-between;margin-left:.9rem;margin-top:1rem}.discriminatorTabsContainer_FMrl{align-items:center;display:flex;overflow:hidden;padding-left:3px;max-width:600px}.discriminatorTabsListContainer_Nnai::-webkit-scrollbar{display:none}.inputBase_MRMh{background-color:var(--openapi-input-background);border:2px solid transparent;border-radius:4px;color:var(--ifm-pre-color);margin-top:calc(var(--ifm-pre-padding)/2);outline:0;padding:12px var(--ifm-pre-padding)}.selectInput_xXUj{-webkit-appearance:none;appearance:none}.selectInput_xXUj option{border-radius:.25rem;color:var(--ifm-menu-color);margin:.25rem 0;padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.selectInput_xXUj:focus{border:2px solid var(--openapi-input-border)}.plus_Swon{display:inline-block;margin-right:6px;transform:rotate(0);transform-origin:center;transition:transform .2s}.plusExpanded_WYvI{transform:rotate(45deg)}.showMoreButton_ZGo2{-webkit-appearance:none;appearance:none;background-color:transparent;border:0 solid transparent;color:var(--ifm-color-primary);display:block;margin-bottom:0;margin-top:var(--ifm-pre-padding);padding:0;text-align:left;user-select:none}.buttonDelete_vlNf,.buttonThin_xRd9{-webkit-appearance:none;font-size:calc(.875rem*var(--ifm-button-size-multiplier));font-weight:400;line-height:1.5;margin-top:calc(var(--ifm-pre-padding)/2);transition-duration:.1s,.1s,.1s,var(--ifm-button-transition-duration);transition-property:color,background,border-color,box-shadow;transition-timing-function:cubic-bezier(.08,.52,.52,1);-webkit-user-select:none}.showMoreButton_ZGo2:hover,.showMoreButton_jDKX:hover{color:var(--ifm-color-primary-hover)}.buttonDelete_vlNf{align-items:center;appearance:none;background-color:var(--openapi-input-background);border:none;border-radius:4px;color:var(--ifm-pre-color);cursor:pointer;display:flex;justify-content:center;margin-left:4px;outline:0;padding:0 12px;user-select:none;white-space:nowrap}.buttonThin_xRd9,.showMoreButton_jDKX{background-color:transparent;cursor:pointer;white-space:nowrap}.buttonThin_xRd9{appearance:none;border:1px solid var(--openapi-input-border);border-radius:var(--ifm-pre-border-radius);color:var(--openapi-input-border);display:block;margin-bottom:var(--ifm-pre-padding);padding:3px 60px 3px 12px;user-select:none}.buttonThin_xRd9:hover{background-color:var(--openapi-input-border);color:var(--openapi-inverse-color)}.buttonThin_xRd9:active{box-shadow:inset 0 0 0 1px var(--openapi-input-border),inset 0 0 0 2px var(--openapi-inverse-color)}.showOptions_Gv8N{margin-top:var(--ifm-pre-padding);visibility:visible}.hideOptions_JLDS{display:none;visibility:hidden}.showMoreButton_jDKX{-webkit-appearance:none;appearance:none;border:0 solid transparent;color:var(--ifm-color-primary);display:block;font-size:var(--ifm-code-font-size);margin-bottom:0;margin-top:var(--ifm-pre-padding);padding:0;text-align:left;-webkit-user-select:none;user-select:none}.optionsPanel_s3ok,.optionsPanel_tfQ6{background:var(--openapi-card-background-color);border-radius:var(--openapi-card-border-radius);color:var(--ifm-pre-color);line-height:var(--ifm-pre-line-height);margin-bottom:var(--ifm-spacing-vertical);margin-top:0;overflow:auto;position:relative}.optionsPanel_s3ok:empty,.optionsPanel_tfQ6:empty{display:none}.optionsPanel_s3ok{padding-top:0!important;padding:var(--ifm-pre-padding)}@media (min-width:600px){.plans_lWeT{column-gap:8px;flex-direction:row;justify-content:center}}@media (min-width:768px){.page-home{height:1300px;left:50%;object-fit:cover;object-position:bottom;transform:translateX(-50%);width:100vw}.ssoBgImb{top:0;width:100vh}.integrationsBgImg{left:0;top:-240px;width:100vw}.enterpriseSsoBgImg{width:50vw}.entSocialLoginBgCircles{top:-336px;width:1120px}.entSocialLoginBgImg{max-width:1164px;top:-264px}.plansBgImg{left:0;top:-50%;width:100vw}.navbar__items--right{display:flex;justify-content:flex-end;left:auto;position:absolute;right:20px;top:16px;width:auto!important}.contentBlock{margin:140px auto}.footer--dark{--ifm-footer-background-color:#2b3137}.footer__links{display:grid;grid-template-columns:repeat(2,1fr)}.footer__col:last-child{grid-column:1/3}.heroIntegrationRow_vSEr{grid-template-columns:repeat(5,1fr);grid-template-rows:repeat(2,1fr);margin:0 auto;max-width:782px}.heroIntegrationMore_ABZQ{grid-column:auto}.heroIntegrationsLines_bELR{max-width:630px;width:100%}.heroIntegration_GaJq{box-shadow:none}.hero_syme{padding-top:60px}.heroProjectTagline_EkV5{font-size:2rem}.heroCta_r0UD .btnPrimary_Yph1{font-size:13px}.sectPreHeadline_myin{margin-bottom:1rem}.sectHeadline_XdUn{font-size:2.2rem;max-width:inherit}.sectHeadline_XdUn img{margin:0 12px}.sectHeadline_XdUn .heart_Zeus{width:60px}.sectOpenSource_IMs3{padding-top:40px}.feature_nI3B{align-items:center;display:flex;margin-bottom:0}.feat_uwf3,.feature_nI3B>*{flex:1}.feature_nI3B:nth-child(2n){flex-direction:row-reverse}.featureCopy_WeZz h3{font-size:1.4rem}.featureCopy_WeZz p{font-size:1.2rem}.featureImg_KAsM img{max-width:550px;width:100%}.featureCopy_WeZz>div{max-width:440px;padding:0 40px}.feats_yvrJ{display:flex;gap:40px;margin:0 auto;max-width:88%}.integrationRow_KYiy{margin-left:0;margin-top:-140px;width:96%}.integrationRow_KYiy+.integrationRow_KYiy{margin-top:48px;max-width:76%}.heroImg_hSI6{margin-top:-80px}}@media only screen and (min-width:768px) and (max-width:996px){.code__tabs_vqEd{justify-content:space-around}}@media (min-width:769px){.hero_syme{padding:48px}.sect_fa0Z{padding:72px 0}}@media (min-width:800px){.heroSections_eYcn{column-gap:8px;flex-direction:row}.heroSectionPicto_NANO{margin-bottom:8px;width:48px}.heroSection_dXSz{flex-direction:column;max-width:150px;padding:20px}.heroSectionPlus_e1gH{align-items:center;background-color:var(--color-wh);border:3px solid #fed9cf;border-radius:50%;display:inline-flex;height:27px;justify-content:center;position:absolute;right:-16px;top:calc(50% - 12px);width:27px;z-index:1}.heroSectionPlus_e1gH img{height:auto;width:14px}.heroFeats_bnZi{column-gap:64px;margin-top:110px}.featCards_qurD{column-gap:12px;flex-direction:row;row-gap:0}.featCard_EPO6{column-gap:32px;flex-direction:row;row-gap:0}.checklist_YNT1{font-size:1rem}}@media (min-width:960px){.carousel .control-dots{bottom:0}}@media (min-width:992px){.readMore{align-items:center;column-gap:72px;flex-direction:row}.readMoreL{width:calc(50% + 72px)}.readMoreR{width:calc(50% - 72px)}.footer,.footer .text--center{text-align:left}.footer__links{display:flex}.footer-logo{float:right;margin-top:-24px}.devs_TdUg,.enterpriseSSO_fLpf{column-gap:80px;flex-direction:row;row-gap:0}.enterpriseSSOL_qbBN{width:64%}.enterpriseSSOR_lqTw{width:36%}.devsL_xtfI,.devsR_oMCi{width:50%}.plansBlocks_GHdj{column-gap:20px;flex-direction:row;row-gap:0}.plansBlock_fHJo{flex:1;height:100%}.plans_lWeT{column-gap:20px}.planHead_C2Ld h3{font-size:1.75rem;margin:16px 0}.planBody_fSqo{min-height:180px}.hero_syme{padding-bottom:60px;padding-top:60px}.heroInner_VWeJ{align-items:center;display:flex;flex-direction:row-reverse;gap:60px}.heroMsg_PB71{flex:1 1 auto}.heroProjectTagline_EkV5{font-size:1.9rem}.heroIntro_TtUU{font-size:1.3rem}.heroImg_wnHi{flex:0 0 auto;min-width:0;width:50%}.heroImg_wnHi img{max-width:800px}.sectOsArrow_ksPO{display:block}}@media (min-width:997px){.collapseSidebarButton_PEFL,.expandButton_m80_{background-color:var(--docusaurus-collapse-button-bg)}.docItemCol,.docItemCol_BDYx,.docItemCol_VOVn,.generatedIndexPage_vN6x{max-width:75%!important}.tocMobile,.tocMobile_ITEo{display:none}:root{--docusaurus-announcement-bar-height:30px}.announcementBarClose_gvF7,.announcementBarPlaceholder_vyr4{flex-basis:50px}.searchBox_ZlJk{padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.collapseSidebarButton_PEFL{border:1px solid var(--ifm-toc-border-color);border-radius:0;bottom:0;display:block!important;height:40px;position:-webkit-sticky;position:sticky}.collapseSidebarButtonIcon_kv0_{margin-top:4px;transform:rotate(180deg)}.expandButtonIcon_BlDH,[dir=rtl] .collapseSidebarButtonIcon_kv0_{transform:rotate(0)}.collapseSidebarButton_PEFL:focus,.collapseSidebarButton_PEFL:hover,.expandButton_m80_:focus,.expandButton_m80_:hover{background-color:var(--docusaurus-collapse-button-bg-hover)}.menuHtmlItem_M9Kj{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu_SIkG{flex-grow:1;padding:.5rem}@supports (scrollbar-gutter:stable){.menu_SIkG{padding:.5rem 0 .5rem .5rem;scrollbar-gutter:stable}}.menuWithAnnouncementBar_GW3s{margin-bottom:var(--docusaurus-announcement-bar-height)}.sidebar_njMd{display:flex;flex-direction:column;height:100%;max-height:100vh;padding-top:var(--ifm-navbar-height);position:-webkit-sticky;position:sticky;top:0;transition:opacity 50ms;width:var(--doc-sidebar-width)}.sidebarWithHideableNavbar_wUlq{padding-top:0}.sidebarHidden_VK0M{height:0;opacity:0;overflow:hidden;visibility:hidden}.sidebarLogo_isFc{align-items:center;color:inherit!important;display:flex!important;margin:0 var(--ifm-navbar-padding-horizontal);max-height:var(--ifm-navbar-height);min-height:var(--ifm-navbar-height);text-decoration:none!important}.sidebarLogo_isFc img{height:2rem;margin-right:.5rem}.expandButton_m80_{align-items:center;display:flex;height:100%;justify-content:center;max-height:100vh;position:-webkit-sticky;position:sticky;top:0;transition:background-color var(--ifm-transition-fast) ease}[dir=rtl] .expandButtonIcon_BlDH{transform:rotate(180deg)}.docSidebarContainer_b6E3{border-right:1px solid var(--ifm-toc-border-color);-webkit-clip-path:inset(0);clip-path:inset(0);display:block;margin-top:calc(var(--ifm-navbar-height)*-1);transition:width var(--ifm-transition-fast) ease;width:var(--doc-sidebar-width);will-change:width}.docSidebarContainerHidden_b3ry{cursor:pointer;width:var(--doc-sidebar-hidden-width)}.docMainContainer_gTbr{flex-grow:1;max-width:calc(100% - var(--doc-sidebar-width))}.docMainContainerEnhanced_Uz_u{max-width:calc(100% - var(--doc-sidebar-hidden-width))}.docItemWrapperEnhanced_czyv{max-width:calc(var(--ifm-container-width) + var(--doc-sidebar-width))!important}.lastUpdated_vwxv{text-align:right}.list_eTzJ article:nth-last-child(-n+2){margin-bottom:0!important}}@media (min-width:1200px){.heroProjectTagline_EkV5,.pageHeroMsg h1{font-size:3.375rem}.pageHero{padding:60px 20px 20px}.pageHeroMsgIntro{margin-left:auto;margin-right:auto;max-width:640px}.pageHeroMsg h1{margin-bottom:24px}.pageHeroImg{margin-bottom:20px;max-width:782px}.heroImg_wnHi{width:55%}}@media (min-width:1440px){.container{max-width:var(--ifm-container-width-xl)}}@media (max-width:996px){.col{--ifm-col-width:100%;flex-basis:var(--ifm-col-width);margin-left:0}.footer{--ifm-footer-padding-horizontal:0}.colorModeToggle_DEke,.footer__link-separator,.navbar__item,.sidebar_re4s,.tableOfContents_bqdL{display:none}.footer__col{margin-bottom:calc(var(--ifm-spacing-vertical)*3)}.footer__link-item{display:block}.hero{padding-left:0;padding-right:0}.navbar>.container,.navbar>.container-fluid{padding:0}.navbar__toggle{display:inherit}.navbar__search-input{width:9rem}.pills--block,.tabs--block{flex-direction:column}.navbar .navbar__items{flex:1 1 auto;justify-content:center;width:100%}.navbar .navbar__toggle{left:0;margin-top:2px;padding:10px;position:absolute}.navbar .navbar__brand{margin-top:3px}.navbar .navbar__logo{width:100px}.navbar .navbar-sidebar{background-color:#fff;display:flex;flex-direction:column;height:100vh;transition:.35s cubic-bezier(.23,1,.32,1)}.navbar .navbar-sidebar__brand{box-shadow:none;flex:0 0 auto;justify-content:center}.navbar .navbar-sidebar__items{display:flex;flex:1 1 auto}.menu{width:100%}.menu .menu__link{font-size:1.15rem;justify-content:center;padding:18px 12px}.navbar .navbar-sidebar__backdrop{background-color:hsla(0,0%,100%,.8);display:block;height:100vh;opacity:0;transition:.3s;visibility:hidden}.navbar.navbar-sidebar--show .navbar-sidebar__backdrop{opacity:1;visibility:visible}.searchBox_ZlJk{position:absolute;right:var(--ifm-navbar-padding-horizontal)}.docItemContainer_F8PC{padding:0 .3rem}}@media only screen and (max-width:996px){.searchQueryColumn_RTkw,.searchResultsColumn_JPFH{max-width:60%!important}.searchLogoColumn_rJIA,.searchVersionColumn_ypXd{max-width:40%!important}.searchLogoColumn_rJIA{padding-left:0!important}}@media (max-width:991px){.heroInner_VWeJ{display:flex;flex-direction:column-reverse}.heroCta_r0UD,.heroIntro_TtUU{margin-left:auto;margin-right:auto}.heroMsg_PB71{text-align:center}.heroCta_r0UD{margin-bottom:30px}}@media screen and (max-width:966px){.heroBanner_UJJx{padding:2rem}}@media (max-width:768px){.DocSearch-Button-Keys,.DocSearch-Button-Placeholder,.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%;max-height:calc(var(--docsearch-vh,1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh,1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh,1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Cancel{-webkit-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:0;overflow:hidden;padding:0;-webkit-user-select:none;user-select:none;white-space:nowrap}}@media (max-width:767px){.featureCopy_WeZz{text-align:center}.featureCopy_WeZz h3{margin-bottom:12px;margin-top:16px}.heroImg_wnHi{margin-left:-12px;margin-right:-12px}}@media (max-width:576px){.markdown h1:first-child{--ifm-h1-font-size:2rem}.markdown>h2{--ifm-h2-font-size:1.5rem}.markdown>h3{--ifm-h3-font-size:1.25rem}.title_f1Hy{font-size:2rem}}@media screen and (max-width:576px){.searchQueryColumn_RTkw{max-width:100%!important}.searchVersionColumn_ypXd{max-width:100%!important;padding-left:var(--ifm-spacing-horizontal)!important}}@media screen and (max-width:500px){.discriminatorTabsTopSection_QeEp,.mimeTabsTopSection_wt53,.responseTabsTopSection_jEoq,.schemaTabsTopSection_sc6Y{align-items:flex-start;flex-direction:column}.mimeTabsContainer_gZbZ,.responseTabsContainer_XEhZ{margin-top:var(--ifm-spacing-vertical);padding:0;width:100%}.discriminatorTabsContainer_FMrl,.schemaTabsContainer_HVyG{width:100%}.tabItem_VIbn{height:100%}}@media (hover:hover){.backToTopButton_sjWU:hover{background-color:var(--ifm-color-emphasis-300)}}@media (pointer:fine){.thin-scrollbar{scrollbar-width:thin}.thin-scrollbar::-webkit-scrollbar{height:var(--ifm-scrollbar-size);width:var(--ifm-scrollbar-size)}.thin-scrollbar::-webkit-scrollbar-track{background:var(--ifm-scrollbar-track-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb{background:var(--ifm-scrollbar-thumb-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb:hover{background:var(--ifm-scrollbar-thumb-hover-background-color)}}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{stroke-width:var(--docsearch-icon-stroke-width);animation:none;-webkit-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0}.DocSearch-Hit--deleting,.DocSearch-Hit--favoriting{transition:none}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:none}}@media print{.announcementBar_mb4j,.footer,.menu,.navbar,.pagination-nav,.table-of-contents,.tocMobile_ITEo{display:none}.tabs{page-break-inside:avoid}.codeBlockLines_e6Vv{white-space:pre-wrap}} \ No newline at end of file +.col,.container{padding:0 var(--ifm-spacing-horizontal);width:100%}.markdown>h2,.markdown>h3,.markdown>h4,.markdown>h5,.markdown>h6{margin-bottom:calc(var(--ifm-heading-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown li,body{word-wrap:break-word}body,ol ol,ol ul,ul ol,ul ul{margin:0}pre,table{overflow:auto}blockquote,pre{margin:0 0 var(--ifm-spacing-vertical)}.breadcrumbs__link,.button{transition-timing-function:var(--ifm-transition-timing-default)}.button,code{vertical-align:middle}.button--outline.button--active,.button--outline:active,.button--outline:hover,:root{--ifm-button-color:var(--ifm-font-color-base-inverse)}.menu__link:hover,a{transition:color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.navbar--dark,:root{--ifm-navbar-link-hover-color:var(--ifm-color-primary)}:root,html[data-theme=dark]{--ifm-color-emphasis-500:var(--ifm-color-gray-500)}.toggleButton_gllP,html{-webkit-tap-highlight-color:transparent}.tableTheme_A_3f,table{border-collapse:collapse}*,.DocSearch-Container,.DocSearch-Container *,.carousel *{box-sizing:border-box}:root{--ifm-color-scheme:light;--ifm-dark-value:10%;--ifm-darker-value:15%;--ifm-darkest-value:30%;--ifm-light-value:15%;--ifm-lighter-value:30%;--ifm-lightest-value:50%;--ifm-contrast-background-value:90%;--ifm-contrast-foreground-value:70%;--ifm-contrast-background-dark-value:70%;--ifm-contrast-foreground-dark-value:90%;--ifm-color-primary:#3578e5;--ifm-color-secondary:#ebedf0;--ifm-color-success:#00a400;--ifm-color-info:#54c7ec;--ifm-color-warning:#ffba00;--ifm-color-danger:#fa383e;--ifm-color-primary-dark:#306cce;--ifm-color-primary-darker:#2d66c3;--ifm-color-primary-darkest:#2554a0;--ifm-color-primary-light:#538ce9;--ifm-color-primary-lighter:#72a1ed;--ifm-color-primary-lightest:#9abcf2;--ifm-color-primary-contrast-background:#ebf2fc;--ifm-color-primary-contrast-foreground:#102445;--ifm-color-secondary-dark:#d4d5d8;--ifm-color-secondary-darker:#c8c9cc;--ifm-color-secondary-darkest:#a4a6a8;--ifm-color-secondary-light:#eef0f2;--ifm-color-secondary-lighter:#f1f2f5;--ifm-color-secondary-lightest:#f5f6f8;--ifm-color-secondary-contrast-background:#fdfdfe;--ifm-color-secondary-contrast-foreground:#474748;--ifm-color-success-dark:#009400;--ifm-color-success-darker:#008b00;--ifm-color-success-darkest:#007300;--ifm-color-success-light:#26b226;--ifm-color-success-lighter:#4dbf4d;--ifm-color-success-lightest:#80d280;--ifm-color-success-contrast-background:#e6f6e6;--ifm-color-success-contrast-foreground:#003100;--ifm-color-info-dark:#4cb3d4;--ifm-color-info-darker:#47a9c9;--ifm-color-info-darkest:#3b8ba5;--ifm-color-info-light:#6ecfef;--ifm-color-info-lighter:#87d8f2;--ifm-color-info-lightest:#aae3f6;--ifm-color-info-contrast-background:#eef9fd;--ifm-color-info-contrast-foreground:#193c47;--ifm-color-warning-dark:#e6a700;--ifm-color-warning-darker:#d99e00;--ifm-color-warning-darkest:#b38200;--ifm-color-warning-light:#ffc426;--ifm-color-warning-lighter:#ffcf4d;--ifm-color-warning-lightest:#ffdd80;--ifm-color-warning-contrast-background:#fff8e6;--ifm-color-warning-contrast-foreground:#4d3800;--ifm-color-danger-dark:#e13238;--ifm-color-danger-darker:#d53035;--ifm-color-danger-darkest:#af272b;--ifm-color-danger-light:#fb565b;--ifm-color-danger-lighter:#fb7478;--ifm-color-danger-lightest:#fd9c9f;--ifm-color-danger-contrast-background:#ffebec;--ifm-color-danger-contrast-foreground:#4b1113;--ifm-color-white:#fff;--ifm-color-black:#000;--ifm-color-gray-0:var(--ifm-color-white);--ifm-color-gray-100:#f5f6f7;--ifm-color-gray-200:#ebedf0;--ifm-color-gray-300:#dadde1;--ifm-color-gray-400:#ccd0d5;--ifm-color-gray-500:#bec3c9;--ifm-color-gray-600:#8d949e;--ifm-color-gray-700:#606770;--ifm-color-gray-800:#444950;--ifm-color-gray-900:#1c1e21;--ifm-color-gray-1000:var(--ifm-color-black);--ifm-color-emphasis-0:var(--ifm-color-gray-0);--ifm-color-emphasis-100:var(--ifm-color-gray-100);--ifm-color-emphasis-200:var(--ifm-color-gray-200);--ifm-color-emphasis-300:var(--ifm-color-gray-300);--ifm-color-emphasis-400:var(--ifm-color-gray-400);--ifm-color-emphasis-600:var(--ifm-color-gray-600);--ifm-color-emphasis-700:var(--ifm-color-gray-700);--ifm-color-emphasis-800:var(--ifm-color-gray-800);--ifm-color-emphasis-900:var(--ifm-color-gray-900);--ifm-color-emphasis-1000:var(--ifm-color-gray-1000);--ifm-color-content:var(--ifm-color-emphasis-900);--ifm-color-content-inverse:var(--ifm-color-emphasis-0);--ifm-color-content-secondary:#525860;--ifm-background-color:transparent;--ifm-background-surface-color:var(--ifm-color-content-inverse);--ifm-global-border-width:1px;--ifm-global-radius:0.4rem;--ifm-hover-overlay:rgba(0,0,0,.05);--ifm-font-color-base:var(--ifm-color-content);--ifm-font-color-base-inverse:var(--ifm-color-content-inverse);--ifm-font-color-secondary:var(--ifm-color-content-secondary);--ifm-font-family-base:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--ifm-font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--ifm-font-size-base:100%;--ifm-font-weight-light:300;--ifm-font-weight-normal:400;--ifm-font-weight-semibold:500;--ifm-font-weight-bold:700;--ifm-font-weight-base:var(--ifm-font-weight-normal);--ifm-line-height-base:1.65;--ifm-global-spacing:1rem;--ifm-spacing-vertical:var(--ifm-global-spacing);--ifm-spacing-horizontal:var(--ifm-global-spacing);--ifm-transition-fast:200ms;--ifm-transition-slow:400ms;--ifm-transition-timing-default:cubic-bezier(0.08,0.52,0.52,1);--ifm-global-shadow-lw:0 1px 2px 0 rgba(0,0,0,.1);--ifm-global-shadow-md:0 5px 40px rgba(0,0,0,.2);--ifm-global-shadow-tl:0 12px 28px 0 rgba(0,0,0,.2),0 2px 4px 0 rgba(0,0,0,.1);--ifm-z-index-dropdown:100;--ifm-z-index-fixed:200;--ifm-z-index-overlay:400;--ifm-container-width:1140px;--ifm-container-width-xl:1320px;--ifm-code-background:#f6f7f8;--ifm-code-border-radius:var(--ifm-global-radius);--ifm-code-font-size:90%;--ifm-code-padding-horizontal:0.1rem;--ifm-code-padding-vertical:0.1rem;--ifm-pre-background:var(--ifm-code-background);--ifm-pre-border-radius:var(--ifm-code-border-radius);--ifm-pre-color:inherit;--ifm-pre-line-height:1.45;--ifm-pre-padding:1rem;--ifm-heading-color:inherit;--ifm-heading-margin-top:0;--ifm-heading-margin-bottom:var(--ifm-spacing-vertical);--ifm-heading-font-family:var(--ifm-font-family-base);--ifm-heading-font-weight:var(--ifm-font-weight-bold);--ifm-heading-line-height:1.25;--ifm-h1-font-size:2rem;--ifm-h2-font-size:1.5rem;--ifm-h3-font-size:1.25rem;--ifm-h4-font-size:1rem;--ifm-h5-font-size:0.875rem;--ifm-h6-font-size:0.85rem;--ifm-image-alignment-padding:1.25rem;--ifm-leading-desktop:1.25;--ifm-leading:calc(var(--ifm-leading-desktop)*1rem);--ifm-list-left-padding:2rem;--ifm-list-margin:1rem;--ifm-list-item-margin:0.25rem;--ifm-list-paragraph-margin:1rem;--ifm-table-cell-padding:0.75rem;--ifm-table-background:transparent;--ifm-table-stripe-background:rgba(0,0,0,.03);--ifm-table-border-width:1px;--ifm-table-border-color:var(--ifm-color-emphasis-300);--ifm-table-head-background:inherit;--ifm-table-head-color:inherit;--ifm-table-head-font-weight:var(--ifm-font-weight-bold);--ifm-table-cell-color:inherit;--ifm-link-color:var(--ifm-color-primary);--ifm-link-decoration:none;--ifm-link-hover-color:var(--ifm-link-color);--ifm-link-hover-decoration:underline;--ifm-paragraph-margin-bottom:var(--ifm-leading);--ifm-blockquote-font-size:var(--ifm-font-size-base);--ifm-blockquote-border-left-width:2px;--ifm-blockquote-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-blockquote-padding-vertical:0;--ifm-blockquote-shadow:none;--ifm-blockquote-color:var(--ifm-color-emphasis-800);--ifm-blockquote-border-color:var(--ifm-color-emphasis-300);--ifm-hr-background-color:var(--ifm-color-emphasis-500);--ifm-hr-height:1px;--ifm-hr-margin-vertical:1.5rem;--ifm-scrollbar-size:7px;--ifm-scrollbar-track-background-color:#f1f1f1;--ifm-scrollbar-thumb-background-color:silver;--ifm-scrollbar-thumb-hover-background-color:#a7a7a7;--ifm-alert-background-color:inherit;--ifm-alert-border-color:inherit;--ifm-alert-border-radius:var(--ifm-global-radius);--ifm-alert-border-width:0px;--ifm-alert-border-left-width:5px;--ifm-alert-color:var(--ifm-font-color-base);--ifm-alert-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-alert-padding-vertical:var(--ifm-spacing-vertical);--ifm-alert-shadow:var(--ifm-global-shadow-lw);--ifm-avatar-intro-margin:1rem;--ifm-avatar-intro-alignment:inherit;--ifm-avatar-photo-size:3rem;--ifm-badge-background-color:inherit;--ifm-badge-border-color:inherit;--ifm-badge-border-radius:var(--ifm-global-radius);--ifm-badge-border-width:var(--ifm-global-border-width);--ifm-badge-color:var(--ifm-color-white);--ifm-badge-padding-horizontal:calc(var(--ifm-spacing-horizontal)*0.5);--ifm-badge-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-breadcrumb-border-radius:1.5rem;--ifm-breadcrumb-spacing:0.5rem;--ifm-breadcrumb-color-active:var(--ifm-color-primary);--ifm-breadcrumb-item-background-active:var(--ifm-hover-overlay);--ifm-breadcrumb-padding-horizontal:0.8rem;--ifm-breadcrumb-padding-vertical:0.4rem;--ifm-breadcrumb-size-multiplier:1;--ifm-breadcrumb-separator:url('data:image/svg+xml;utf8,');--ifm-breadcrumb-separator-filter:none;--ifm-breadcrumb-separator-size:0.5rem;--ifm-breadcrumb-separator-size-multiplier:1.25;--ifm-button-background-color:inherit;--ifm-button-border-color:var(--ifm-button-background-color);--ifm-button-border-width:var(--ifm-global-border-width);--ifm-button-font-weight:var(--ifm-font-weight-bold);--ifm-button-padding-horizontal:1.5rem;--ifm-button-padding-vertical:0.375rem;--ifm-button-size-multiplier:1;--ifm-button-transition-duration:var(--ifm-transition-fast);--ifm-button-border-radius:calc(var(--ifm-global-radius)*var(--ifm-button-size-multiplier));--ifm-button-group-spacing:2px;--ifm-card-background-color:var(--ifm-background-surface-color);--ifm-card-border-radius:calc(var(--ifm-global-radius)*2);--ifm-card-horizontal-spacing:var(--ifm-global-spacing);--ifm-card-vertical-spacing:var(--ifm-global-spacing);--ifm-toc-border-color:var(--ifm-color-emphasis-300);--ifm-toc-link-color:var(--ifm-color-content-secondary);--ifm-toc-padding-vertical:0.5rem;--ifm-toc-padding-horizontal:0.5rem;--ifm-dropdown-background-color:var(--ifm-background-surface-color);--ifm-dropdown-font-weight:var(--ifm-font-weight-semibold);--ifm-dropdown-link-color:var(--ifm-font-color-base);--ifm-dropdown-hover-background-color:var(--ifm-hover-overlay);--ifm-footer-background-color:var(--ifm-color-emphasis-100);--ifm-footer-color:inherit;--ifm-footer-link-color:var(--ifm-color-emphasis-700);--ifm-footer-link-hover-color:var(--ifm-color-primary);--ifm-footer-link-horizontal-spacing:0.5rem;--ifm-footer-padding-horizontal:calc(var(--ifm-spacing-horizontal)*2);--ifm-footer-padding-vertical:calc(var(--ifm-spacing-vertical)*2);--ifm-footer-title-color:inherit;--ifm-footer-logo-max-width:min(30rem,90vw);--ifm-hero-background-color:var(--ifm-background-surface-color);--ifm-hero-text-color:var(--ifm-color-emphasis-800);--ifm-menu-color:var(--ifm-color-emphasis-700);--ifm-menu-color-active:var(--ifm-color-primary);--ifm-menu-color-background-active:var(--ifm-hover-overlay);--ifm-menu-color-background-hover:var(--ifm-hover-overlay);--ifm-menu-link-padding-horizontal:0.75rem;--ifm-menu-link-padding-vertical:0.375rem;--ifm-menu-link-sublist-icon:url('data:image/svg+xml;utf8,');--ifm-menu-link-sublist-icon-filter:none;--ifm-navbar-background-color:var(--ifm-background-surface-color);--ifm-navbar-height:3.75rem;--ifm-navbar-item-padding-horizontal:0.75rem;--ifm-navbar-item-padding-vertical:0.25rem;--ifm-navbar-link-color:var(--ifm-font-color-base);--ifm-navbar-link-active-color:var(--ifm-link-color);--ifm-navbar-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-navbar-padding-vertical:calc(var(--ifm-spacing-vertical)*0.5);--ifm-navbar-shadow:var(--ifm-global-shadow-lw);--ifm-navbar-search-input-background-color:var(--ifm-color-emphasis-200);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-800);--ifm-navbar-search-input-placeholder-color:var(--ifm-color-emphasis-500);--ifm-navbar-search-input-icon:url('data:image/svg+xml;utf8,');--ifm-navbar-sidebar-width:83vw;--ifm-pagination-border-radius:var(--ifm-global-radius);--ifm-pagination-color-active:var(--ifm-color-primary);--ifm-pagination-font-size:1rem;--ifm-pagination-item-active-background:var(--ifm-hover-overlay);--ifm-pagination-page-spacing:0.2em;--ifm-pagination-padding-horizontal:calc(var(--ifm-spacing-horizontal)*1);--ifm-pagination-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-pagination-nav-border-radius:var(--ifm-global-radius);--ifm-pagination-nav-color-hover:var(--ifm-color-primary);--ifm-pills-color-active:var(--ifm-color-primary);--ifm-pills-color-background-active:var(--ifm-hover-overlay);--ifm-pills-spacing:0.125rem;--ifm-tabs-color:var(--ifm-font-color-secondary);--ifm-tabs-color-active:var(--ifm-color-primary);--ifm-tabs-color-active-border:var(--ifm-tabs-color-active);--ifm-tabs-padding-horizontal:1rem;--ifm-tabs-padding-vertical:1rem}.badge--danger,.badge--info,.badge--primary,.badge--secondary,.badge--success,.badge--warning{--ifm-badge-border-color:var(--ifm-badge-background-color)}.button--link,.button--outline{--ifm-button-background-color:transparent}html{-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--ifm-background-color);color:var(--ifm-font-color-base);color-scheme:var(--ifm-color-scheme);font:var(--ifm-font-size-base)/var(--ifm-line-height-base) var(--ifm-font-family-base);scroll-behavior:smooth}body{color:var(--main)}iframe{border:0;color-scheme:auto}.container{margin:0 auto;max-width:var(--ifm-container-width)}.container--fluid{max-width:inherit}.row{display:flex;flex-wrap:wrap;margin:0 calc(var(--ifm-spacing-horizontal)*-1)}.list_eTzJ article:last-child,.margin-bottom--none,.margin-vert--none,.markdown>:last-child{margin-bottom:0!important}.margin-top--none,.margin-vert--none,.tabItem_kzG2{margin-top:0!important}.row--no-gutters{margin-left:0;margin-right:0}.margin-horiz--none,.margin-right--none,.tabItem_AM8E:last-child,.tabItem_VIbn:last-child,.tabItem_es3Q:last-child,.tabItem_fIhq:last-child{margin-right:0!important}.row--no-gutters>.col{padding-left:0;padding-right:0}.row--align-top{align-items:flex-start}.row--align-bottom{align-items:flex-end}.menuExternalLink_NmtK,.row--align-center{align-items:center}.row--align-stretch{align-items:stretch}.row--align-baseline{align-items:baseline}.col{--ifm-col-width:100%;flex:1 0;margin-left:0;max-width:var(--ifm-col-width)}.padding-bottom--none,.padding-vert--none{padding-bottom:0!important}.padding-top--none,.padding-vert--none,.pt0{padding-top:0!important}.discriminatorTabsContainer,.padding-horiz--none,.padding-left--none{padding-left:0!important}.padding-horiz--none,.padding-right--none{padding-right:0!important}.col[class*=col--]{flex:0 0 var(--ifm-col-width)}.col--1{--ifm-col-width:8.33333%}.col--offset-1{margin-left:8.33333%}.col--2{--ifm-col-width:16.66667%}.col--offset-2{margin-left:16.66667%}.col--3{--ifm-col-width:25%}.col--offset-3{margin-left:25%}.col--4{--ifm-col-width:33.33333%}.col--offset-4{margin-left:33.33333%}.col--5{--ifm-col-width:41.66667%}.col--offset-5{margin-left:41.66667%}.col--6{--ifm-col-width:50%}.col--offset-6{margin-left:50%}.col--7{--ifm-col-width:58.33333%}.col--offset-7{margin-left:58.33333%}.col--8{--ifm-col-width:66.66667%}.col--offset-8{margin-left:66.66667%}.col--9{--ifm-col-width:75%}.col--offset-9{margin-left:75%}.col--10{--ifm-col-width:83.33333%}.col--offset-10{margin-left:83.33333%}.col--11{--ifm-col-width:91.66667%}.col--offset-11{margin-left:91.66667%}.col--12{--ifm-col-width:100%}.col--offset-12{margin-left:100%}.margin-horiz--none,.margin-left--none{margin-left:0!important}.margin--none{margin:0!important}.margin-bottom--xs,.margin-vert--xs{margin-bottom:.25rem!important}.margin-top--xs,.margin-vert--xs{margin-top:.25rem!important}.margin-horiz--xs,.margin-left--xs{margin-left:.25rem!important}.margin-horiz--xs,.margin-right--xs{margin-right:.25rem!important}.margin--xs{margin:.25rem!important}.margin-bottom--sm,.margin-vert--sm{margin-bottom:.5rem!important}.margin-top--sm,.margin-vert--sm{margin-top:.5rem!important}.margin-horiz--sm,.margin-left--sm{margin-left:.5rem!important}.margin-horiz--sm,.margin-right--sm{margin-right:.5rem!important}.margin--sm{margin:.5rem!important}.margin-bottom--md,.margin-vert--md{margin-bottom:1rem!important}.margin-top--md,.margin-vert--md{margin-top:1rem!important}.margin-horiz--md,.margin-left--md{margin-left:1rem!important}.margin-horiz--md,.margin-right--md{margin-right:1rem!important}.margin--md{margin:1rem!important}.margin-bottom--lg,.margin-vert--lg{margin-bottom:2rem!important}.margin-top--lg,.margin-vert--lg{margin-top:2rem!important}.margin-horiz--lg,.margin-left--lg{margin-left:2rem!important}.margin-horiz--lg,.margin-right--lg{margin-right:2rem!important}.margin--lg{margin:2rem!important}.margin-bottom--xl,.margin-vert--xl{margin-bottom:5rem!important}.margin-top--xl,.margin-vert--xl{margin-top:5rem!important}.margin-horiz--xl,.margin-left--xl{margin-left:5rem!important}.margin-horiz--xl,.margin-right--xl{margin-right:5rem!important}.margin--xl{margin:5rem!important}.padding--none{padding:0!important}.padding-bottom--xs,.padding-vert--xs{padding-bottom:.25rem!important}.padding-top--xs,.padding-vert--xs{padding-top:.25rem!important}.padding-horiz--xs,.padding-left--xs{padding-left:.25rem!important}.padding-horiz--xs,.padding-right--xs{padding-right:.25rem!important}.padding--xs{padding:.25rem!important}.padding-bottom--sm,.padding-vert--sm{padding-bottom:.5rem!important}.padding-top--sm,.padding-vert--sm{padding-top:.5rem!important}.padding-horiz--sm,.padding-left--sm{padding-left:.5rem!important}.padding-horiz--sm,.padding-right--sm{padding-right:.5rem!important}.padding--sm{padding:.5rem!important}.padding-bottom--md,.padding-vert--md{padding-bottom:1rem!important}.padding-top--md,.padding-vert--md{padding-top:1rem!important}.padding-horiz--md,.padding-left--md{padding-left:1rem!important}.padding-horiz--md,.padding-right--md{padding-right:1rem!important}.padding--md{padding:1rem!important}.padding-bottom--lg,.padding-vert--lg{padding-bottom:2rem!important}.padding-top--lg,.padding-vert--lg{padding-top:2rem!important}.padding-horiz--lg,.padding-left--lg{padding-left:2rem!important}.padding-horiz--lg,.padding-right--lg{padding-right:2rem!important}.padding--lg{padding:2rem!important}.padding-bottom--xl,.padding-vert--xl{padding-bottom:5rem!important}.padding-top--xl,.padding-vert--xl{padding-top:5rem!important}.padding-horiz--xl,.padding-left--xl{padding-left:5rem!important}.padding-horiz--xl,.padding-right--xl{padding-right:5rem!important}.padding--xl{padding:5rem!important}code{background-color:var(--ifm-code-background);border:.1rem solid rgba(0,0,0,.1);border-radius:var(--ifm-code-border-radius);font-family:var(--ifm-font-family-monospace);font-size:var(--ifm-code-font-size);padding:var(--ifm-code-padding-vertical) var(--ifm-code-padding-horizontal)}a code{color:inherit}pre{background-color:var(--ifm-pre-background);border-radius:var(--ifm-pre-border-radius);color:var(--ifm-pre-color);font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace);padding:var(--ifm-pre-padding)}pre code{background-color:transparent;border:none;font-size:100%;line-height:inherit;padding:0}kbd{background-color:var(--ifm-color-emphasis-0);border:1px solid var(--ifm-color-emphasis-400);border-radius:.2rem;box-shadow:inset 0 -1px 0 var(--ifm-color-emphasis-400);color:var(--ifm-color-emphasis-800);font:80% var(--ifm-font-family-monospace);padding:.15rem .3rem}h1,h2,h3,h4,h5,h6{color:var(--ifm-heading-color);font-family:var(--ifm-heading-font-family);font-weight:var(--ifm-heading-font-weight);line-height:var(--ifm-heading-line-height);margin:var(--ifm-heading-margin-top) 0 var(--ifm-heading-margin-bottom) 0}h1{font-size:var(--ifm-h1-font-size)}h2{font-size:var(--ifm-h2-font-size)}h3{font-size:var(--ifm-h3-font-size)}h4{font-size:var(--ifm-h4-font-size)}h5{font-size:var(--ifm-h5-font-size)}h6{font-size:var(--ifm-h6-font-size)}img{max-width:100%}img[align=right]{padding-left:var(--image-alignment-padding)}img[align=left]{padding-right:var(--image-alignment-padding)}.markdown{--ifm-h1-vertical-rhythm-top:3;--ifm-h2-vertical-rhythm-top:2;--ifm-h3-vertical-rhythm-top:1.5;--ifm-heading-vertical-rhythm-top:1.25;--ifm-h1-vertical-rhythm-bottom:1.25;--ifm-heading-vertical-rhythm-bottom:1}.markdown:after,.markdown:before{content:"";display:table}.markdown:after{clear:both}.markdown h1:first-child{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-h1-vertical-rhythm-bottom)*var(--ifm-leading));--ifm-h1-font-size:2.5rem}.markdown>h2{--ifm-h2-font-size:2rem;margin-top:calc(var(--ifm-h2-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h3{--ifm-h3-font-size:1.5rem;margin-top:calc(var(--ifm-h3-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h4,.markdown>h5,.markdown>h6{margin-top:calc(var(--ifm-heading-vertical-rhythm-top)*var(--ifm-leading))}.markdown>p,.markdown>pre,.markdown>ul,.tabList_OQG4{margin-bottom:var(--ifm-leading)}.markdown li>p{margin-top:var(--ifm-list-paragraph-margin)}.markdown li+li{margin-top:var(--ifm-list-item-margin)}ol,ul{margin:0 0 var(--ifm-list-margin);padding-left:var(--ifm-list-left-padding)}ol ol,ul ol{list-style-type:lower-roman}ol ol ol,ol ul ol,ul ol ol,ul ul ol{list-style-type:lower-alpha}table{display:block;margin-bottom:var(--ifm-spacing-vertical)}table thead tr{border-bottom:2px solid var(--ifm-table-border-color)}table thead,table tr:nth-child(2n){background-color:var(--ifm-table-stripe-background)}table tr{background-color:var(--ifm-table-background);border-top:var(--ifm-table-border-width) solid var(--ifm-table-border-color)}table td,table th{border:var(--ifm-table-border-width) solid var(--ifm-table-border-color);padding:var(--ifm-table-cell-padding)}table th{background-color:var(--ifm-table-head-background);color:var(--ifm-table-head-color);font-weight:var(--ifm-table-head-font-weight)}table td{color:var(--ifm-table-cell-color)}strong{font-weight:var(--ifm-font-weight-bold)}a{color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration);transition:.2s}a:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration);text-decoration:none}.button:hover,.text--no-decoration,.text--no-decoration:hover,a:not([href]){text-decoration:none}p{margin:0 0 var(--ifm-paragraph-margin-bottom)}blockquote{border-left:var(--ifm-blockquote-border-left-width) solid var(--ifm-blockquote-border-color);box-shadow:var(--ifm-blockquote-shadow);color:var(--ifm-blockquote-color);font-size:var(--ifm-blockquote-font-size);padding:var(--ifm-blockquote-padding-vertical) var(--ifm-blockquote-padding-horizontal)}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{background-color:var(--ifm-hr-background-color);border:0;height:var(--ifm-hr-height);margin:var(--ifm-hr-margin-vertical) 0}.shadow--lw{box-shadow:var(--ifm-global-shadow-lw)!important}.shadow--md{box-shadow:var(--ifm-global-shadow-md)!important}.shadow--tl{box-shadow:var(--ifm-global-shadow-tl)!important}.text--primary,.wordWrapButtonEnabled_EoeP .wordWrapButtonIcon_Bwma{color:var(--ifm-color-primary)}.text--secondary{color:var(--ifm-color-secondary)}.text--success{color:var(--ifm-color-success)}.text--info{color:var(--ifm-color-info)}.text--warning{color:var(--ifm-color-warning)}.text--danger{color:var(--ifm-color-danger)}.text--center{text-align:center}.text--left{text-align:left}.text--justify{text-align:justify}.text--right{text-align:right}.text--capitalize{text-transform:capitalize}.text--lowercase{text-transform:lowercase}.admonitionHeading_tbUL,.alert__heading,.sectPreHeadline_myin,.text--uppercase{text-transform:uppercase}.text--light{font-weight:var(--ifm-font-weight-light)}.text--normal{font-weight:var(--ifm-font-weight-normal)}.menu,.text--semibold{font-weight:var(--ifm-font-weight-semibold)}.text--bold{font-weight:var(--ifm-font-weight-bold)}.text--italic{font-style:italic}.text--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text--break{word-wrap:break-word!important;word-break:break-word!important}.clean-btn{background:none;border:none;color:inherit;cursor:pointer;font-family:inherit;padding:0}.alert,.alert .close{color:var(--ifm-alert-foreground-color)}.clean-list{list-style:none;padding-left:0}.alert--primary{--ifm-alert-background-color:var(--ifm-color-primary-contrast-background);--ifm-alert-background-color-highlight:rgba(53,120,229,.15);--ifm-alert-foreground-color:var(--ifm-color-primary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-primary-dark)}.alert--secondary{--ifm-alert-background-color:var(--ifm-color-secondary-contrast-background);--ifm-alert-background-color-highlight:rgba(235,237,240,.15);--ifm-alert-foreground-color:var(--ifm-color-secondary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-secondary-dark)}.alert--success{--ifm-alert-background-color:var(--ifm-color-success-contrast-background);--ifm-alert-background-color-highlight:rgba(0,164,0,.15);--ifm-alert-foreground-color:var(--ifm-color-success-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-success-dark)}.alert--info{--ifm-alert-background-color:var(--ifm-color-info-contrast-background);--ifm-alert-background-color-highlight:rgba(84,199,236,.15);--ifm-alert-foreground-color:var(--ifm-color-info-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-info-dark)}.alert--warning{--ifm-alert-background-color:var(--ifm-color-warning-contrast-background);--ifm-alert-background-color-highlight:rgba(255,186,0,.15);--ifm-alert-foreground-color:var(--ifm-color-warning-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-warning-dark)}.alert--danger{--ifm-alert-background-color:var(--ifm-color-danger-contrast-background);--ifm-alert-background-color-highlight:rgba(250,56,62,.15);--ifm-alert-foreground-color:var(--ifm-color-danger-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-danger-dark)}.alert{--ifm-code-background:var(--ifm-alert-background-color-highlight);--ifm-link-color:var(--ifm-alert-foreground-color);--ifm-link-hover-color:var(--ifm-alert-foreground-color);--ifm-link-decoration:underline;--ifm-tabs-color:var(--ifm-alert-foreground-color);--ifm-tabs-color-active:var(--ifm-alert-foreground-color);--ifm-tabs-color-active-border:var(--ifm-alert-border-color);background-color:var(--ifm-alert-background-color);border:var(--ifm-alert-border-width) solid var(--ifm-alert-border-color);border-left-width:var(--ifm-alert-border-left-width);border-radius:var(--ifm-alert-border-radius);box-shadow:var(--ifm-alert-shadow);padding:var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal)}.alert__heading{align-items:center;display:flex;font:700 var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.5rem}.alert__icon{display:inline-flex;margin-right:.4em}.alert__icon svg{fill:var(--ifm-alert-foreground-color);stroke:var(--ifm-alert-foreground-color);stroke-width:0}.alert .close{margin:calc(var(--ifm-alert-padding-vertical)*-1) calc(var(--ifm-alert-padding-horizontal)*-1) 0 0;opacity:.75}.alert .close:focus,.alert .close:hover{opacity:1}.alert a{-webkit-text-decoration-color:var(--ifm-alert-border-color);text-decoration-color:var(--ifm-alert-border-color)}.alert a:hover{text-decoration-thickness:2px}.avatar{column-gap:var(--ifm-avatar-intro-margin);display:flex}.avatar__photo{border-radius:50%;display:block;height:var(--ifm-avatar-photo-size);overflow:hidden;width:var(--ifm-avatar-photo-size)}.avatar__photo--sm{--ifm-avatar-photo-size:2rem}.avatar__photo--lg{--ifm-avatar-photo-size:4rem}.avatar__photo--xl{--ifm-avatar-photo-size:6rem}.avatar__intro{display:flex;flex:1 1;flex-direction:column;justify-content:center;text-align:var(--ifm-avatar-intro-alignment)}.badge,.breadcrumbs__item,.breadcrumbs__link,.button,.dropdown>.navbar__link:after{display:inline-block}.avatar__name{font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base)}.avatar__subtitle{margin-top:.25rem}.avatar--vertical{--ifm-avatar-intro-alignment:center;--ifm-avatar-intro-margin:0.5rem;align-items:center;flex-direction:column}.badge{background-color:var(--ifm-badge-background-color);border:var(--ifm-badge-border-width) solid var(--ifm-badge-border-color);border-radius:var(--ifm-badge-border-radius);color:var(--ifm-badge-color);font-size:75%;font-weight:var(--ifm-font-weight-bold);line-height:1;padding:var(--ifm-badge-padding-vertical) var(--ifm-badge-padding-horizontal)}.badge--primary{--ifm-badge-background-color:var(--ifm-color-primary)}.badge--secondary{--ifm-badge-background-color:var(--ifm-color-secondary);color:var(--ifm-color-black)}.breadcrumbs__link,.button.button--secondary.button--outline:not(.button--active):not(:hover){color:var(--ifm-font-color-base)}.badge--success{--ifm-badge-background-color:var(--ifm-color-success)}.badge--info{--ifm-badge-background-color:var(--ifm-color-info)}.badge--warning{--ifm-badge-background-color:var(--ifm-color-warning)}.badge--danger{--ifm-badge-background-color:var(--ifm-color-danger)}.breadcrumbs{margin-bottom:0;padding-left:0}.breadcrumbs__item:not(:last-child):after{background:var(--ifm-breadcrumb-separator) center;content:" ";display:inline-block;filter:var(--ifm-breadcrumb-separator-filter);height:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier));margin:0 var(--ifm-breadcrumb-spacing);opacity:.5;width:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier))}.breadcrumbs__item--active .breadcrumbs__link{background:var(--ifm-breadcrumb-item-background-active);color:var(--ifm-breadcrumb-color-active)}.breadcrumbs__link{border-radius:var(--ifm-breadcrumb-border-radius);font-size:calc(1rem*var(--ifm-breadcrumb-size-multiplier));padding:calc(var(--ifm-breadcrumb-padding-vertical)*var(--ifm-breadcrumb-size-multiplier)) calc(var(--ifm-breadcrumb-padding-horizontal)*var(--ifm-breadcrumb-size-multiplier));transition-duration:var(--ifm-transition-fast);transition-property:background,color}.breadcrumbs__link:link:hover,.breadcrumbs__link:visited:hover,area.breadcrumbs__link[href]:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs__link:-webkit-any-link:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs__link:any-link:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs--sm{--ifm-breadcrumb-size-multiplier:0.8}.breadcrumbs--lg{--ifm-breadcrumb-size-multiplier:1.2}.button{background-color:var(--ifm-button-background-color);border:var(--ifm-button-border-width) solid var(--ifm-button-border-color);border-radius:var(--ifm-button-border-radius);cursor:pointer;font-size:calc(.875rem*var(--ifm-button-size-multiplier));font-weight:var(--ifm-button-font-weight);line-height:1.5;padding:calc(var(--ifm-button-padding-vertical)*var(--ifm-button-size-multiplier)) calc(var(--ifm-button-padding-horizontal)*var(--ifm-button-size-multiplier));text-align:center;transition-duration:var(--ifm-button-transition-duration);transition-property:color,background,border-color;-webkit-user-select:none;user-select:none;white-space:nowrap}.aportal_VoVb img,.carousel .slide img,.carousel .thumb img,.dropdown,.keycloak_K4jr img{vertical-align:top}.button,.button:hover{color:var(--ifm-button-color)}.button--outline{--ifm-button-color:var(--ifm-button-border-color)}.button--outline:hover{--ifm-button-background-color:var(--ifm-button-border-color)}.button--link{--ifm-button-border-color:transparent;color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}.button--link.button--active,.button--link:active,.button--link:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button.disabled,.button:disabled,.button[disabled]{opacity:.65;pointer-events:none}.button--sm{--ifm-button-size-multiplier:0.8}.button--lg{--ifm-button-size-multiplier:1.35}.button--block,.planFoot_Kh7l .btnPlan_oVpz{display:block;width:100%}.button.button--secondary{color:var(--ifm-color-gray-900)}:where(.button--primary){--ifm-button-background-color:var(--ifm-color-primary);--ifm-button-border-color:var(--ifm-color-primary)}:where(.button--primary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-primary-dark);--ifm-button-border-color:var(--ifm-color-primary-dark)}.button--primary.button--active,.button--primary:active{--ifm-button-background-color:var(--ifm-color-primary-darker);--ifm-button-border-color:var(--ifm-color-primary-darker)}:where(.button--secondary){--ifm-button-background-color:var(--ifm-color-secondary);--ifm-button-border-color:var(--ifm-color-secondary)}:where(.button--secondary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-secondary-dark);--ifm-button-border-color:var(--ifm-color-secondary-dark)}.button--secondary.button--active,.button--secondary:active{--ifm-button-background-color:var(--ifm-color-secondary-darker);--ifm-button-border-color:var(--ifm-color-secondary-darker)}:where(.button--success){--ifm-button-background-color:var(--ifm-color-success);--ifm-button-border-color:var(--ifm-color-success)}:where(.button--success):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-success-dark);--ifm-button-border-color:var(--ifm-color-success-dark)}.button--success.button--active,.button--success:active{--ifm-button-background-color:var(--ifm-color-success-darker);--ifm-button-border-color:var(--ifm-color-success-darker)}:where(.button--info){--ifm-button-background-color:var(--ifm-color-info);--ifm-button-border-color:var(--ifm-color-info)}:where(.button--info):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-info-dark);--ifm-button-border-color:var(--ifm-color-info-dark)}.button--info.button--active,.button--info:active{--ifm-button-background-color:var(--ifm-color-info-darker);--ifm-button-border-color:var(--ifm-color-info-darker)}:where(.button--warning){--ifm-button-background-color:var(--ifm-color-warning);--ifm-button-border-color:var(--ifm-color-warning)}:where(.button--warning):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-warning-dark);--ifm-button-border-color:var(--ifm-color-warning-dark)}.button--warning.button--active,.button--warning:active{--ifm-button-background-color:var(--ifm-color-warning-darker);--ifm-button-border-color:var(--ifm-color-warning-darker)}:where(.button--danger){--ifm-button-background-color:var(--ifm-color-danger);--ifm-button-border-color:var(--ifm-color-danger)}:where(.button--danger):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-danger-dark);--ifm-button-border-color:var(--ifm-color-danger-dark)}.button--danger.button--active,.button--danger:active{--ifm-button-background-color:var(--ifm-color-danger-darker);--ifm-button-border-color:var(--ifm-color-danger-darker)}.button-group{display:inline-flex;gap:var(--ifm-button-group-spacing)}.button-group>.button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.button-group>.button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.button-group--block{display:flex;justify-content:stretch}.button-group--block>.button{flex-grow:1}.card{background-color:var(--ifm-card-background-color);border-radius:var(--ifm-card-border-radius);box-shadow:var(--ifm-global-shadow-lw);display:flex;flex-direction:column;overflow:hidden}.card--full-height{height:100%}.card__image{padding-top:var(--ifm-card-vertical-spacing)}.card__image:first-child{padding-top:0}.card__body,.card__footer,.card__header{padding:var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing)}.card__body:not(:last-child),.card__footer:not(:last-child),.card__header:not(:last-child){padding-bottom:0}.card__body>:last-child,.card__footer>:last-child,.card__header>:last-child{margin-bottom:0}.card__footer{margin-top:auto}.table-of-contents{font-size:.8rem;margin-bottom:0;padding:var(--ifm-toc-padding-vertical) 0}.table-of-contents,.table-of-contents ul{list-style:none;padding-left:var(--ifm-toc-padding-horizontal)}.table-of-contents li{margin:var(--ifm-toc-padding-vertical) var(--ifm-toc-padding-horizontal)}.table-of-contents__left-border{border-left:1px solid var(--ifm-toc-border-color)}.table-of-contents__link{color:var(--ifm-toc-link-color);display:block}.table-of-contents__link--active,.table-of-contents__link--active code,.table-of-contents__link:hover,.table-of-contents__link:hover code{color:var(--ifm-color-primary);text-decoration:none}.close{color:var(--ifm-color-black);float:right;font-size:1.5rem;font-weight:var(--ifm-font-weight-bold);line-height:1;opacity:.5;padding:1rem;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.close:hover{opacity:.7}.close:focus,.theme-code-block-highlighted-line .codeLineNumber_Tfdd:before{opacity:.8}.dropdown{display:inline-flex;font-weight:var(--ifm-dropdown-font-weight);position:relative}.dropdown--hoverable:hover .dropdown__menu,.dropdown--show .dropdown__menu{opacity:1;pointer-events:all;transform:translateY(-1px);visibility:visible}#nprogress,.carousel img,.dropdown__menu,.navbar__item.dropdown .navbar__link:not([href]){pointer-events:none}.dropdown--right .dropdown__menu{left:inherit;right:0}.dropdown--nocaret .navbar__link:after{content:none!important}.dropdown__menu{background-color:var(--ifm-dropdown-background-color);border-radius:var(--ifm-global-radius);box-shadow:var(--ifm-global-shadow-md);left:0;list-style:none;max-height:80vh;min-width:10rem;opacity:0;overflow-y:auto;position:absolute;top:calc(100% - var(--ifm-navbar-item-padding-vertical) + .3rem);transform:translateY(-.625rem);transition-duration:var(--ifm-transition-fast);transition-property:opacity,transform,visibility;transition-timing-function:var(--ifm-transition-timing-default);visibility:hidden;z-index:var(--ifm-z-index-dropdown)}.menu__caret,.menu__link,.menu__list-item-collapsible{border-radius:.25rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.dropdown__link{color:var(--ifm-dropdown-link-color);display:block;margin-top:.2rem;white-space:nowrap}.dropdown__link--active,.dropdown__link:hover{background-color:var(--ifm-dropdown-hover-background-color);color:var(--ifm-dropdown-link-color);text-decoration:none}.dropdown__link--active,.dropdown__link--active:hover{--ifm-dropdown-link-color:var(--ifm-link-color)}.dropdown>.navbar__link:after{border-width:.4em .4em 0;content:"";margin-left:.3em;position:relative;top:2px;transform:translateY(-50%)}.footer{background-color:var(--ifm-footer-background-color);color:var(--ifm-footer-color);padding:var(--ifm-footer-padding-vertical) var(--ifm-footer-padding-horizontal)}.footer--dark{--ifm-footer-background-color:#303846;--ifm-footer-color:var(--ifm-footer-link-color);--ifm-footer-link-color:var(--ifm-color-secondary);--ifm-footer-title-color:var(--ifm-color-white);--ifm-footer-background-color:#2b3137}.footer__link-item{color:var(--ifm-footer-link-color);line-height:2}.footer__link-item:hover{color:var(--ifm-footer-link-hover-color)}.footer__link-separator{margin:0 var(--ifm-footer-link-horizontal-spacing)}.footer__logo{margin-top:1rem;max-width:var(--ifm-footer-logo-max-width)}.footer__title{color:var(--ifm-footer-title-color);font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base);margin-bottom:var(--ifm-heading-margin-bottom)}.apiItemContainer article>:first-child,.apiItemContainer header+*,.docItemContainer article>:first-child,.docItemContainer header+*,.docItemContainer_Djhp article>:first-child,.docItemContainer_Djhp header+*,.docItemContainer_oq1c article>:first-child,.docItemContainer_oq1c header+*,.footer__item,.formItem_WgRa:first-child,.showMoreButton_ZGo2:first-child,.showMoreButton_jDKX:first-child{margin-top:0}.admonitionContent_S0QG>:last-child,.cardContainer_fWXF :last-child,.collapsibleContent_i85q>:last-child,.featCard_EPO6 p,.footer__items,.tabItem_Ymn6>:last-child,.theme-api-markdown details p{margin-bottom:0}.codeBlockStandalone_MEMb,[type=checkbox]{padding:0}.hero{align-items:center;background-color:var(--ifm-hero-background-color);color:var(--ifm-hero-text-color);display:flex;padding:4rem 2rem}.hero--primary{--ifm-hero-background-color:var(--ifm-color-primary);--ifm-hero-text-color:var(--ifm-font-color-base-inverse)}.hero--dark{--ifm-hero-background-color:#303846;--ifm-hero-text-color:var(--ifm-color-white)}.hero__title,.title_f1Hy{font-size:3rem}.hero__subtitle{font-size:1.5rem}.menu__list{list-style:none;margin:0;padding-left:0}.menu__caret,.menu__link{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu__list .menu__list{flex:0 0 100%;margin-top:.25rem;padding-left:var(--ifm-menu-link-padding-horizontal)}.menu__list-item:not(:first-child){margin-top:.25rem}.menu__list-item--collapsed .menu__list{height:0;overflow:hidden}.details_lb9f[data-collapsed=false].isBrowser_bmU9>summary:before,.details_lb9f[open]:not(.isBrowser_bmU9)>summary:before,.menu__list-item--collapsed .menu__caret:before,.menu__list-item--collapsed .menu__link--sublist:after,.tabArrowRight_X4Xu,.tabArrowRight_oN04,.tabArrowRight_qsDv,.tabArrowRight_x8Ix{transform:rotate(90deg)}.menu__list-item-collapsible{display:flex;flex-wrap:wrap;position:relative}.menu__caret:hover,.menu__link:hover,.menu__list-item-collapsible--active,.menu__list-item-collapsible:hover{background:var(--ifm-menu-color-background-hover)}.menu__list-item-collapsible .menu__link--active,.menu__list-item-collapsible .menu__link:hover{background:none!important}.menu__caret,.menu__link{align-items:center;display:flex}.navbar-sidebar,.navbar-sidebar__backdrop{opacity:0;top:0;transition-timing-function:ease-in-out;left:0;bottom:0;visibility:hidden}.menu__link{color:var(--ifm-menu-color);flex:1;line-height:1.25}.menu__link:hover{color:var(--ifm-menu-color);text-decoration:none}.menu__caret:before,.menu__link--sublist-caret:after{height:1.25rem;transition:transform var(--ifm-transition-fast) linear;width:1.25rem;transform:rotate(180deg);content:"";filter:var(--ifm-menu-link-sublist-icon-filter)}.menu__link--sublist-caret:after{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;margin-left:auto;min-width:1.25rem}.menu__link--active,.menu__link--active:hover{color:var(--ifm-menu-color-active)}.navbar__brand,.navbar__link{color:var(--ifm-navbar-link-color)}.menu__link--active:not(.menu__link--sublist),.paramsItem:focus,.paramsItem:hover,.paramsItem_PKlE:focus,.paramsItem_PKlE:hover,.schemaItem:focus,.schemaItem:hover,.schemaItem_P8yX:focus,.schemaItem_P8yX:hover{background-color:var(--ifm-menu-color-background-active)}.menu__caret:before{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem}.navbar--dark,html[data-theme=dark]{--ifm-menu-link-sublist-icon-filter:invert(100%) sepia(94%) saturate(17%) hue-rotate(223deg) brightness(104%) contrast(98%)}.navbar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-navbar-shadow);height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}.docsWrapper_BCFX,.navbar,.navbar>.container,.navbar>.container-fluid{display:flex}.navbar--fixed-top{position:-webkit-sticky;position:sticky;top:0;z-index:var(--ifm-z-index-fixed)}.bgImg,.page-home{z-index:-1}.navbar__inner{display:flex;flex-wrap:wrap;justify-content:space-between;width:100%}.navbar__brand{align-items:center;display:flex;margin-right:1rem;min-width:0}.navbar__brand:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.announcementBarContent_xLdY,.navbar__title{flex:1 1 auto}.navbar__toggle{display:none;margin-right:.5rem}.navbar__logo{flex:0 0 auto;height:2rem;margin-right:.5rem}.docs-wrapper,.navbar__logo img,body,html{height:100%}.navbar__items{align-items:center;display:flex;flex:1;min-width:0}.navbar__items--center{flex:0 0 auto}.heroIntegrations_GxsS p,.listFeats_Pnmk p,.navbar__items--center .navbar__brand{margin:0}.navbar__items--center+.navbar__items--right{flex:1}.navbar__items--right{flex:0 0 auto;justify-content:flex-end}.navbar__items--right>:last-child{padding-right:0}.navbar__item{display:inline-block;padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.navbar__link{font-weight:var(--ifm-font-weight-semibold)}.navbar__link--active,.navbar__link:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.navbar--dark,.navbar--primary{--ifm-menu-color:var(--ifm-color-gray-300);--ifm-navbar-link-color:var(--ifm-color-gray-100);--ifm-navbar-search-input-background-color:hsla(0,0%,100%,.1);--ifm-navbar-search-input-placeholder-color:hsla(0,0%,100%,.5);color:var(--ifm-color-white)}.navbar--dark{--ifm-navbar-background-color:#242526;--ifm-menu-color-background-active:hsla(0,0%,100%,.05);--ifm-navbar-search-input-color:var(--ifm-color-white)}.navbar--primary{--ifm-navbar-background-color:var(--ifm-color-primary);--ifm-navbar-link-hover-color:var(--ifm-color-white);--ifm-menu-color-active:var(--ifm-color-white);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-500)}.navbar__search-input{-webkit-appearance:none;appearance:none;background:var(--ifm-navbar-search-input-background-color) var(--ifm-navbar-search-input-icon) no-repeat .75rem center/1rem 1rem;border:none;border-radius:2rem;color:var(--ifm-navbar-search-input-color);cursor:text;display:inline-block;font-size:.9rem;height:2rem;padding:0 .5rem 0 2.25rem;width:12.5rem}.navbar__search-input::placeholder{color:var(--ifm-navbar-search-input-placeholder-color)}.navbar-sidebar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-global-shadow-md);overflow-x:hidden;position:fixed;transform:translate3d(-100%,0,0);transition-duration:.25s;transition-property:opacity,visibility,transform;width:var(--ifm-navbar-sidebar-width)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar__items{transform:translateZ(0)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar--show .navbar-sidebar__backdrop{opacity:1;visibility:visible}.navbar-sidebar__backdrop{background-color:rgba(0,0,0,.6);position:fixed;right:0;transition-duration:.1s;transition-property:opacity,visibility}.navbar-sidebar__brand{align-items:center;box-shadow:var(--ifm-navbar-shadow);display:flex;flex:1;height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar-sidebar__items{display:flex;height:calc(100% - var(--ifm-navbar-height));transition:transform var(--ifm-transition-fast) ease-in-out}.navbar-sidebar__items--show-secondary{transform:translate3d(calc((var(--ifm-navbar-sidebar-width))*-1),0,0)}.navbar-sidebar__item{flex-shrink:0;padding:.5rem;width:calc(var(--ifm-navbar-sidebar-width))}.navbar-sidebar__back{background:var(--ifm-menu-color-background-active);font-size:15px;font-weight:var(--ifm-button-font-weight);margin:0 0 .2rem -.5rem;padding:.6rem 1.5rem;position:relative;text-align:left;top:-.5rem;width:calc(100% + 1rem)}.navbar-sidebar__close{display:flex;margin-left:auto}.pagination{column-gap:var(--ifm-pagination-page-spacing);display:flex;font-size:var(--ifm-pagination-font-size);padding-left:0}.pagination--sm{--ifm-pagination-font-size:0.8rem;--ifm-pagination-padding-horizontal:0.8rem;--ifm-pagination-padding-vertical:0.2rem}.pagination--lg{--ifm-pagination-font-size:1.2rem;--ifm-pagination-padding-horizontal:1.2rem;--ifm-pagination-padding-vertical:0.3rem}.pagination__item{display:inline-flex}.pagination__item>span{padding:var(--ifm-pagination-padding-vertical)}.pagination__item--active .pagination__link{color:var(--ifm-pagination-color-active)}.pagination__item--active .pagination__link,.pagination__item:not(.pagination__item--active):hover .pagination__link{background:var(--ifm-pagination-item-active-background)}.pagination__item--disabled,.pagination__item[disabled]{opacity:.25;pointer-events:none}.pagination__link{border-radius:var(--ifm-pagination-border-radius);color:var(--ifm-font-color-base);display:inline-block;padding:var(--ifm-pagination-padding-vertical) var(--ifm-pagination-padding-horizontal);transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination__link:hover,.sidebarItemLink_mo7H:hover{text-decoration:none}.pagination-nav{grid-gap:var(--ifm-spacing-horizontal);display:grid;gap:var(--ifm-spacing-horizontal);grid-template-columns:repeat(2,1fr)}.pagination-nav__link{border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-pagination-nav-border-radius);display:block;height:100%;line-height:var(--ifm-heading-line-height);padding:var(--ifm-global-spacing);transition:border-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination-nav__link:hover{border-color:var(--ifm-pagination-nav-color-hover);text-decoration:none}.pagination-nav__link--next{grid-column:2/3;text-align:right}.aportal_VoVb,.bodyImg_PAPi,.bodyImg_ZN7y,.bodyImg_kD9N,.contentBlockCta,.contentBlockHead,.content_knG7,.featCard_EPO6,.featInner_FP2u,.featureImg_KAsM,.footer,.heroImage_ba0c,.keycloak_K4jr,.pageHero,.pageHeroMsg,.pills--block .pills__item,.planFoot_Kh7l,.planHead_C2Ld,.planSupport_QMOZ,.sectHead_KgE8,.text-center{text-align:center}.pagination-nav__label{font-size:var(--ifm-h4-font-size);font-weight:var(--ifm-heading-font-weight);word-break:break-word}.pagination-nav__link--prev .pagination-nav__label:before{content:"« "}.pagination-nav__link--next .pagination-nav__label:after{content:" »"}.pagination-nav__sublabel{color:var(--ifm-color-content-secondary);font-size:var(--ifm-h5-font-size);font-weight:var(--ifm-font-weight-semibold);margin-bottom:.25rem}.pills__item,.tabs{font-weight:var(--ifm-font-weight-bold)}.pills{display:flex;gap:var(--ifm-pills-spacing);padding-left:0}.pills__item{border-radius:.5rem;cursor:pointer;display:inline-block;padding:.25rem 1rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pills__item--active{color:var(--ifm-pills-color-active)}.pills__item--active,.pills__item:not(.pills__item--active):hover{background:var(--ifm-pills-color-background-active)}.pills--block{justify-content:stretch}.pills--block .pills__item{flex-grow:1}.tabs{color:var(--ifm-tabs-color);display:flex;margin-bottom:0;overflow-x:auto;padding-left:0}.tabs__item{border-bottom:3px solid transparent;border-radius:var(--ifm-global-radius);cursor:pointer;display:inline-flex;padding:var(--ifm-tabs-padding-vertical) var(--ifm-tabs-padding-horizontal);transition:background-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs__item--active{border-bottom-color:var(--ifm-tabs-color-active-border);border-bottom-left-radius:0;border-bottom-right-radius:0;color:var(--ifm-tabs-color-active)}.btnCta,.btnPrimary{background-color:var(--ifm-color-primary);border:none}.tabs__item:hover{background-color:var(--ifm-hover-overlay)}.tabs--block{justify-content:stretch}.tabs--block .tabs__item{flex-grow:1;justify-content:center}html[data-theme=dark]{--ifm-color-scheme:dark;--ifm-color-emphasis-0:var(--ifm-color-gray-1000);--ifm-color-emphasis-100:var(--ifm-color-gray-900);--ifm-color-emphasis-200:var(--ifm-color-gray-800);--ifm-color-emphasis-300:var(--ifm-color-gray-700);--ifm-color-emphasis-400:var(--ifm-color-gray-600);--ifm-color-emphasis-600:var(--ifm-color-gray-400);--ifm-color-emphasis-700:var(--ifm-color-gray-300);--ifm-color-emphasis-800:var(--ifm-color-gray-200);--ifm-color-emphasis-900:var(--ifm-color-gray-100);--ifm-color-emphasis-1000:var(--ifm-color-gray-0);--ifm-background-color:#1b1b1d;--ifm-background-surface-color:#242526;--ifm-hover-overlay:hsla(0,0%,100%,.05);--ifm-color-content:#e3e3e3;--ifm-color-content-secondary:#fff;--ifm-breadcrumb-separator-filter:invert(64%) sepia(11%) saturate(0%) hue-rotate(149deg) brightness(99%) contrast(95%);--ifm-code-background:hsla(0,0%,100%,.1);--ifm-scrollbar-track-background-color:#444;--ifm-scrollbar-thumb-background-color:#686868;--ifm-scrollbar-thumb-hover-background-color:#7a7a7a;--ifm-table-stripe-background:hsla(0,0%,100%,.07);--ifm-toc-border-color:var(--ifm-color-emphasis-200);--ifm-color-primary-contrast-background:#102445;--ifm-color-primary-contrast-foreground:#ebf2fc;--ifm-color-secondary-contrast-background:#474748;--ifm-color-secondary-contrast-foreground:#fdfdfe;--ifm-color-success-contrast-background:#003100;--ifm-color-success-contrast-foreground:#e6f6e6;--ifm-color-info-contrast-background:#193c47;--ifm-color-info-contrast-foreground:#eef9fd;--ifm-color-warning-contrast-background:#4d3800;--ifm-color-warning-contrast-foreground:#fff8e6;--ifm-color-danger-contrast-background:#4b1113;--ifm-color-danger-contrast-foreground:#ffebec;--docsearch-text-color:#f5f6f7;--docsearch-container-background:rgba(9,10,17,.8);--docsearch-modal-background:#15172a;--docsearch-modal-shadow:inset 1px 1px 0 0 #2c2e40,0 3px 8px 0 #000309;--docsearch-searchbox-background:#090a11;--docsearch-searchbox-focus-background:#000;--docsearch-hit-color:#bec3c9;--docsearch-hit-shadow:none;--docsearch-hit-background:#090a11;--docsearch-key-gradient:linear-gradient(-26.5deg,#565872,#31355b);--docsearch-key-shadow:inset 0 -2px 0 0 #282d55,inset 0 0 1px 1px #51577d,0 2px 2px 0 rgba(3,4,9,.3);--docsearch-footer-background:#1e2136;--docsearch-footer-shadow:inset 0 1px 0 0 rgba(73,76,106,.5),0 -4px 8px 0 rgba(0,0,0,.2);--docsearch-logo-color:#fff;--docsearch-muted-color:#7f8497}:root{--docusaurus-progress-bar-color:var(--ifm-color-primary);--ifm-color-primary:#3fa1e3;--ifm-color-primary-dark:#2695df;--ifm-color-primary-darker:#1f8dd7;--ifm-color-primary-darkest:#1a74b1;--ifm-color-primary-light:#58ade7;--ifm-color-primary-lighter:#65b4e9;--ifm-color-primary-lightest:#ebf5fc;--ifm-navbar-height:80px;--ifm-code-font-size:95%;--ifm-heading-color:#161b1d;--ifm-navbar-background-color:hsla(0,0%,100%,.9);--ifm-navbar-link-color:#3c474e;--main:#2c353a;--success:#18cc6d;--form-outline:#e1e6ea;--form-outline-focus:#bcc5cb;--inner-width:1400px;--inner-width-narrow:1040px;--ifm-container-width:1400px;--openapi-code-dim-light:#aaaaa1!important;--color-wh:#fff;--color-bg:#f4f4fc;--color-main:#0d0f19;--color-n1:#c2cad0;--color-n5:#3c474e;--color-planbadge:#f36529;--soft-shadow-branded:0px 2px 12px rgba(248,204,241,.55);--soft-shadow-branded-strong:0px 5px 12px rgba(248,204,241,.85);--breakpoint-mid:800px;--breakpoint-lar:1200px}.btnCta,.btnPrimary{color:var(--color-wh)}#nprogress .bar{background:var(--docusaurus-progress-bar-color);height:2px;left:0;position:fixed;top:0;width:100%;z-index:1031}#nprogress .peg,.bgImg,.page-home{position:absolute}#nprogress .peg{box-shadow:0 0 10px var(--docusaurus-progress-bar-color),0 0 5px var(--docusaurus-progress-bar-color);height:100%;opacity:1;right:0;transform:rotate(3deg) translateY(-4px);width:100px}.page-bg{background-position:top;background-repeat:no-repeat;background-size:100% auto}.page-home{height:auto;left:0;top:0;width:100vw}.page-identity{background-image:url(/assets/images/gradient-short-bg-525ffc124d4fcefe4f556cfcf2d5dc55.png)}.btnCta{border-radius:8px;box-shadow:0 16px 30px rgba(0,0,0,.2);display:inline-block;font-size:16px;font-weight:550;padding:18px 24px;transition:.2s}.btnCta:hover,.btnPrimary:hover{background-color:var(--ifm-color-primary-light);color:var(--color-wh);cursor:pointer}.btnSecondary:hover,.navbar__items--right [buttontype=btnSecondary]:hover{box-shadow:inset 0 0 0 2px var(--ifm-color-primary);color:var(--ifm-color-primary-light);cursor:pointer}.btnPrimary{border-radius:8px;display:inline-block;font-size:13px;font-weight:700;padding:12px 16px;transition:.2s}.btnSecondary,.navbar__items--right [buttontype=btnSecondary]{border:none;background-color:var(--color-wh);display:inline-block;font-size:13px;font-weight:700;padding:12px 16px;transition:.2s}.btnSecondary{border-radius:8px;box-shadow:inset 0 0 0 1px var(--ifm-color-primary);color:var(--ifm-color-primary)}.btnSecondary:hover{background-color:var(--color-wh)}.btnReadMore{color:var(--ifm-color-primary);column-gap:12px;display:inline-flex;line-height:1.2em;margin:0}.btnReadMore img{transition:.25s ease-out}.btnReadMore:hover img{transform:translateX(4px)}.codeBox{background-color:var(--color-main);border-radius:16px;color:var(--color-wh);min-height:390px;padding:20px}.bgImg img,.pageHeroBgCircles img{height:auto;width:100%}.entSocialLoginBgCircles,.entSocialLoginBgImg,.keycloakBgCircles,.socialLoginBgCircles,.socialLoginBgImg{left:50%;max-width:1120px;top:-280px;transform:translateX(-50%);width:100%}.socialLoginBgCircles{top:-100px}.socialLoginBgImg{max-width:1164px;top:-220px}.entSocialLoginBgCircles{top:-310px;width:280vw;z-index:0}.entSocialLoginBgImg{top:92px;width:170vw}.enterpriseSsoBgImg{width:120vw}.plansBgImg{left:-100vw;top:-100px;width:400vw}.addToAppBgImg,.integrationsBgImg,.kubernetesBgImg,.mfaBgImg{left:-70vw;top:-320px;width:240vw}.mfaBgImg{top:-320px}.addToAppBgImg{top:-840px}.kubernetesBgImg{top:-560px}.ssoBgImb{left:0;top:-80px;width:400vw}.docusaurus-highlight-code-line{background-color:#484d5b;display:block;margin:0 calc(var(--ifm-pre-padding)*-1);padding:0 var(--ifm-pre-padding)}.footer,.pageHero,.theme-api-markdown details{background-color:transparent}.navbar .navbar__inner{margin:0 auto;position:relative;width:var(--inner-width)}.navbar .navbar__logo{margin-right:0}.navbar__items--right{column-gap:8px;display:none}.navbar__items--right [buttontype=btnSecondary]{border-radius:8px;box-shadow:inset 0 0 0 1px var(--ifm-color-primary);color:var(--ifm-color-primary)}.navbar__items--right [buttontype=btnSecondary]:hover{background-color:var(--color-wh)}.navbar__items--right [buttontype=btnPrimary]{background-color:var(--ifm-color-primary);border:none;border-radius:8px;color:var(--color-wh);display:inline-block;font-size:13px;font-weight:700;padding:12px 16px;transition:.2s}.navbar__items--right [buttontype=btnPrimary]:hover{background-color:var(--ifm-color-primary-light);box-shadow:none;color:var(--color-wh);cursor:pointer}.dropdown__menu{border-radius:16px;padding:8px}.dropdown__link{border-radius:8px;font-size:1rem;line-height:1.2em;padding:12px 16px}.dropdown__link:hover{background-color:var(--color-bg)}.dropdown>.navbar__link:after{background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNiIgaGVpZ2h0PSI0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0uNS43NSAzIDMuMjUgNS41Ljc1IiBzdHJva2U9IiMxRjJDMzQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg==") 50% no-repeat;border:none;height:8px;width:10px}.pageHero{padding:40px 20px 20px;position:relative;z-index:3}.pageHeroMsg{margin:0 auto;max-width:782px}.pageHeroMsg h1{font-size:2.6rem;line-height:1.2em;margin-bottom:16px;margin-top:0}.pageHeroMsgIntro{font-size:1.125rem;line-height:1.4em}.pageHeroImg{margin-bottom:12px;width:100%}.pageHeroBgCircles{left:50%;position:absolute;top:-360px;transform:translateX(-50%);width:1120px;z-index:-1}.contentBlock{margin:80px auto;position:relative}.contentBlockHead{margin:0 auto;max-width:560px;padding:0 20px 48px}.contentBlockHead h2{font-size:1.75rem;line-height:1.2em;margin:0}.contentBlockHead p{font-size:1.125rem;line-height:1.4em;margin:0}.contentBlockHead h2+p{margin-top:24px}.contentBlockCta{padding-top:48px}.contentBlockBody{margin:0 auto;max-width:1120px;padding:0 20px}.readMore{display:flex;flex-direction:column;row-gap:24px}.readMore,.readMore:hover{color:var(--color-n5)}.readMore h3{font-size:1.375rem;line-height:1.2em;margin-bottom:24px}.devsL_xtfI,.devsR_oMCi,.readMoreL,.readMoreR,.responseSamplesTabItem_f_p_{width:100%}.readMoreImg{border-radius:16px}.footer{color:rgba(60,71,78,.5);font-size:.8rem}.footer .footer__title,[data-theme=dark] .planInner_G5WZ h5,[data-theme=dark] .planInner_G5WZ p{color:var(--main)}.container_mt6G,.footer .footer__link-item,.sidebarItemList_Yudw{font-size:.9rem}.footer__links{margin-bottom:100px}.footer__bottom{margin-bottom:30px}[data-theme=dark] .footer,[data-theme=dark] .footer .footer__title,[data-theme=dark] .footer .text--center{color:#999}.markdown img{box-shadow:0 0 2.5px;height:auto}.menu{font-size:95%;font-weight:var(--ifm-font-weight-normal);overflow-x:hidden}a.menu__link.menu__link--sublist{font-weight:var(--ifm-font-weight-bold);text-align:center}.menu__link--sublist:after{background:var(--ifm-menu-link-sublist-icon) 50%/1.2rem 1.2rem;margin-left:8px;transform:rotate(0)}.menu__list-item--collapsed .menu__link--sublist:after{transform:rotate(180deg)}.menu__list .menu__list{padding-bottom:24px;padding-left:0}:root{--openapi-required:var(--ifm-color-danger);--openapi-deprecated:var(--ifm-color-warning);--openapi-nullable:var(--ifm-color-info);--openapi-code-blue:var(--ifm-color-info);--openapi-code-red:var(--ifm-color-danger);--openapi-code-orange:var(--ifm-color-warning);--openapi-code-green:var(--ifm-color-success);--openapi-card-background-color:var(--ifm-color-gray-100);--openapi-card-border-radius:var(--ifm-pre-border-radius);--openapi-input-border:var(--ifm-color-primary);--openapi-input-background:var(--openapi-card-background-color);--bash-background-color:transparent;--bash-border-radius:none;--code-tab-logo-width:26px;--code-tab-logo-height:26px;--docusaurus-announcement-bar-height:auto;--docusaurus-collapse-button-bg:transparent;--docusaurus-collapse-button-bg-hover:rgba(0,0,0,.1);--doc-sidebar-width:300px;--doc-sidebar-hidden-width:30px;--docsearch-primary-color:#5468ff;--docsearch-text-color:#1c1e21;--docsearch-spacing:12px;--docsearch-icon-stroke-width:1.4;--docsearch-highlight-color:var(--docsearch-primary-color);--docsearch-muted-color:#969faf;--docsearch-container-background:rgba(101,108,133,.8);--docsearch-logo-color:#5468ff;--docsearch-modal-width:560px;--docsearch-modal-height:600px;--docsearch-modal-background:#f5f6f7;--docsearch-modal-shadow:inset 1px 1px 0 0 hsla(0,0%,100%,.5),0 3px 8px 0 #555a64;--docsearch-searchbox-height:56px;--docsearch-searchbox-background:#ebedf0;--docsearch-searchbox-focus-background:#fff;--docsearch-searchbox-shadow:inset 0 0 0 2px var(--docsearch-primary-color);--docsearch-hit-height:56px;--docsearch-hit-color:#444950;--docsearch-hit-active-color:#fff;--docsearch-hit-background:#fff;--docsearch-hit-shadow:0 1px 3px 0 #d4d9e1;--docsearch-key-gradient:linear-gradient(-225deg,#d5dbe4,#f8f8f8);--docsearch-key-shadow:inset 0 -2px 0 0 #cdcde6,inset 0 0 1px 1px #fff,0 1px 2px 1px rgba(30,35,90,.4);--docsearch-footer-height:44px;--docsearch-footer-background:#fff;--docsearch-footer-shadow:0 -1px 0 0 #e0e3e8,0 -3px 6px 0 rgba(69,98,155,.12);--docsearch-primary-color:var(--ifm-color-primary);--docsearch-text-color:var(--ifm-font-color-base);--docusaurus-tag-list-border:var(--ifm-color-emphasis-300)}[data-theme=dark]{--bash-background-color:#d3d3d3;--bash-border-radius:20px;--openapi-card-background-color:var(--ifm-color-gray-900)!important}.theme-api-markdown div[class^=collapsibleContent]{margin-top:0!important;padding-left:2px}.theme-api-markdown details{--docusaurus-details-decoration-color:var(--ifm-font-color-base);border:unset!important;box-shadow:unset!important;color:var(--ifm-font-color-base);margin:unset;max-width:600px;padding:unset}.theme-api-markdown details ul{font-size:14px;padding-left:0}.theme-api-markdown details li{list-style:none;padding-top:5px}.theme-api-markdown .tabs__item{padding-bottom:unset;padding-top:unset}.theme-api-markdown details>div>div{border-top:unset!important;padding-top:unset!important}.theme-api-markdown .details__demo-panel{background:var(--openapi-card-background-color);border-radius:var(--openapi-card-border-radius);margin-bottom:1rem}.theme-api-markdown .details__demo-panel>summary{cursor:pointer;padding-left:1rem;padding-top:1rem}.codeBlockTitle_Ktv7+.codeBlockContent_biex .codeBlock_bY9V,.theme-api-markdown .details__demo-panel>div>div>pre{border-top-left-radius:0;border-top-right-radius:0}.theme-api-markdown .details__demo-panel>summary::marker{content:"";display:none}.theme-api-markdown .details__demo-panel>summary::-webkit-details-marker{content:"";display:none}.theme-api-markdown .details__demo-panel>pre{margin-bottom:0;padding-top:0}.theme-api-markdown .details__request-summary>button,.theme-api-markdown .details__response-summary>button{margin-bottom:1rem;margin-right:1rem}.mimeTabsTopSection_wt53,.theme-api-markdown .details__request-summary,.theme-api-markdown .details__response-summary{align-items:center;display:flex;justify-content:space-between}.version-button div{display:block}.version-button div>button>span:after{border-color:currentcolor transparent;border-style:solid;border-width:.4em .4em 0;content:"";display:inline-block;font-size:.8rem;margin-left:.3em;position:relative;top:1px;transform:translateY(-50%)}[class^=paramsItem]:before,[class^=schemaItem]:before{border-bottom:thin solid var(--ifm-color-gray-500);content:"";display:inline-block;height:.5rem;left:0;position:absolute;top:10px;vertical-align:top;width:.7rem}.code__tab--bash:after,.code__tab--csharp:after,.code__tab--go:after,.code__tab--java:after,.code__tab--javascript:after,.code__tab--nodejs:after,.code__tab--php:after,.code__tab--powershell:after,.code__tab--python:after,.code__tab--ruby:after{height:var(--code-tab-logo-height);margin-block:auto;width:var(--code-tab-logo-width);content:""}.schemaItem{padding:5px 0 5px 1rem}.discriminatorItem,.schemaItem{border-left:thin solid var(--ifm-color-gray-500)!important;list-style:none;margin:0!important;position:relative}.discriminatorItem{padding:5px 0!important}.code__tab--go,.code__tab--python{padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--python:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/python/python-original.svg)}.code__tab--python{color:var(--ifm-color-success)}.code__tab--python.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-success)}.language-bash,.language-csharp,.language-go,.language-java,.language-javascript,.language-nodejs,.language-php,.language-powershell,.language-python,.language-ruby,.openapi-demo__code-block code{max-height:500px;overflow:auto}.code__tab--go:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/go/go-original-wordmark.svg)}.code__tab--go{color:var(--ifm-color-info)}.code__tab--go.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-info)}.code__tab--javascript:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/javascript/javascript-original.svg)}.code__tab--javascript{color:var(--ifm-color-warning);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--bash,.code__tab--ruby{color:var(--ifm-color-danger);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--javascript.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-warning)}.code__tab--bash:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/bash/bash-plain.svg);background-color:var(--bash-background-color);border-radius:var(--bash-border-radius)}.code__tab--bash.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-danger)}.code__tab--ruby:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/ruby/ruby-plain.svg)}.code__tab--ruby.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-danger)}.code__tab--csharp:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/csharp/csharp-original.svg)}.code__tab--csharp{color:var(--ifm-color-gray-500);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--csharp.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-gray-500)}.code__tab--nodejs:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/nodejs/nodejs-original.svg)}.code__tab--nodejs{color:var(--ifm-color-success);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--nodejs.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-success)}.code__tab--php:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/php/php-original.svg)}.code__tab--php{color:var(--ifm-color-gray-500);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--php.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-gray-500)}.code__tab--java:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/java/java-original.svg)}.code__tab--java{color:var(--ifm-color-warning);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--java.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-warning)}.code__tab--powershell:after{background:url(https://raw.githubusercontent.com/devicons/devicon/master/icons/windows8/windows8-original.svg)}.code__tab--powershell{color:var(--ifm-color-info);padding-bottom:1rem!important;padding-left:1.4rem;padding-right:1.4rem;padding-top:1rem!important}.code__tab--powershell.tabs__item--active{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-color-info)}.prism-code.language-java{white-space:pre!important}.openapi__logo{width:250px}div:has(>ul.openapi-tabs__security-schemes){max-width:100%}.carousel-root,body:not(.navigation-with-keyboard) :not(input):focus{outline:0}#docusaurus-base-url-issue-banner-container,.themedImage_ToTc,[data-theme=dark] .lightToggleIcon_pyhR,[data-theme=light] .darkToggleIcon_wfgR,html[data-announcement-bar-initially-dismissed=true] .announcementBar_mb4j{display:none}.skipToContent_fXgn{background-color:var(--ifm-background-surface-color);color:var(--ifm-color-emphasis-900);left:100%;padding:calc(var(--ifm-global-spacing)/2) var(--ifm-global-spacing);position:fixed;top:1rem;z-index:calc(var(--ifm-z-index-fixed) + 1)}.skipToContent_fXgn:focus{box-shadow:var(--ifm-global-shadow-md);left:1rem}.closeButton_CVFx{line-height:0;padding:0}.content_knG7{font-size:85%;padding:5px 0}.content_knG7 a{color:inherit;text-decoration:underline}.DocSearch-Container a,.btnVideo_gcnS:hover{text-decoration:none}.announcementBar_mb4j{align-items:center;background-color:var(--ifm-color-white);border-bottom:1px solid var(--ifm-color-emphasis-100);color:var(--ifm-color-black);display:flex;height:var(--docusaurus-announcement-bar-height)}.announcementBarPlaceholder_vyr4{flex:0 0 10px}.announcementBarClose_gvF7{align-self:stretch;flex:0 0 30px}.toggle_vylO{height:2rem;width:2rem}.toggleButton_gllP{align-items:center;border-radius:50%;display:flex;height:100%;justify-content:center;transition:background var(--ifm-transition-fast);width:100%}.toggleButton_gllP:hover{background:var(--ifm-color-emphasis-200)}.toggleButtonDisabled_aARS{cursor:not-allowed}[data-theme=dark] .themedImage--dark_i4oU,[data-theme=light] .themedImage--light_HNdA{display:initial}.iconExternalLink_nPIU{margin-left:.3rem}.iconLanguage_nlXk{margin-right:5px;vertical-align:text-bottom}.navbarHideable_m1mJ{transition:transform var(--ifm-transition-fast) ease}.navbarHidden_jGov{transform:translate3d(0,calc(-100% - 2px),0)}.footerLogoLink_BH7S{opacity:.5;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.carousel .control-arrow:focus,.carousel .control-arrow:hover,.carousel .control-dots .dot.selected,.carousel .control-dots .dot:hover,.carousel:hover .slide .legend,.footerLogoLink_BH7S:hover,.hash-link:focus,.tabItem_fIhq:hover,:hover>.hash-link{opacity:1}.mainWrapper_z2l0{flex:1 0 auto}.docusaurus-mt-lg{margin-top:3rem}#__docusaurus{overflow:hidden;display:flex;flex-direction:column;min-height:100%}.sidebar_re4s{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:-webkit-sticky;position:sticky;top:calc(var(--ifm-navbar-height) + 2rem)}.sidebarItemTitle_pO2u{font-size:var(--ifm-h3-font-size);font-weight:var(--ifm-font-weight-bold)}.tabItem_AM8E,.tabItem_fIhq{font-weight:var(--ifm-font-weight-normal)}.sidebarItem__DBe{margin-top:.7rem}.sidebarItemLink_mo7H{color:var(--ifm-font-color-base);display:block}.sidebarItemLinkActive_I1ZP{color:var(--ifm-color-primary)!important}.tabItem_fIhq{align-items:center;border:1px solid var(--ifm-color-primary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-primary);display:flex;height:2.5rem;justify-content:center;margin-right:.5rem;margin-top:0!important}.tabItem_VIbn:not(.discriminatorTabActive_wwM1),.tabItem_es3Q:not(.schemaTabActive_dWHR),.tabItem_fIhq:not(.active_3R0z){opacity:.65}.responseTabsTopSection_jEoq{align-items:center;display:flex;justify-content:space-between;margin-top:2rem}.responseTabsContainer_XEhZ{align-items:center;display:flex;max-width:390px;overflow:hidden;padding-left:1rem}.discriminatorTabsListContainer_Nnai,.responseTabsListContainer_itgM{overflow-x:scroll;overflow-y:hidden;padding:0 .25rem;scroll-behavior:smooth}.responseTabsListContainer_itgM::-webkit-scrollbar{display:none}.mimeTabDot_NO24,.responseTabDot_mwcE{border-radius:50%;height:12.5px;margin-right:5px;width:12.5px}.mimeTabDotSuccess_M1bZ,.responseStatusSuccess_FIkE>.responseTabDot_mwcE{background-color:var(--ifm-color-success)}.mimeTabDotDanger_NBeV,.responseStatusDanger_hE0A>.responseTabDot_mwcE{background-color:var(--ifm-color-danger)}.mimeTabDotInfo_R3vj,.responseStatusInfo_aVn2>.responseTabDot_mwcE{background-color:var(--ifm-color-info)}.active_3R0z,.schemaTabActive_dWHR,.tabItem_AM8E:active,.tabItem_VIbn.discriminatorTabActive_wwM1{background-color:var(--ifm-color-emphasis-100)}.mimeSchemaContainer_yYbZ,.responseSchemaContainer_Sp_v{max-width:600px}.tabArrow_LyoO,.tabArrow_x5p_{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;border:none;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;min-width:1.25rem;width:1.25rem}.tabArrow_LyoO:hover,.tabArrow_WDKX:hover,.tabArrow_x5p_:hover,.tabArrow_zmvw:hover{cursor:pointer}.tabArrowLeft_Cztf,.tabArrowLeft_RQtH,.tabArrowLeft_RzDG,.tabArrowLeft_woLb{transform:rotate(270deg)}.tabItem_AM8E{align-items:center;display:flex;font-size:12px;height:2rem;justify-content:center;margin-right:.5rem;margin-top:0!important;white-space:nowrap}.mimeTabsContainer_gZbZ{align-items:center;display:flex;max-width:390px;overflow:hidden}.mimeTabsListContainer_LPoX,.schemaTabsListContainer_wmy4{overflow-x:scroll;overflow-y:hidden;scroll-behavior:smooth}.mimeTabsListContainer_LPoX::-webkit-scrollbar{display:none}.mimeTabActive_nq7U{background-color:var(--ifm-color-emphasis-100);border-bottom-color:var(--ifm-tabs-color-active-border);border-bottom-left-radius:0;border-bottom-right-radius:0;color:var(--ifm-tabs-color-active)}.cardContainer_fWXF{--ifm-link-color:var(--ifm-color-emphasis-800);--ifm-link-hover-color:var(--ifm-color-emphasis-700);--ifm-link-hover-decoration:none;border:1px solid var(--ifm-color-emphasis-200);box-shadow:0 1.5px 3px 0 rgba(0,0,0,.15);transition:all var(--ifm-transition-fast) ease;transition-property:border,box-shadow}.cardContainer_fWXF:hover{border-color:var(--ifm-color-primary);box-shadow:0 3px 6px 0 rgba(0,0,0,.2)}.cardTitle_rnsV{font-size:1.2rem}.cardDescription_PWke{font-size:.8rem}.searchQueryInput_u2C7,.searchVersionInput_m0Ui{background:var(--docsearch-searchbox-focus-background);border:2px solid var(--ifm-toc-border-color);border-radius:var(--ifm-global-radius);color:var(--docsearch-text-color);font:var(--ifm-font-size-base) var(--ifm-font-family-base);margin-bottom:.5rem;padding:.8rem;transition:border var(--ifm-transition-fast) ease;width:100%}.searchQueryInput_u2C7:focus,.searchVersionInput_m0Ui:focus{border-color:var(--docsearch-primary-color);outline:0}.searchQueryInput_u2C7::placeholder{color:var(--docsearch-muted-color)}.searchResultsColumn_JPFH{font-size:.9rem;font-weight:700}.algoliaLogo_rT1R{max-width:150px}.algoliaLogoPathFill_WdUC{fill:var(--ifm-font-color-base)}.searchResultItem_Tv2o{border-bottom:1px solid var(--ifm-toc-border-color);padding:1rem 0}.searchResultItemHeading_KbCB{font-weight:400;margin-bottom:0}.searchResultItemPath_lhe1{--ifm-breadcrumb-separator-size-multiplier:1;color:var(--ifm-color-content-secondary);font-size:.8rem}.searchResultItemSummary_AEaO{font-style:italic;margin:.5rem 0 0}.loadingSpinner_XVxU{animation:1s linear infinite a;border:.4em solid #eee;border-radius:50%;border-top:.4em solid var(--ifm-color-primary);height:3rem;margin:0 auto;width:3rem}@keyframes a{to{transform:rotate(1turn)}}.loader_vvXV,.responseSamplesContainer_ITGy{margin-top:2rem}.search-result-match{background:rgba(255,215,142,.25);color:var(--docsearch-hit-color);padding:.09em 0}.backToTopButton_sjWU{background-color:var(--ifm-color-emphasis-200);border-radius:50%;bottom:1.3rem;box-shadow:var(--ifm-global-shadow-lw);height:3rem;opacity:0;position:fixed;right:1.3rem;transform:scale(0);transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default);visibility:hidden;width:3rem;z-index:calc(var(--ifm-z-index-fixed) - 1)}.backToTopButton_sjWU:after{background-color:var(--ifm-color-emphasis-1000);content:" ";display:inline-block;height:100%;-webkit-mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;width:100%}.backToTopButtonShow_xfvO{opacity:1;transform:scale(1);visibility:visible}[data-theme=dark]:root{--docusaurus-collapse-button-bg:hsla(0,0%,100%,.05);--docusaurus-collapse-button-bg-hover:hsla(0,0%,100%,.1)}.collapseSidebarButton_PEFL,.docSidebarContainer_b6E3,.heroSectionPlus_e1gH,.sidebarLogo_isFc{display:none}.docMainContainer_gTbr,.docPage__5DB{display:flex;width:100%}.formControl_yBmG{border:1px solid var(--form-outline);border-radius:3px;box-shadow:0 2px 6px var(--form-outline);flex:1 1 auto;font-size:14px;padding:12px 15px;transition:.2s}.heroIntegration_GaJq,.heroSection_dXSz{background-color:var(--color-wh);border-radius:16px}.formControl_yBmG::placeholder{color:var(--form-outline-focus)}.formControl_yBmG:focus{border-color:var(--form-outline-focus);box-shadow:0 1px 3px var(--form-outline);outline:0}.heroImage_ba0c img{height:auto;max-width:950px;width:100%}.heroSections_eYcn{display:flex;flex-direction:column;justify-content:center;margin-top:80px;row-gap:4px}.heroFeats_bnZi,.heroIntegrations_GxsS{margin-top:64px}.heroSection_dXSz{align-items:center;color:var(--color-main);column-gap:16px;display:flex;flex:1;padding:8px 16px;position:relative;row-gap:4px;transition:.25s ease-out}.heroSectionPicto_NANO{height:auto;transition:.25s ease-out;width:32px}.heroSection_dXSz:hover{box-shadow:var(--soft-shadow-branded-strong);color:var(--color-main)}.heroSection_dXSz:hover .heroSectionPicto_NANO{transform:scale(1.1)}.heroSection_dXSz p{font-size:1.1rem;font-weight:700;line-height:1.1em;margin:0}.heroFeats_bnZi{column-gap:20px;display:flex;justify-content:center}.heroFeat_fBWQ{flex:1;max-width:160px}.heroFeat_fBWQ img{margin-bottom:12px}.heroFeat_fBWQ p{line-height:1.3em}.heroIntegrationsLines_bELR{height:auto;margin:8px 0;width:80%}.heroIntegrationRow_vSEr{grid-gap:8px;column-gap:8px;display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(4,1fr)}.heroIntegrationRow_vSEr+.heroIntegrationRow_vSEr{margin-top:8px}.heroIntegration_GaJq{align-items:center;box-shadow:0 2px 6px rgba(248,204,241,.55);display:inline-flex;flex:1;height:64px;justify-content:center;max-width:150px}.featCard_EPO6,.plan_scje{background-color:var(--color-wh);border-radius:16px;box-shadow:var(--soft-shadow-branded)}.heroIntegration_GaJq img,.integration_JE4l img{height:auto;width:80px}.heroIntegrationMore_ABZQ{grid-column:1/4;max-width:inherit}.listFeats_Pnmk{list-style:none;margin:0;padding:0}.listFeats_Pnmk li{padding-left:64px;position:relative}.listFeats_Pnmk li+li{margin-top:32px}.listFeats_Pnmk h5{font-size:1rem;margin-bottom:8px}.listFeatsPicto_LatF{left:0;position:absolute}.featCards_qurD{display:flex;flex-direction:column;row-gap:12px}.featCard_EPO6{flex:1;padding:24px 32px}.blogPostFooterDetailsFull_mRVl,.carousel .slider-wrapper.axis-vertical .slider,.enterpriseSSO_fLpf,.plans_lWeT{flex-direction:column}.featCard_EPO6 h5{font-size:1rem;margin:16px 0}.devs_TdUg,.enterpriseSSO_fLpf{align-items:center;display:flex;row-gap:24px}.devs_TdUg{flex-direction:column-reverse}.aportal_VoVb img{max-width:1120px;width:100%}.keycloak_K4jr{margin-bottom:64px}.heart_Zeus{margin:0 8px}.plans_lWeT{display:flex;row-gap:32px}.plansSects_kiqc{column-gap:160px;display:flex;margin-bottom:24px;padding:0 80px}.plansBlocks_GHdj{display:flex;flex-direction:column;margin:0 auto;max-width:100%;row-gap:32px}.plan_scje{display:flex;flex:1;flex-direction:column;position:relative}.planHead_C2Ld{padding:32px 20px}.planHead_C2Ld h3{font-size:1.4rem;line-height:1.2em;margin:8px 0}.planHead_C2Ld p{line-height:1.1em;margin:0}.planBadge_qnN9{background-color:var(--color-planbadge);border-radius:30px;color:var(--color-wh);display:inline-flex;left:50%;line-height:1em;padding:3px 8px;position:absolute;top:-10px;transform:translateX(-50%);white-space:nowrap}.planFrom_mnEa{color:var(--color-n1);font-size:.86em}.planPrice_u1BA{font-size:1.2em}.planBody_fSqo{flex:1 1 auto;padding:0 20px 20px}.planFoot_Kh7l{column-gap:4px;display:flex;flex-direction:row;padding:0 20px 20px}.planFootA_VTyg{margin:0 auto}.planSupport_QMOZ{margin:auto;max-width:720px}.planSupport_QMOZ .planBody_fSqo{min-height:auto}.planSupport_QMOZ .checklist_YNT1{align-items:center;display:flex;flex-wrap:wrap;justify-content:space-around}.planSupport_QMOZ .checklist_YNT1>li+li{margin-left:1rem;margin-top:0}.buttons_pzbO,.planSupport_QMOZ .planFoot_Kh7l{justify-content:center}.featCardPicto_mQtc{height:50px}.btnVideo_gcnS{align-items:center;display:inline-flex;font-size:14px;transition:.2s}.btnVideo_gcnS img{flex:0 0 auto;margin-right:8px;transition:.25s ease-out}.btnVideo_gcnS:hover{color:var(--main)}.btnVideo_gcnS:hover img{transform:scale(1.15)}.sect_fa0Z{padding:50px 0;position:relative}.sectHead_KgE8{margin:0 auto 40px;max-width:700px}.sectPreHeadline_myin{font-size:12px;letter-spacing:2px;margin-bottom:.6rem}.sectHeadline_XdUn{line-height:1.2em;margin:0 auto 1rem;max-width:300px}.sectHeadline_XdUn img{margin:0 5px;position:relative;top:-4px;vertical-align:middle}.sectHeadline_XdUn .heart_Zeus{height:auto;width:40px}.sectOsArrow_ksPO{display:none;left:55%;position:absolute;top:130px;z-index:-1}.sectOpenSource_IMs3{padding-top:80px}.featblocks_OTQj{margin-left:auto;margin-right:auto;max-width:var(--inner-width-narrow)}.feature_nI3B{margin-bottom:50px}.featureCopy_WeZz>div{margin:0 auto;max-width:280px}.featureCopy_WeZz h3{margin-bottom:25px}.featureCopy_WeZz p{font-size:1rem;line-height:1.5em}.featureImg_KAsM img{max-width:280px}.feat_uwf3{margin:40px 0}.feat_uwf3 img{height:auto;width:200px}.feat_uwf3 h3{font-size:1.5rem;margin:20px 0}.feat_uwf3 p{font-size:1rem;line-height:1.7em}.featInner_FP2u{margin:0 auto;max-width:400px}.badgeSuccess_e6Hh{background-color:rgba(24,204,109,.15);border-radius:3px;color:var(--success);display:inline-block;font-size:14px;line-height:1em;margin-bottom:8px;padding:5px 8px}.tableTheme_A_3f tbody,.tableTheme_A_3f td,.tableTheme_A_3f thead,.tableTheme_A_3f thead th,.tableTheme_A_3f tr{border:none;background:none}.checklist_YNT1{font-size:.875rem;list-style:none;padding-left:0;text-align:left}.checklist_YNT1>li{padding-left:26px;position:relative}.checklist_YNT1>li+li{margin-top:.3rem}.checklistIcon_bRVA{height:auto;left:0;margin-top:8px;position:absolute;width:10px}.access_So_Z{padding:24px;text-align:center}.featureImage_yA8i{margin:0 auto;max-height:128px;max-width:60%}.announcement_FsS0{font-size:24px;font-weight:700;margin:0 auto;padding:48px;text-align:center}.announcementDark_tzC4{background-color:#20232a;color:var(--color-wh)}.announcementInner_RsrQ{margin:0 auto;max-width:768px}.tableThemeWrapper_WRn5{align-items:center;display:flex;justify-content:center}.tableTheme_A_3f{background:none;margin:0 auto 1rem}.tableTheme_A_3f th{font-size:1.2rem;min-width:150px}.tableTheme_A_3f tr td:first-child{font-style:italic;text-align:left}.tableTheme_A_3f td{padding:.5rem 2rem;text-align:center}.notPartOfPlan_xlGG{opacity:.5}.heroBanner_UJJx{overflow:hidden;padding:4rem 0;position:relative;text-align:center}.DocSearch-Button-Container,.buttons_pzbO,.features_keug{align-items:center;display:flex}.features_keug{padding:2rem 0;width:100%}.featureImage_yA8i{height:200px;width:200px}[data-theme=dark] .hero_syme{background-color:transparent;border-bottom:1px solid hsla(0,0%,100%,.1)}[data-theme=dark] .pricing_KaGM{background-color:hsla(0,0%,100%,.1)}[data-theme=dark] h1,[data-theme=dark] h2,[data-theme=dark] h3,[data-theme=dark] h4,[data-theme=dark] h5,[data-theme=dark] li,[data-theme=dark] p,[data-theme=dark] td,[data-theme=dark] th{color:#eee}.authorCol_Hf19{flex-grow:1!important;max-width:inherit!important}.imageOnlyAuthorRow_pa_O{display:flex;flex-flow:row wrap}.imageOnlyAuthorCol_G86a{margin-left:.3rem;margin-right:.3rem}.integrationRow_KYiy{column-gap:8px;display:flex;justify-content:space-between;margin:-100px auto 0 -15%;width:130%}.integrationRow_KYiy+.integrationRow_KYiy{margin:12px auto 0;max-width:96%}.integration_JE4l{align-items:center;background-color:var(--color-wh);border-radius:16px;display:inline-flex;flex:1;height:64px;justify-content:center;max-width:150px}.integrationMore_bWBP{margin-bottom:140px;margin-top:16px;text-align:center}.carousel .control-arrow,.carousel.carousel-slider .control-arrow{background:none;border:0;cursor:pointer;font-size:32px;opacity:.4;position:absolute;top:20px;transition:.25s ease-in;z-index:2}.DocSearch-Button-Key,.bodyImg_d8QI,.carousel,.carousel .carousel,.carousel .slide,.carousel .slider,.carousel .thumbs,.floatingButton_oJlZ{position:relative}.carousel .control-arrow:before,.carousel.carousel-slider .control-arrow:before{border-bottom:8px solid transparent;border-top:8px solid transparent;content:"";display:inline-block;margin:0 5px}.carousel .control-disabled.control-arrow{cursor:inherit;display:none;opacity:0}.carousel .control-prev.control-arrow{left:0}.carousel .control-prev.control-arrow:before{border-right:8px solid #fff}.carousel .control-next.control-arrow{right:0}.carousel .control-next.control-arrow:before{border-left:8px solid #fff}.carousel{width:100%}.carousel img{display:inline-block;width:100%}.carousel .control-arrow{background:none;border:0;font-size:18px;margin-top:-13px;outline:0;top:50%}.carousel .thumbs-wrapper{margin:20px;overflow:hidden}.carousel .thumbs{list-style:none;transform:translateZ(0);transition:.15s ease-in;white-space:nowrap}.carousel .thumb{border:3px solid #fff;display:inline-block;margin-right:6px;overflow:hidden;padding:2px;transition:border .15s ease-in;white-space:nowrap}.carousel .thumb:focus{border:3px solid #ccc;outline:0}.carousel .thumb.selected,.carousel .thumb:hover{border:3px solid #333}.carousel.carousel-slider{margin:0;overflow:hidden;position:relative}.carousel.carousel-slider .control-arrow{bottom:0;color:#fff;font-size:26px;margin-top:0;padding:5px;top:0}.carousel.carousel-slider .control-arrow:hover{background:rgba(0,0,0,.2)}.carousel .slider-wrapper{margin:auto;overflow:hidden;transition:height .15s ease-in;width:100%}.carousel .slider-wrapper.axis-horizontal .slider,.carousel .slider-wrapper.axis-vertical{-ms-box-orient:horizontal;display:-moz-flex;display:flex}.carousel .slider-wrapper.axis-horizontal .slider .slide{flex-direction:column;flex-flow:column}.carousel .slider{list-style:none;margin:0;padding:0;width:100%}.carousel .slider.animated{transition:.35s ease-in-out}.carousel .slide{margin:0;min-width:100%;text-align:center}.carousel .slide img{border:0;width:100%}.carousel .slide iframe{border:0;display:inline-block;margin:0 40px 40px;width:calc(100% - 80px)}.carousel .slide .legend{background:#000;border-radius:10px;bottom:40px;color:#fff;font-size:12px;left:50%;margin-left:-45%;opacity:.25;padding:10px;position:absolute;text-align:center;transition:opacity .35s ease-in-out;width:90%}.carousel .control-dots{bottom:0;margin:10px 0;padding:0;position:absolute;text-align:center;width:100%;z-index:1}.carousel .control-dots .dot{background:#fff;border-radius:50%;box-shadow:1px 1px 2px rgba(0,0,0,.9);cursor:pointer;display:inline-block;height:8px;margin:0 8px;opacity:.3;transition:opacity .25s ease-in;width:8px}.carousel .carousel-status{color:#fff;font-size:10px;padding:5px;position:absolute;right:0;text-shadow:1px 1px 1px rgba(0,0,0,.9);top:0}.bodyImg_d8QI{text-align:center;z-index:2}.heroImg_hSI6{margin-top:20px;padding:0 20px;text-align:center}.heroImg_hSI6 img{height:auto;max-width:900px;width:100%}.codeBox_eWav{margin:0 auto;max-width:560px}.DocSearch-Button{align-items:center;background:var(--docsearch-searchbox-background);border:0;border-radius:40px;color:var(--docsearch-muted-color);cursor:pointer;display:flex;font-weight:500;height:36px;justify-content:space-between;padding:0 8px;-webkit-user-select:none;user-select:none}.DocSearch-Button:active,.DocSearch-Button:focus,.DocSearch-Button:hover{background:var(--docsearch-searchbox-focus-background);box-shadow:var(--docsearch-searchbox-shadow);color:var(--docsearch-text-color);outline:0}.DocSearch-Search-Icon{stroke-width:1.6}.DocSearch-Hit-Tree,.DocSearch-Hit-action,.DocSearch-Hit-icon,.DocSearch-Reset{stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Button .DocSearch-Search-Icon{color:var(--docsearch-text-color)}.DocSearch-Button-Placeholder{font-size:1rem;padding:0 12px 0 6px}.DocSearch-Input,.DocSearch-Link{-webkit-appearance:none;font:inherit}.DocSearch-Button-Keys{display:flex;min-width:calc(40px + .8em)}.DocSearch-Button-Key{align-items:center;background:var(--docsearch-key-gradient);border:0;border-radius:3px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);display:flex;height:18px;justify-content:center;margin-right:.4em;padding:0 0 2px;top:-1px;width:20px}.DocSearch--active{overflow:hidden!important}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:200}.DocSearch-Link{appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{appearance:none;background:0 0;border:0;color:var(--docsearch-text-color);flex:1;font-size:1.2em;height:100%;outline:0;padding:0 0 0 8px;width:80%}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Cancel,.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator,.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset{animation:.1s ease-in forwards b;-webkit-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;padding:2px;right:0}.DocSearch-Help,.DocSearch-HitsFooter,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Reset:focus,.buttonDelete_vlNf:focus,.buttonGroup_SdX7 button:focus,.buttonThin_xRd9:focus{outline:0}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:0 0}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help{font-size:.9em;margin:0;-webkit-user-select:none;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}.DocSearch-Hit--deleting{opacity:0;transition:.25s linear}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:.25s linear .25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:-webkit-sticky;position:sticky;top:0;z-index:10}.DocSearch-Hit-action-button,.DocSearch-Prefill{-webkit-appearance:none;background:none;cursor:pointer}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.buttonGroup_SdX7 button.selected_VHjy,.buttonGroup_SdX7 button:hover{background:var(--ifm-menu-color-background-active)}.DocSearch-Hit[aria-selected=true] mark{text-decoration:underline}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{appearance:none;border:0;border-radius:50%;color:inherit;padding:2px}svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon,.tocCollapsibleContent_vkbj a{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:background-color .1s ease-in}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"» "}.DocSearch-Prefill{appearance:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:0;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;-webkit-user-select:none;user-select:none;width:100%;z-index:300}.DocSearch-Commands li,.DocSearch-Commands-Key{align-items:center;display:flex}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{background:var(--docsearch-key-gradient);border:0;border-radius:2px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);height:18px;justify-content:center;margin-right:.4em;padding:0 0 1px;width:20px}.codeBlockContainer_Ckt0,.playgroundContainer_X_Ta,.playgroundContainer_l4rC{box-shadow:var(--ifm-global-shadow-lw)}@keyframes b{0%{opacity:0}to{opacity:1}}.DocSearch-Button{margin:0;transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.DocSearch-Container{z-index:calc(var(--ifm-z-index-fixed) + 1)}.iconEdit_Z9Sw{margin-right:.3em;vertical-align:sub}.playgroundContainer_X_Ta{border-radius:var(--ifm-global-radius);margin-bottom:var(--ifm-leading);overflow:hidden}.playgroundHeader_dyrN,.playgroundHeader_yAq7{background:var(--ifm-color-emphasis-200);color:var(--ifm-color-content);font-size:var(--ifm-code-font-size);font-weight:700;letter-spacing:.08rem;padding:.75rem;text-transform:uppercase}.playgroundHeader_dyrN:first-of-type,.playgroundHeader_yAq7:first-of-type{background:var(--ifm-color-emphasis-600);color:var(--ifm-color-content-inverse)}.playgroundEditor_Q6Y7,.playgroundEditor_nWOY{direction:ltr;font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace)!important}.playgroundPreview_DzOI,.playgroundPreview_iGtG{background-color:var(--ifm-pre-background);padding:1rem}.tag_zVej{border:1px solid var(--docusaurus-tag-list-border);transition:border var(--ifm-transition-fast)}.tag_zVej:hover{--docusaurus-tag-list-border:var(--ifm-link-color);text-decoration:none}.tagRegular_sFm0{border-radius:var(--ifm-global-radius);font-size:90%;padding:.2rem .5rem .3rem}.tagWithCount_h2kH{align-items:center;border-left:0;display:flex;padding:0 .5rem 0 1rem;position:relative}.tagWithCount_h2kH:after,.tagWithCount_h2kH:before{border:1px solid var(--docusaurus-tag-list-border);content:"";position:absolute;top:50%;transition:inherit}.tagWithCount_h2kH:before{border-bottom:0;border-right:0;height:1.18rem;right:100%;transform:translate(50%,-50%) rotate(-45deg);width:1.18rem}.tagWithCount_h2kH:after{border-radius:50%;height:.5rem;left:0;transform:translateY(-50%);width:.5rem}.tagWithCount_h2kH span{background:var(--ifm-color-secondary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-black);font-size:.7rem;line-height:1.2;margin-left:.3rem;padding:.1rem .4rem}.buttonGroup__atx button,.codeBlockContainer_Ckt0{background:var(--prism-background-color);color:var(--prism-color)}.tag_Nnez{display:inline-block;margin:.5rem .5rem 0 1rem}.codeBlockContainer_Ckt0{border-radius:var(--ifm-code-border-radius);margin-bottom:var(--ifm-leading)}.tags_jXut{display:inline}.tag_QGVx{display:inline-block;margin:0 .4rem .5rem 0}.codeBlockContent_biex{border-radius:inherit;direction:ltr;position:relative}.codeBlockTitle_Ktv7{border-bottom:1px solid var(--ifm-color-emphasis-300);border-top-left-radius:inherit;border-top-right-radius:inherit;font-size:var(--ifm-code-font-size);font-weight:500;padding:.75rem var(--ifm-pre-padding)}.codeBlock_bY9V{--ifm-pre-background:var(--prism-background-color);margin:0;padding:0}.codeBlockLines_e6Vv{float:left;font:inherit;min-width:100%;padding:var(--ifm-pre-padding)}.codeBlockLinesWithNumbering_o6Pm{display:table;padding:var(--ifm-pre-padding) 0}.buttonGroup__atx{column-gap:.2rem;display:flex;position:absolute;right:calc(var(--ifm-pre-padding)/2);top:calc(var(--ifm-pre-padding)/2)}.buttonGroup__atx button{align-items:center;border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-global-radius);display:flex;line-height:0;opacity:0;padding:.4rem;transition:opacity .2s ease-in-out}.buttonGroup__atx button:focus-visible,.buttonGroup__atx button:hover{opacity:1!important}.theme-code-block:hover .buttonGroup__atx button{opacity:.4}.lastUpdated_vwxv{font-size:smaller;font-style:italic;margin-top:.2rem}:where(:root){--docusaurus-highlighted-code-line-bg:#484d5b}:where([data-theme=dark]){--docusaurus-highlighted-code-line-bg:#646464}.theme-code-block-highlighted-line{background-color:var(--docusaurus-highlighted-code-line-bg);display:block;margin:0 calc(var(--ifm-pre-padding)*-1);padding:0 var(--ifm-pre-padding)}.codeLine_lJS_{counter-increment:a;display:table-row}.codeLineNumber_Tfdd{background:var(--ifm-pre-background);display:table-cell;left:0;overflow-wrap:normal;padding:0 var(--ifm-pre-padding);position:-webkit-sticky;position:sticky;text-align:right;width:1%}.codeLineNumber_Tfdd:before{content:counter(a);opacity:.4}.codeLineContent_feaV{padding-right:var(--ifm-pre-padding)}.theme-code-block:hover .copyButtonCopied_obH4{opacity:1!important}.copyButtonIcons_eSgA{height:1.125rem;position:relative;width:1.125rem}.copyButtonIcon_y97N,.copyButtonSuccessIcon_LjdS{fill:currentColor;height:inherit;left:0;opacity:inherit;position:absolute;top:0;transition:.15s;width:inherit}.copyButtonSuccessIcon_LjdS{color:#00d600;left:50%;opacity:0;top:50%;transform:translate(-50%,-50%) scale(.33)}.copyButtonCopied_obH4 .copyButtonIcon_y97N{opacity:0;transform:scale(.33)}.copyButtonCopied_obH4 .copyButtonSuccessIcon_LjdS{opacity:1;transform:translate(-50%,-50%) scale(1);transition-delay:75ms}.tocCollapsibleButton_TO0P{align-items:center;display:flex;font-size:inherit;justify-content:space-between;padding:.4rem .8rem;width:100%}.tocCollapsibleButton_TO0P:after{background:var(--ifm-menu-link-sublist-icon) 50% 50%/2rem 2rem no-repeat;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast);width:1.25rem}.tocCollapsibleButtonExpanded_MG3E:after,.tocCollapsibleExpanded_sAul{transform:none}.wordWrapButtonIcon_Bwma{height:1.2rem;width:1.2rem}.details_lb9f{--docusaurus-details-summary-arrow-size:0.38rem;--docusaurus-details-transition:transform 200ms ease;--docusaurus-details-decoration-color:grey}.details_lb9f>summary{cursor:pointer;list-style:none;padding-left:1rem;position:relative}.details_lb9f>summary::-webkit-details-marker{display:none}.details_lb9f>summary:before{border-color:transparent transparent transparent var(--docusaurus-details-decoration-color);border-style:solid;border-width:var(--docusaurus-details-summary-arrow-size);content:"";left:0;position:absolute;top:.45rem;transform:rotate(0);transform-origin:calc(var(--docusaurus-details-summary-arrow-size)/2) 50%;transition:var(--docusaurus-details-transition)}.collapsibleContent_i85q{border-top:1px solid var(--docusaurus-details-decoration-color);margin-top:1rem;padding-top:1rem}.details_b_Ee{--docusaurus-details-decoration-color:var(--ifm-alert-border-color);--docusaurus-details-transition:transform var(--ifm-transition-fast) ease;border:1px solid var(--ifm-alert-border-color);margin:0 0 var(--ifm-spacing-vertical)}.anchorWithStickyNavbar_LWe7{scroll-margin-top:calc(var(--ifm-navbar-height) + .5rem)}.anchorWithHideOnScrollNavbar_WYt5{scroll-margin-top:.5rem}.hash-link{opacity:0;padding-left:.5rem;transition:opacity var(--ifm-transition-fast);-webkit-user-select:none;user-select:none}.buttonGroup_SdX7 button,.showMoreButton_ZGo2{cursor:pointer;-webkit-user-select:none;white-space:nowrap}.hash-link:before{content:"#"}.containsTaskList_mC6p{list-style:none}:not(.containsTaskList_mC6p>li)>.containsTaskList_mC6p{padding-left:0}.tocCollapsible_ETCw{background-color:var(--ifm-menu-color-background-active);border-radius:var(--ifm-global-radius);margin:1rem 0}.tocCollapsibleContent_vkbj>ul{border-left:none;border-top:1px solid var(--ifm-color-emphasis-300);font-size:15px;padding:.2rem 0}.tocCollapsibleContent_vkbj ul li{margin:.4rem .8rem}.img_ev3q{height:auto}.breadcrumbsContainer_Z_bl{--ifm-breadcrumb-size-multiplier:0.8;margin-bottom:.8rem}.breadcrumbHomeIcon_OVgt{height:1.1rem;position:relative;top:1px;vertical-align:top;width:1.1rem}.title_kItE{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-leading)*1.25)}.admonition_LlT9{margin-bottom:1em}.admonitionHeading_tbUL{font:var(--ifm-heading-font-weight) var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.3rem}.admonitionHeading_tbUL code{text-transform:none}.admonitionIcon_kALy{display:inline-block;margin-right:.4em;vertical-align:middle}.admonitionIcon_kALy svg{fill:var(--ifm-alert-foreground-color);display:inline-block;height:1.6em;width:1.6em}.tableOfContents_bqdL{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:-webkit-sticky;position:sticky;top:calc(var(--ifm-navbar-height) + 1rem)}.code__tabs_vqEd{display:table-row-group}.buttonGroup_SdX7{background:var(--openapi-card-background-color);border-radius:var(--openapi-card-border-radius) var(--openapi-card-border-radius) 2px 2px;color:var(--ifm-pre-color);display:flex;font-family:var(--ifm-font-family-monospace);justify-content:flex-end;margin-bottom:1px;margin-top:0}.buttonGroup_SdX7 button{--margin:0.25rem;-webkit-appearance:none;appearance:none;background:0 0;border:0 solid transparent;border-radius:calc(var(--margin));color:var(--ifm-menu-color);display:block;font-size:13.3333px;font-weight:var(--ifm-font-weight-semibold);line-height:var(--ifm-pre-line-height);margin:var(--margin);margin-right:0;padding:calc(var(--ifm-button-padding-vertical)*var(--ifm-button-size-multiplier)) calc(var(--ifm-button-padding-horizontal)*var(--ifm-button-size-multiplier));text-align:center;transition:color var(--ifm-button-transition-duration) cubic-bezier(.08,.52,.52,1),background var(--ifm-button-transition-duration) cubic-bezier(.08,.52,.52,1),border-color var(--ifm-button-transition-duration) cubic-bezier(.08,.52,.52,1);user-select:none;width:100%}.buttonGroup_SdX7 button:last-child{margin-right:.25rem}.buttonGroup_SdX7 button.selected_VHjy{color:var(--ifm-menu-color-active)}.formItem_WgRa{margin-top:var(--ifm-pre-padding)}.inputBase_a3Vd,.inputBase_kdtO,.inputBase_p1em,html[data-theme=dark] .selectInput__VTP{border:none;margin-top:calc(var(--ifm-pre-padding)/2);background-color:var(--openapi-input-background);font-size:var(--ifm-code-font-size);width:100%;outline:0}.paramsRequired_UKPH,.required_cVzG{color:var(--openapi-required);font-size:var(--ifm-code-font-size)}.inputBase_kdtO{padding:12px 48px 12px var(--ifm-pre-padding)}.inputBase_kdtO,html[data-theme=dark] .selectInput__VTP{border-radius:4px;color:var(--ifm-pre-color)}html[data-theme=dark] .selectInput__VTP{background-image:url('data:image/svg+xml;charset=US-ASCII,')}.selectInput__VTP,html[data-theme=dark] .selectInput__VTP{-webkit-appearance:none;appearance:none;background-position:right var(--ifm-pre-padding) top 50%;background-repeat:no-repeat;background-size:auto auto}.selectInput__VTP{background-image:url('data:image/svg+xml;charset=US-ASCII,')}.buttonDelete_vlNf:active,.input_CV2d:focus,.input_Ru3N:focus,.selectInput__VTP:focus{box-shadow:inset 0 0 0 2px var(--openapi-input-border)}.inputBase_a3Vd,.inputBase_p1em{border-radius:4px;color:var(--ifm-pre-color);padding:12px var(--ifm-pre-padding)}.floatingButton_oJlZ button{background:var(--ifm-color-emphasis-900);border:none;border-radius:var(--ifm-global-radius);color:var(--ifm-color-emphasis-100);cursor:pointer;opacity:0;padding:.4rem .5rem;position:absolute;right:calc(var(--ifm-pre-padding)/2);transition:opacity .2s ease-in-out,visibility .2s ease-in-out,bottom .2s ease-in-out;visibility:hidden}.floatingButton_oJlZ button:focus-visible,.floatingButton_oJlZ:focus-visible button,.floatingButton_oJlZ:hover button{opacity:1;visibility:visible}.dropzone_Y7D0{border:2px dashed var(--openapi-monaco-border-color)}.dropzoneHover_IkP6,.dropzone_Y7D0{align-items:center;background-color:var(--openapi-input-background);border-radius:4px;cursor:pointer;display:inline-flex;font-size:var(--ifm-code-font-size);justify-content:center;padding:var(--ifm-pre-padding);width:100%}.dropzoneHover_IkP6,.dropzone_Y7D0:hover{background:linear-gradient(var(--openapi-dropzone-hover-shim),var(--openapi-dropzone-hover-shim)),linear-gradient(var(--ifm-color-primary),var(--ifm-color-primary));border:2px dashed var(--ifm-color-primary)}.dropzoneContent_CEnm{align-items:center;color:var(--openapi-dropzone-color);display:flex;flex-wrap:wrap;justify-content:center;margin:var(--ifm-pre-padding) 0}.dropzone_Y7D0:hover .dropzoneContent_CEnm{color:var(--ifm-pre-color)}.dropzoneHover_IkP6 .dropzoneContent_CEnm{align-items:center;color:var(--ifm-pre-color);display:flex;flex-wrap:wrap;justify-content:center;margin:var(--ifm-pre-padding) 0}.discriminatorTabsTopSection_QeEp+hr,.schemaTabsTopSection_sc6Y+hr{display:none}.filename_FaIo{flex:1;margin:0 calc(var(--ifm-pre-padding)*1.5);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.playgroundContainer_l4rC{border-radius:var(--ifm-global-radius);margin-bottom:var(--ifm-leading);margin-top:1rem;max-height:500px;overflow:auto}.tabItem_VIbn,.tabItem_es3Q{align-items:center;border:1px solid var(--ifm-color-primary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-primary);display:flex;font-size:12px;height:1.8rem;justify-content:center;margin-right:.5rem;margin-top:0!important}.tabItem_VIbn:hover,.tabItem_es3Q:hover{background-color:var(--ifm-color-emphasis-100);opacity:1}.schemaTabsTopSection_sc6Y{align-items:center;display:flex;justify-content:space-between;margin-top:1rem}.schemaTabsListContainer_wmy4::-webkit-scrollbar{display:none}.discriminatorTabLabel_dvfv,.schemaTabLabel_clV0{white-space:nowrap}.schemaTabsContainer_HVyG{align-items:center;display:flex;max-width:600px;overflow:hidden}.tabArrow_WDKX,.tabArrow_zmvw{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;border:none;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;min-width:1.25rem;padding:0 .75rem;width:1.25rem}.paramsItem_PKlE,.schemaItem_P8yX{border-left:thin solid var(--ifm-color-gray-500)!important;position:relative}.inputBase_MRMh,.showMoreButton_ZGo2{font-size:var(--ifm-code-font-size);width:100%}.marginVertical_VWja{margin-bottom:unset!important;margin-top:1rem!important}.paramsItem_PKlE{margin:0 0 0 1rem!important;margin-top:unset!important;padding-left:1rem}.schemaName_KIS9,.schemaName_MpGo{opacity:.6}.schemaItem_P8yX{list-style:none;margin:0!important;padding:5px 0 5px 1rem}.required_Wlqr{color:var(--openapi-required)}.deprecated_K3O1,.required_Wlqr{font-size:var(--ifm-code-font-size)}.deprecated_K3O1{color:var(--openapi-deprecated)}.nullable_MFJr{color:var(--openapi-nullable);font-size:var(--ifm-code-font-size)}.strikethrough_nBFj{text-decoration:line-through}.discriminatorTabsTopSection_QeEp{align-items:center;display:flex;justify-content:space-between;margin-left:.9rem;margin-top:1rem}.discriminatorTabsContainer_FMrl{align-items:center;display:flex;overflow:hidden;padding-left:3px;max-width:600px}.discriminatorTabsListContainer_Nnai::-webkit-scrollbar{display:none}.inputBase_MRMh{background-color:var(--openapi-input-background);border:2px solid transparent;border-radius:4px;color:var(--ifm-pre-color);margin-top:calc(var(--ifm-pre-padding)/2);outline:0;padding:12px var(--ifm-pre-padding)}.selectInput_xXUj{-webkit-appearance:none;appearance:none}.selectInput_xXUj option{border-radius:.25rem;color:var(--ifm-menu-color);margin:.25rem 0;padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.selectInput_xXUj:focus{border:2px solid var(--openapi-input-border)}.plus_Swon{display:inline-block;margin-right:6px;transform:rotate(0);transform-origin:center;transition:transform .2s}.plusExpanded_WYvI{transform:rotate(45deg)}.showMoreButton_ZGo2{-webkit-appearance:none;appearance:none;background-color:transparent;border:0 solid transparent;color:var(--ifm-color-primary);display:block;margin-bottom:0;margin-top:var(--ifm-pre-padding);padding:0;text-align:left;user-select:none}.buttonDelete_vlNf,.buttonThin_xRd9{-webkit-appearance:none;font-size:calc(.875rem*var(--ifm-button-size-multiplier));font-weight:400;line-height:1.5;margin-top:calc(var(--ifm-pre-padding)/2);transition-duration:.1s,.1s,.1s,var(--ifm-button-transition-duration);transition-property:color,background,border-color,box-shadow;transition-timing-function:cubic-bezier(.08,.52,.52,1);-webkit-user-select:none}.showMoreButton_ZGo2:hover,.showMoreButton_jDKX:hover{color:var(--ifm-color-primary-hover)}.buttonDelete_vlNf{align-items:center;appearance:none;background-color:var(--openapi-input-background);border:none;border-radius:4px;color:var(--ifm-pre-color);cursor:pointer;display:flex;justify-content:center;margin-left:4px;outline:0;padding:0 12px;user-select:none;white-space:nowrap}.buttonThin_xRd9,.showMoreButton_jDKX{background-color:transparent;cursor:pointer;white-space:nowrap}.buttonThin_xRd9{appearance:none;border:1px solid var(--openapi-input-border);border-radius:var(--ifm-pre-border-radius);color:var(--openapi-input-border);display:block;margin-bottom:var(--ifm-pre-padding);padding:3px 60px 3px 12px;user-select:none}.buttonThin_xRd9:hover{background-color:var(--openapi-input-border);color:var(--openapi-inverse-color)}.buttonThin_xRd9:active{box-shadow:inset 0 0 0 1px var(--openapi-input-border),inset 0 0 0 2px var(--openapi-inverse-color)}.showOptions_Gv8N{margin-top:var(--ifm-pre-padding);visibility:visible}.hideOptions_JLDS{display:none;visibility:hidden}.showMoreButton_jDKX{-webkit-appearance:none;appearance:none;border:0 solid transparent;color:var(--ifm-color-primary);display:block;font-size:var(--ifm-code-font-size);margin-bottom:0;margin-top:var(--ifm-pre-padding);padding:0;text-align:left;-webkit-user-select:none;user-select:none}.optionsPanel_s3ok,.optionsPanel_tfQ6{background:var(--openapi-card-background-color);border-radius:var(--openapi-card-border-radius);color:var(--ifm-pre-color);line-height:var(--ifm-pre-line-height);margin-bottom:var(--ifm-spacing-vertical);margin-top:0;overflow:auto;position:relative}.optionsPanel_s3ok:empty,.optionsPanel_tfQ6:empty{display:none}.optionsPanel_s3ok{padding-top:0!important;padding:var(--ifm-pre-padding)}@media (min-width:600px){.plans_lWeT{column-gap:8px;flex-direction:row;justify-content:center}}@media (min-width:768px){.page-home{height:1300px;left:50%;object-fit:cover;object-position:bottom;transform:translateX(-50%);width:100vw}.ssoBgImb{top:0;width:100vh}.integrationsBgImg{left:0;top:-240px;width:100vw}.enterpriseSsoBgImg{width:50vw}.entSocialLoginBgCircles{top:-336px;width:1120px}.entSocialLoginBgImg{max-width:1164px;top:-264px}.plansBgImg{left:0;top:-50%;width:100vw}.navbar__items--right{display:flex;justify-content:flex-end;left:auto;position:absolute;right:20px;top:16px;width:auto!important}.contentBlock{margin:140px auto}.footer--dark{--ifm-footer-background-color:#2b3137}.footer__links{display:grid;grid-template-columns:repeat(2,1fr)}.footer__col:last-child{grid-column:1/3}.heroIntegrationRow_vSEr{grid-template-columns:repeat(5,1fr);grid-template-rows:repeat(2,1fr);margin:0 auto;max-width:782px}.heroIntegrationMore_ABZQ{grid-column:auto}.heroIntegrationsLines_bELR{max-width:630px;width:100%}.heroIntegration_GaJq{box-shadow:none}.hero_syme{padding-top:60px}.heroProjectTagline_EkV5{font-size:2rem}.heroCta_r0UD .btnPrimary_Yph1{font-size:13px}.sectPreHeadline_myin{margin-bottom:1rem}.sectHeadline_XdUn{font-size:2.2rem;max-width:inherit}.sectHeadline_XdUn img{margin:0 12px}.sectHeadline_XdUn .heart_Zeus{width:60px}.sectOpenSource_IMs3{padding-top:40px}.feature_nI3B{align-items:center;display:flex;margin-bottom:0}.feat_uwf3,.feature_nI3B>*{flex:1}.feature_nI3B:nth-child(2n){flex-direction:row-reverse}.featureCopy_WeZz h3{font-size:1.4rem}.featureCopy_WeZz p{font-size:1.2rem}.featureImg_KAsM img{max-width:550px;width:100%}.featureCopy_WeZz>div{max-width:440px;padding:0 40px}.feats_yvrJ{display:flex;gap:40px;margin:0 auto;max-width:88%}.integrationRow_KYiy{margin-left:0;margin-top:-140px;width:96%}.integrationRow_KYiy+.integrationRow_KYiy{margin-top:48px;max-width:76%}.heroImg_hSI6{margin-top:-80px}}@media only screen and (min-width:768px) and (max-width:996px){.code__tabs_vqEd{justify-content:space-around}}@media (min-width:769px){.hero_syme{padding:48px}.sect_fa0Z{padding:72px 0}}@media (min-width:800px){.heroSections_eYcn{column-gap:8px;flex-direction:row}.heroSectionPicto_NANO{margin-bottom:8px;width:48px}.heroSection_dXSz{flex-direction:column;max-width:150px;padding:20px}.heroSectionPlus_e1gH{align-items:center;background-color:var(--color-wh);border:3px solid #fed9cf;border-radius:50%;display:inline-flex;height:27px;justify-content:center;position:absolute;right:-16px;top:calc(50% - 12px);width:27px;z-index:1}.heroSectionPlus_e1gH img{height:auto;width:14px}.heroFeats_bnZi{column-gap:64px;margin-top:110px}.featCards_qurD{column-gap:12px;flex-direction:row;row-gap:0}.featCard_EPO6{column-gap:32px;flex-direction:row;row-gap:0}.checklist_YNT1{font-size:1rem}}@media (min-width:960px){.carousel .control-dots{bottom:0}}@media (min-width:992px){.readMore{align-items:center;column-gap:72px;flex-direction:row}.readMoreL{width:calc(50% + 72px)}.readMoreR{width:calc(50% - 72px)}.footer,.footer .text--center{text-align:left}.footer__links{display:flex}.footer-logo{float:right;margin-top:-24px}.devs_TdUg,.enterpriseSSO_fLpf{column-gap:80px;flex-direction:row;row-gap:0}.enterpriseSSOL_qbBN{width:64%}.enterpriseSSOR_lqTw{width:36%}.devsL_xtfI,.devsR_oMCi{width:50%}.plansBlocks_GHdj{column-gap:20px;flex-direction:row;row-gap:0}.plansBlock_fHJo{flex:1;height:100%}.plans_lWeT{column-gap:20px}.planHead_C2Ld h3{font-size:1.75rem;margin:16px 0}.planBody_fSqo{min-height:180px}.hero_syme{padding-bottom:60px;padding-top:60px}.heroInner_VWeJ{align-items:center;display:flex;flex-direction:row-reverse;gap:60px}.heroMsg_PB71{flex:1 1 auto}.heroProjectTagline_EkV5{font-size:1.9rem}.heroIntro_TtUU{font-size:1.3rem}.heroImg_wnHi{flex:0 0 auto;min-width:0;width:50%}.heroImg_wnHi img{max-width:800px}.sectOsArrow_ksPO{display:block}}@media (min-width:997px){.collapseSidebarButton_PEFL,.expandButton_m80_{background-color:var(--docusaurus-collapse-button-bg)}.docItemCol,.docItemCol_BDYx,.docItemCol_VOVn,.generatedIndexPage_vN6x{max-width:75%!important}.tocMobile,.tocMobile_ITEo{display:none}:root{--docusaurus-announcement-bar-height:30px}.announcementBarClose_gvF7,.announcementBarPlaceholder_vyr4{flex-basis:50px}.searchBox_ZlJk{padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.collapseSidebarButton_PEFL{border:1px solid var(--ifm-toc-border-color);border-radius:0;bottom:0;display:block!important;height:40px;position:-webkit-sticky;position:sticky}.collapseSidebarButtonIcon_kv0_{margin-top:4px;transform:rotate(180deg)}.expandButtonIcon_BlDH,[dir=rtl] .collapseSidebarButtonIcon_kv0_{transform:rotate(0)}.collapseSidebarButton_PEFL:focus,.collapseSidebarButton_PEFL:hover,.expandButton_m80_:focus,.expandButton_m80_:hover{background-color:var(--docusaurus-collapse-button-bg-hover)}.menuHtmlItem_M9Kj{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu_SIkG{flex-grow:1;padding:.5rem}@supports (scrollbar-gutter:stable){.menu_SIkG{padding:.5rem 0 .5rem .5rem;scrollbar-gutter:stable}}.menuWithAnnouncementBar_GW3s{margin-bottom:var(--docusaurus-announcement-bar-height)}.sidebar_njMd{display:flex;flex-direction:column;height:100%;max-height:100vh;padding-top:var(--ifm-navbar-height);position:-webkit-sticky;position:sticky;top:0;transition:opacity 50ms;width:var(--doc-sidebar-width)}.sidebarWithHideableNavbar_wUlq{padding-top:0}.sidebarHidden_VK0M{height:0;opacity:0;overflow:hidden;visibility:hidden}.sidebarLogo_isFc{align-items:center;color:inherit!important;display:flex!important;margin:0 var(--ifm-navbar-padding-horizontal);max-height:var(--ifm-navbar-height);min-height:var(--ifm-navbar-height);text-decoration:none!important}.sidebarLogo_isFc img{height:2rem;margin-right:.5rem}.expandButton_m80_{align-items:center;display:flex;height:100%;justify-content:center;max-height:100vh;position:-webkit-sticky;position:sticky;top:0;transition:background-color var(--ifm-transition-fast) ease}[dir=rtl] .expandButtonIcon_BlDH{transform:rotate(180deg)}.docSidebarContainer_b6E3{border-right:1px solid var(--ifm-toc-border-color);-webkit-clip-path:inset(0);clip-path:inset(0);display:block;margin-top:calc(var(--ifm-navbar-height)*-1);transition:width var(--ifm-transition-fast) ease;width:var(--doc-sidebar-width);will-change:width}.docSidebarContainerHidden_b3ry{cursor:pointer;width:var(--doc-sidebar-hidden-width)}.docMainContainer_gTbr{flex-grow:1;max-width:calc(100% - var(--doc-sidebar-width))}.docMainContainerEnhanced_Uz_u{max-width:calc(100% - var(--doc-sidebar-hidden-width))}.docItemWrapperEnhanced_czyv{max-width:calc(var(--ifm-container-width) + var(--doc-sidebar-width))!important}.lastUpdated_vwxv{text-align:right}.list_eTzJ article:nth-last-child(-n+2){margin-bottom:0!important}}@media (min-width:1200px){.heroProjectTagline_EkV5,.pageHeroMsg h1{font-size:3.375rem}.pageHero{padding:60px 20px 20px}.pageHeroMsgIntro{margin-left:auto;margin-right:auto;max-width:640px}.pageHeroMsg h1{margin-bottom:24px}.pageHeroImg{margin-bottom:20px;max-width:782px}.heroImg_wnHi{width:55%}}@media (min-width:1440px){.container{max-width:var(--ifm-container-width-xl)}}@media (max-width:996px){.col{--ifm-col-width:100%;flex-basis:var(--ifm-col-width);margin-left:0}.footer{--ifm-footer-padding-horizontal:0}.colorModeToggle_DEke,.footer__link-separator,.navbar__item,.sidebar_re4s,.tableOfContents_bqdL{display:none}.footer__col{margin-bottom:calc(var(--ifm-spacing-vertical)*3)}.footer__link-item{display:block}.hero{padding-left:0;padding-right:0}.navbar>.container,.navbar>.container-fluid{padding:0}.navbar__toggle{display:inherit}.navbar__search-input{width:9rem}.pills--block,.tabs--block{flex-direction:column}.navbar .navbar__items{flex:1 1 auto;justify-content:center;width:100%}.navbar .navbar__toggle{left:0;margin-top:2px;padding:10px;position:absolute}.navbar .navbar__brand{margin-top:3px}.navbar .navbar__logo{width:100px}.navbar .navbar-sidebar{background-color:#fff;display:flex;flex-direction:column;height:100vh;transition:.35s cubic-bezier(.23,1,.32,1)}.navbar .navbar-sidebar__brand{box-shadow:none;flex:0 0 auto;justify-content:center}.navbar .navbar-sidebar__items{display:flex;flex:1 1 auto}.menu{width:100%}.menu .menu__link{font-size:1.15rem;justify-content:center;padding:18px 12px}.navbar .navbar-sidebar__backdrop{background-color:hsla(0,0%,100%,.8);display:block;height:100vh;opacity:0;transition:.3s;visibility:hidden}.navbar.navbar-sidebar--show .navbar-sidebar__backdrop{opacity:1;visibility:visible}.searchBox_ZlJk{position:absolute;right:var(--ifm-navbar-padding-horizontal)}.docItemContainer_F8PC{padding:0 .3rem}}@media only screen and (max-width:996px){.searchQueryColumn_RTkw,.searchResultsColumn_JPFH{max-width:60%!important}.searchLogoColumn_rJIA,.searchVersionColumn_ypXd{max-width:40%!important}.searchLogoColumn_rJIA{padding-left:0!important}}@media (max-width:991px){.heroInner_VWeJ{display:flex;flex-direction:column-reverse}.heroCta_r0UD,.heroIntro_TtUU{margin-left:auto;margin-right:auto}.heroMsg_PB71{text-align:center}.heroCta_r0UD{margin-bottom:30px}}@media screen and (max-width:966px){.heroBanner_UJJx{padding:2rem}}@media (max-width:768px){.DocSearch-Button-Keys,.DocSearch-Button-Placeholder,.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%;max-height:calc(var(--docsearch-vh,1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh,1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh,1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Cancel{-webkit-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:0;overflow:hidden;padding:0;-webkit-user-select:none;user-select:none;white-space:nowrap}}@media (max-width:767px){.featureCopy_WeZz{text-align:center}.featureCopy_WeZz h3{margin-bottom:12px;margin-top:16px}.heroImg_wnHi{margin-left:-12px;margin-right:-12px}}@media (max-width:576px){.markdown h1:first-child{--ifm-h1-font-size:2rem}.markdown>h2{--ifm-h2-font-size:1.5rem}.markdown>h3{--ifm-h3-font-size:1.25rem}.title_f1Hy{font-size:2rem}}@media screen and (max-width:576px){.searchQueryColumn_RTkw{max-width:100%!important}.searchVersionColumn_ypXd{max-width:100%!important;padding-left:var(--ifm-spacing-horizontal)!important}}@media screen and (max-width:500px){.discriminatorTabsTopSection_QeEp,.mimeTabsTopSection_wt53,.responseTabsTopSection_jEoq,.schemaTabsTopSection_sc6Y{align-items:flex-start;flex-direction:column}.mimeTabsContainer_gZbZ,.responseTabsContainer_XEhZ{margin-top:var(--ifm-spacing-vertical);padding:0;width:100%}.discriminatorTabsContainer_FMrl,.schemaTabsContainer_HVyG{width:100%}.tabItem_VIbn{height:100%}}@media (hover:hover){.backToTopButton_sjWU:hover{background-color:var(--ifm-color-emphasis-300)}}@media (pointer:fine){.thin-scrollbar{scrollbar-width:thin}.thin-scrollbar::-webkit-scrollbar{height:var(--ifm-scrollbar-size);width:var(--ifm-scrollbar-size)}.thin-scrollbar::-webkit-scrollbar-track{background:var(--ifm-scrollbar-track-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb{background:var(--ifm-scrollbar-thumb-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb:hover{background:var(--ifm-scrollbar-thumb-hover-background-color)}}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{stroke-width:var(--docsearch-icon-stroke-width);animation:none;-webkit-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0}.DocSearch-Hit--deleting,.DocSearch-Hit--favoriting{transition:none}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:none}}@media print{.announcementBar_mb4j,.footer,.menu,.navbar,.pagination-nav,.table-of-contents,.tocMobile_ITEo{display:none}.tabs{page-break-inside:avoid}.codeBlockLines_e6Vv{white-space:pre-wrap}} \ No newline at end of file diff --git a/assets/js/c4f5d8e4.17f06b3c.js b/assets/js/c4f5d8e4.17f06b3c.js new file mode 100644 index 000000000..3c288cdec --- /dev/null +++ b/assets/js/c4f5d8e4.17f06b3c.js @@ -0,0 +1,2 @@ +/*! For license information please see c4f5d8e4.17f06b3c.js.LICENSE.txt */ +(self.webpackChunkphasetwo_docs=self.webpackChunkphasetwo_docs||[]).push([[4195],{62841:function(e,t,a){"use strict";a.r(t);var l=a(67294),n=(a(94184),a(56866)),r=a(39960),c=a(52263),s=(a(44996),a(99578)),o=a(98383);function i(){window.open("https://phasetwo.io/dashboard/","_blank")}function m(){window.open("mailto:support@phasetwo.io")}function g(){window.open("https://github.com/p2-inc/","_blank")}function u(){window.location="/docs/introduction"}const p=()=>l.createElement("img",{src:"img/checkmark.svg",alt:"Checkmark"}),d=()=>l.createElement("span",{className:s.Z.notPartOfPlan},"\u2014"),E=[{feature:"Architecture review",silver:l.createElement(p,null),gold:l.createElement(p,null)},{feature:"Installation and configuration support",silver:l.createElement(p,null),gold:l.createElement(p,null)},{feature:"Email support",silver:l.createElement(p,null),gold:l.createElement(p,null)},{feature:"Slack support",silver:l.createElement(d,null),gold:l.createElement(p,null)},{feature:"Phone support",silver:l.createElement(d,null),gold:l.createElement(p,null)},{feature:l.createElement("span",null,"Support hours ",l.createElement("span",{style:{opacity:.5}},"(US EST)")),silver:"9x5",gold:"24x7x365"},{feature:l.createElement("span",null,"Response time ",l.createElement("span",{style:{opacity:.5}},"(hours)")),silver:"24",gold:"4"},{feature:"Health assessment",silver:"Quarterly",gold:"Monthly"},{feature:l.createElement("span",null,"Incl. service hours ",l.createElement("span",{style:{opacity:.5}},"(/mth)")),silver:l.createElement("span",null,"10 ",l.createElement("span",{style:{opacity:.5}},"(max)")),gold:"20"},{feature:"Custom development",silver:l.createElement(d,null),gold:l.createElement(p,null)},{feature:l.createElement("span",null,"Pricing ",l.createElement("span",{style:{opacity:.5}},"(/mth)")),silver:l.createElement("span",null,l.createElement("span",{style:{opacity:.5}},"from")," $3,500"),gold:l.createElement("span",null,l.createElement("span",{style:{opacity:.5}},"from")," $7,500")}];t.default=function(){const e=(0,c.Z)(),{siteConfig:t={}}=e;return(0,l.useEffect)((()=>{document.body.classList.add("page-bg")})),l.createElement(n.Z,{description:`${t.tagline}`},l.createElement("picture",null,l.createElement("source",{media:"(max-width: 767px)",srcset:"/img/home-bg-mobile.jpg"}),l.createElement("source",{media:"(min-width: 768px)",srcset:"/img/home-bg.jpg"}),l.createElement("img",{className:"page-home",src:"/img/home-bg-mobile.jpg",alt:"Gradient Background"})),l.createElement("main",null,l.createElement("div",{className:"pageHero"},l.createElement("div",{className:s.Z.heroInner},l.createElement("div",{className:"pageHeroMsg"},l.createElement("h1",null,"Future-Proof Your App"),l.createElement("p",{className:"pageHeroMsgIntro"},"Accelerate SaaS time-to-market and enterprise adoption by rapidly integrating the features you need."),l.createElement("div",{className:"pageHeroCta"},l.createElement("button",{className:"btnCta",onClick:i},"Try Phase Two for Free")))),l.createElement("div",{className:s.Z.heroSections},l.createElement(r.Z,{to:"product/sso",className:s.Z.heroSection},l.createElement("img",{className:s.Z.heroSectionPicto,src:"img/picto-sso.svg",alt:"Pictogram showing key"}),l.createElement("p",null,"SSO"),l.createElement("div",{className:s.Z.heroSectionPlus},l.createElement("img",{src:"img/plus.svg",alt:"Plus"}))),l.createElement(r.Z,{to:"product/identity",className:s.Z.heroSection},l.createElement("img",{className:s.Z.heroSectionPicto,src:"img/picto-identity.svg",alt:"Pictogram showing a person"}),l.createElement("p",null,"Identity"),l.createElement("div",{className:s.Z.heroSectionPlus},l.createElement("img",{src:"img/plus.svg",alt:"Plus"}))),l.createElement(r.Z,{to:"product/organizations",className:s.Z.heroSection},l.createElement("img",{className:s.Z.heroSectionPicto,src:"img/picto-organizations.svg",alt:"Pictogram showing multiple persons interacting"}),l.createElement("p",null,"Organizations"),l.createElement("div",{className:s.Z.heroSectionPlus},l.createElement("img",{src:"img/plus.svg",alt:"Plus"}))),l.createElement(r.Z,{to:"product/adminportal",className:s.Z.heroSection},l.createElement("img",{className:s.Z.heroSectionPicto,src:"img/picto-admin-portal.svg",alt:"Pictogram showing a browser"}),l.createElement("p",null,"Admin Portal"),l.createElement("div",{className:s.Z.heroSectionPlus},l.createElement("img",{src:"img/plus.svg",alt:"Plus"}))),l.createElement(r.Z,{to:"product/onprem",className:s.Z.heroSection},l.createElement("img",{className:s.Z.heroSectionPicto,src:"img/picto-on-prem.svg",alt:"Pictogram showing on prem servers"}),l.createElement("p",null,"On-Prem Deployment"))),l.createElement("div",{className:s.Z.heroFeats},l.createElement("div",{className:s.Z.heroFeat},l.createElement("img",{src:"img/picto-open-source.svg",alt:"Open Source Logo"}),l.createElement("p",null,"We are open source")),l.createElement("div",{className:s.Z.heroFeat},l.createElement("img",{src:"img/picto-fixed-pricing.svg",alt:"Pictogram showing fixed US dollar sign"}),l.createElement("p",null,"Fixed pricing for peace of mind")),l.createElement("div",{className:s.Z.heroFeat},l.createElement("img",{src:"img/picto-cloud-or-on-prem.svg",alt:"Pictogran showing cloud and on-prem servers"}),l.createElement("p",null,"Cloud or on-prem deployment"))),l.createElement("div",{className:s.Z.heroIntegrations},l.createElement("p",null,"Integrates with"),l.createElement("picture",null,l.createElement("source",{media:"(max-width: 767px)",srcset:"/img/integration-lines-mobile.svg"}),l.createElement("source",{media:"(min-width: 768px)",srcset:"/img/integration-lines.svg"}),l.createElement("img",{className:s.Z.heroIntegrationsLines,src:"/img/integration-lines.svg",alt:"Integration Lines"})),l.createElement("div",{className:s.Z.heroIntegrationRow},l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-okta.svg",alt:"Okta Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-auth0.svg",alt:"Auth0 Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-azure.svg",alt:"Azure Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-google-workspace.svg",alt:"Google Workspace Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-active-directory.svg",alt:"Active Directory Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-jump-cloud.svg",alt:"JumpCloud Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-onelogin.svg",alt:"Onelogin Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-ping-identity.svg",alt:"Ping Identity Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-duo-security.svg",alt:"Duo Security Logo"})),l.createElement("div",{className:`${s.Z.heroIntegration} ${s.Z.heroIntegrationMore}`},l.createElement("p",null,"+ many more"))))),l.createElement("div",{className:"contentBlock"},l.createElement("div",{className:"enterpriseSsoBgImg bgImg"},l.createElement("img",{src:"/img/enterprise-sso-bg.png",alt:"Color Gradient"})),l.createElement("div",{className:"contentBlockHead"},l.createElement("h2",null,"No-code Enterprise SSO"),l.createElement("p",null,"Leap up market into enterprise adoption with seamless SSO support.")),l.createElement("div",{className:"contentBlockBody"},l.createElement("div",{className:s.Z.enterpriseSSO},l.createElement("div",{className:s.Z.enterpriseSSOL},l.createElement("img",{className:s.Z.listFeatsImg,src:"img/hero-feature-sso.png",alt:"SSO Login Examples"})),l.createElement("div",{className:s.Z.enterpriseSSOR},l.createElement("ul",{className:s.Z.listFeats},l.createElement("li",null,l.createElement("img",{className:s.Z.listFeatsPicto,src:"img/picto-5-min-integration.svg",alt:"Pictogram showing 5 minutes on a hour"}),l.createElement("h5",null,"5-minute integration"),l.createElement("p",null,"One integration adds all enterprise identity providers. With or without adopting our identity feature, you can support all popular identity providers.")),l.createElement("li",null,l.createElement("img",{className:s.Z.listFeatsPicto,src:"img/picto-integrate-once.svg",alt:"Pictogram showing puzzle pieces"}),l.createElement("h5",null,"Integrate Once"),l.createElement("p",null,"SAML, OIDC, OAuth2? Support all the standards without years of development and debugging.")),l.createElement("li",null,l.createElement("img",{className:s.Z.listFeatsPicto,src:"img/picto-no-variable-cost.svg",alt:"Pictogram showing US dollar sign"}),l.createElement("h5",null,"No variable cost"),l.createElement("p",null,"We're not a parasite on your business model. Unlimited SSO connections for a single price."))))))),l.createElement("div",{className:"contentBlock"},l.createElement("div",{className:"contentBlockHead"},l.createElement("h2",null,"Admin Portal"),l.createElement("p",null,"Seamless onboarding and self-management for your customer administrators and users. Empower your users and customers to easily manage every aspect of identity, organization and SSO. Drastically reduce customer support.")),l.createElement("div",{className:"contentBlockBody"},l.createElement("div",{className:s.Z.aportal},l.createElement("picture",null,l.createElement("source",{media:"(max-width: 767px)",srcset:"/img/img-admin-portal-new1-mobile.png"}),l.createElement("source",{media:"(min-width: 768px)",srcset:"/img/img-admin-portal-new1.png"}),l.createElement("img",{src:"/img/img-admin-portal-new1.png",alt:"Screenshots showing management of users, domains and SSO"}))))),l.createElement("div",{className:"contentBlock"},l.createElement("div",{className:"contentBlockHead"},l.createElement("h2",null,"By Developers, For Developers"),l.createElement("p",null,"Create delightful, seamless experiences for your customers. In just a few minutes!")),l.createElement("div",{className:"contentBlockBody"},l.createElement("div",{className:s.Z.devs},l.createElement("div",{className:s.Z.devsL},l.createElement("ul",{className:s.Z.listFeats},l.createElement("li",null,l.createElement("img",{className:s.Z.listFeatsPicto,src:"img/picto-simple-integration.svg",alt:"Pictogram showing a code"}),l.createElement("h5",null,"Simple Integration"),l.createElement("p",null,"Our goal is to make it as easy as possible for developers to integrate with our system so they can add SSO and other features quickly and then move on to what's important\u2014their app!")),l.createElement("li",null,l.createElement("img",{className:s.Z.listFeatsPicto,src:"img/picto-documentation.svg",alt:"Pictogram showing documents"}),l.createElement("h5",null,"Full Documentation"),l.createElement("p",null,"We are building great documentation, tutorials and modern SDKs, so implementation is easy regardless of skill level or technology stack.")),l.createElement("li",null,l.createElement("img",{className:s.Z.listFeatsPicto,src:"img/picto-secure.svg",alt:"Pictogram showing a key inside the shield"}),l.createElement("h5",null,"Secure and Standardized"),l.createElement("p",null,"Standards compliance and security are our strengths so you can focus on your your customers.")))),l.createElement("div",{className:s.Z.devsR},l.createElement(o.Z,{language:"javascript",title:"Protect a page"},"var auth = new Keycloak({\n url: 'https://{host}/auth',\n realm: '{realm}',\n clientId: '{clientId}'\n});\nauth.init({\n onLoad: 'login-required'\n}).then(function(authenticated) {\n alert(authenticated ? 'authenticated' :\n 'not authenticated');\n}).catch(function() {\n alert('failed to initialize');\n});\n"))),l.createElement("div",{class:"contentBlockCta"},l.createElement("button",{className:"btnPrimary",onClick:u},"Go to Documentation")))),l.createElement("div",{className:"contentBlock"},l.createElement("div",{className:"keycloakBgCircles bgImg"},l.createElement("img",{src:"/img/circles.svg",alt:"Concentric Circles"})),l.createElement("div",{className:"contentBlockHead"},l.createElement("h2",null,"Phase Two"," ",l.createElement("img",{className:s.Z.heart,src:"img/heart-filled.svg",alt:"Heart symbols"})," ","Keycloak"),l.createElement("p",null,"Phase Two is based on the Keycloak Open Source Identity and Access Management system, built and maintained by Red Hat.")),l.createElement("div",{className:"contentBlockBody"},l.createElement("div",{className:s.Z.keycloak},l.createElement("img",{className:s.Z.keycloakImg,src:"img/diagram-keycloak.svg",alt:"Diagram showing how Keycloak works with Phase Two"})),l.createElement("div",{className:s.Z.featCards},l.createElement("div",{class:s.Z.featCard},l.createElement("img",{className:s.Z.featCardPicto,src:"img/picto-open-source-alt.svg",alt:"Pictogram showing Open Source logo"}),l.createElement("h5",null,"Always Open Source"),l.createElement("p",null,"Phase Two is built as a collection of open source Keycloak extensions. While we endeavor to make Keycloak simple to use, operate and scale, in the cloud or on prem.")),l.createElement("div",{class:s.Z.featCard},l.createElement("img",{className:s.Z.featCardPicto,src:"img/picto-hardened.svg",alt:"Pictogram showing a fortress"}),l.createElement("h5",null,"Battle-tested and hardened"),l.createElement("p",null,"Keycloak has been battle-tested and hardened for over 7 years. Its security and reliability is depended on by organizations from small startups to governments and Fortune 500 companies.")),l.createElement("div",{class:s.Z.featCard},l.createElement("img",{className:s.Z.featCardPicto,src:"img/picto-community.svg",alt:"Pictogram showing a group of poeple interconnected"}),l.createElement("h5",null,"Community Superpower"),l.createElement("p",null,"We believe that community participation in building our software is a superpower, and can't wait to see what you will help us build."))))),l.createElement("div",{id:"pricing",className:"contentBlock"},l.createElement("div",{className:"plansBgImg bgImg"},l.createElement("img",{src:"/img/gradient-bg.png",alt:"Color Gradient"})),l.createElement("div",{className:"contentBlockHead"},l.createElement("h2",null,"Phase Two is One Price Per Project"),l.createElement("p",null,"No hidden fees, no unpredictable costs.")),l.createElement("div",{className:"contentBlockBody"},l.createElement("div",{className:s.Z.plansBlocks},l.createElement("div",{className:s.Z.plansBlock},l.createElement("div",{className:s.Z.plans},l.createElement("div",{className:s.Z.plan},l.createElement("div",{className:s.Z.planHead},l.createElement("img",{className:s.Z.featCardPicto,src:"img/plan-starter.svg",alt:"Starter plan"}),l.createElement("h3",null,"Starter"),l.createElement("p",null,"Always FREE ",l.createElement("sup",null,"1"))),l.createElement("div",{className:s.Z.planBody},l.createElement("ul",{className:s.Z.checklist},l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Shared cluster"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"<1,000 users"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"<10 SSO connections"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Community support"),l.createElement("li",null,"No SLA"))),l.createElement("div",{className:s.Z.planFoot},l.createElement("button",{className:`btnPrimary ${s.Z.btnPlan}`,onClick:i},"Get started"),l.createElement("button",{className:`btnSecondary ${s.Z.btnPlan}`,onClick:g},"Self Host"))),l.createElement("div",{className:s.Z.plan},l.createElement("div",{className:s.Z.planBadge},"Most Popular"),l.createElement("div",{className:s.Z.planHead},l.createElement("img",{className:s.Z.featCardPicto,src:"img/plan-supported.svg",alt:"Premium plan"}),l.createElement("h3",null,"Premium"),l.createElement("p",null,l.createElement("span",{className:s.Z.planFrom},"from")," ",l.createElement("strong",{className:s.Z.planPrice},"$499"),"/mo"," ",l.createElement("sup",null,"2"))),l.createElement("div",{className:s.Z.planBody},l.createElement("ul",{className:s.Z.checklist},l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Dedicated cluster"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Unlimited users"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Unlimited SSO connections"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Custom domain"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Email support"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"99.9% uptime guarantee"))),l.createElement("div",{className:s.Z.planFoot},l.createElement("button",{className:`btnPrimary ${s.Z.btnPlan}`,onClick:i},"Get started"))),l.createElement("div",{className:s.Z.plan},l.createElement("div",{className:s.Z.planHead},l.createElement("img",{className:s.Z.featCardPicto,src:"img/plan-premium.svg",alt:"Enterprise plan"}),l.createElement("h3",null,"Enterprise"),l.createElement("p",null,l.createElement("span",{className:s.Z.planFrom},"from")," ",l.createElement("strong",{className:s.Z.planPrice},"$1999"),"/mo"," ",l.createElement("sup",null,"2"))),l.createElement("div",{className:s.Z.planBody},l.createElement("ul",{className:s.Z.checklist},l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"All Premium features"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Global deployment"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Custom themes & extensions ",l.createElement("sup",null,"3")),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Dedicated support"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"99.99% uptime guarantee"))),l.createElement("div",{className:s.Z.planFoot},l.createElement("button",{className:`btnPrimary ${s.Z.btnPlan}`,onClick:i},"Get started"))))))),l.createElement("div",{className:"contentBlockCta"},l.createElement("p",null,"(1) Subject to availabilty (2) When paid annually (3) Additional fees based on extension complexity"),l.createElement("p",null,"For on-prem support and bundling options, please"," ",l.createElement("a",{href:"mailto:sales@phasetwo.io"},"contact sales"),".")),l.createElement("div",{class:"contentBlockBody"},l.createElement("div",null,l.createElement("h2",{style:{textAlign:"center",marginTop:"3rem"}},"Phase Two are Keycloak Experts"," "),l.createElement("div",{className:s.Z.planBody,style:{maxWidth:"760px",margin:"0 auto"}},l.createElement("p",null,"Configuring, integrating and operating an Identity and Access Management system can be daunting."),l.createElement("p",null,"For both hosted, on-prem customers, or those with their own Keycloak deployment, our goal is to create an understanding in your organization of what is possible with Keycloak. We want to support your goals as you adopt and implement Keycloak in your products. Let us lend our expertise to every step of your journey."))),l.createElement("div",{className:s.Z.planSupport},l.createElement("div",{className:s.Z.plan},l.createElement("div",{className:s.Z.planHead},l.createElement("img",{className:s.Z.featCardPicto,src:"img/plan-community.svg",alt:"Enterprise plan"}),l.createElement("h3",null,"Enterprise Support Packages")),l.createElement("div",{className:s.Z.tableThemeWrapper},l.createElement("table",{className:s.Z.tableTheme},l.createElement("thead",null,l.createElement("tr",null,l.createElement("th",null),l.createElement("th",null,"Silver"),l.createElement("th",null,"Gold"))),l.createElement("tbody",null,E.map((e=>{let{feature:t,silver:a,gold:n}=e;return l.createElement("tr",null,l.createElement("td",null,t),l.createElement("td",null,a),l.createElement("td",null,n))}))))),l.createElement("div",{className:s.Z.planFoot},l.createElement("a",{href:"mailto:sales@phasetwo.io",className:s.Z.planFootA},l.createElement("button",{className:`btnPrimary ${s.Z.btnPlan}`,onClick:m},"Contact sales")))))))))}},94184:function(e,t){var a;!function(){"use strict";var l={}.hasOwnProperty;function n(){for(var e=[],t=0;tl.createElement("img",{src:"img/checkmark.svg",alt:"Checkmark"}),d=()=>l.createElement("span",{className:s.Z.notPartOfPlan},"\u2014"),E=[{feature:"Architecture review",silver:l.createElement(p,null),gold:l.createElement(p,null)},{feature:"Installation and configuration support",silver:l.createElement(p,null),gold:l.createElement(p,null)},{feature:"Email support",silver:l.createElement(p,null),gold:l.createElement(p,null)},{feature:"Slack support",silver:l.createElement(d,null),gold:l.createElement(p,null)},{feature:"Phone support",silver:l.createElement(d,null),gold:l.createElement(p,null)},{feature:l.createElement("span",null,"Support Hours ",l.createElement("span",{style:{opacity:.5}},"(US EST)")),silver:"9x5",gold:"24x7x365"},{feature:l.createElement("span",null,"Response time ",l.createElement("span",{style:{opacity:.5}},"(hours)")),silver:"24",gold:"4"},{feature:"Health assessment",silver:"Quarterly",gold:"Monthly"},{feature:l.createElement("span",null,"Incl. service hours ",l.createElement("span",{style:{opacity:.5}},"(/mth)")),silver:"10",gold:"20"}];t.default=function(){const e=(0,c.Z)(),{siteConfig:t={}}=e;return(0,l.useEffect)((()=>{document.body.classList.add("page-bg")})),l.createElement(n.Z,{description:`${t.tagline}`},l.createElement("picture",null,l.createElement("source",{media:"(max-width: 767px)",srcset:"/img/home-bg-mobile.jpg"}),l.createElement("source",{media:"(min-width: 768px)",srcset:"/img/home-bg.jpg"}),l.createElement("img",{className:"page-home",src:"/img/home-bg-mobile.jpg",alt:"Gradient Background"})),l.createElement("main",null,l.createElement("div",{className:"pageHero"},l.createElement("div",{className:s.Z.heroInner},l.createElement("div",{className:"pageHeroMsg"},l.createElement("h1",null,"Future-Proof Your App"),l.createElement("p",{className:"pageHeroMsgIntro"},"Accelerate SaaS time-to-market and enterprise adoption by rapidly integrating the features you need."),l.createElement("div",{className:"pageHeroCta"},l.createElement("button",{className:"btnCta",onClick:i},"Try Phase Two for Free")))),l.createElement("div",{className:s.Z.heroSections},l.createElement(r.Z,{to:"product/sso",className:s.Z.heroSection},l.createElement("img",{className:s.Z.heroSectionPicto,src:"img/picto-sso.svg",alt:"Pictogram showing key"}),l.createElement("p",null,"SSO"),l.createElement("div",{className:s.Z.heroSectionPlus},l.createElement("img",{src:"img/plus.svg",alt:"Plus"}))),l.createElement(r.Z,{to:"product/identity",className:s.Z.heroSection},l.createElement("img",{className:s.Z.heroSectionPicto,src:"img/picto-identity.svg",alt:"Pictogram showing a person"}),l.createElement("p",null,"Identity"),l.createElement("div",{className:s.Z.heroSectionPlus},l.createElement("img",{src:"img/plus.svg",alt:"Plus"}))),l.createElement(r.Z,{to:"product/organizations",className:s.Z.heroSection},l.createElement("img",{className:s.Z.heroSectionPicto,src:"img/picto-organizations.svg",alt:"Pictogram showing multiple persons interacting"}),l.createElement("p",null,"Organizations"),l.createElement("div",{className:s.Z.heroSectionPlus},l.createElement("img",{src:"img/plus.svg",alt:"Plus"}))),l.createElement(r.Z,{to:"product/adminportal",className:s.Z.heroSection},l.createElement("img",{className:s.Z.heroSectionPicto,src:"img/picto-admin-portal.svg",alt:"Pictogram showing a browser"}),l.createElement("p",null,"Admin Portal"),l.createElement("div",{className:s.Z.heroSectionPlus},l.createElement("img",{src:"img/plus.svg",alt:"Plus"}))),l.createElement(r.Z,{to:"product/onprem",className:s.Z.heroSection},l.createElement("img",{className:s.Z.heroSectionPicto,src:"img/picto-on-prem.svg",alt:"Pictogram showing on prem servers"}),l.createElement("p",null,"On-Prem Deployment"))),l.createElement("div",{className:s.Z.heroFeats},l.createElement("div",{className:s.Z.heroFeat},l.createElement("img",{src:"img/picto-open-source.svg",alt:"Open Source Logo"}),l.createElement("p",null,"We are open source")),l.createElement("div",{className:s.Z.heroFeat},l.createElement("img",{src:"img/picto-fixed-pricing.svg",alt:"Pictogram showing fixed US dollar sign"}),l.createElement("p",null,"Fixed pricing for peace of mind")),l.createElement("div",{className:s.Z.heroFeat},l.createElement("img",{src:"img/picto-cloud-or-on-prem.svg",alt:"Pictogran showing cloud and on-prem servers"}),l.createElement("p",null,"Cloud or on-prem deployment"))),l.createElement("div",{className:s.Z.heroIntegrations},l.createElement("p",null,"Integrates with"),l.createElement("picture",null,l.createElement("source",{media:"(max-width: 767px)",srcset:"/img/integration-lines-mobile.svg"}),l.createElement("source",{media:"(min-width: 768px)",srcset:"/img/integration-lines.svg"}),l.createElement("img",{className:s.Z.heroIntegrationsLines,src:"/img/integration-lines.svg",alt:"Integration Lines"})),l.createElement("div",{className:s.Z.heroIntegrationRow},l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-okta.svg",alt:"Okta Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-auth0.svg",alt:"Auth0 Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-azure.svg",alt:"Azure Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-google-workspace.svg",alt:"Google Workspace Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-active-directory.svg",alt:"Active Directory Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-jump-cloud.svg",alt:"JumpCloud Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-onelogin.svg",alt:"Onelogin Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-ping-identity.svg",alt:"Ping Identity Logo"})),l.createElement("div",{className:s.Z.heroIntegration},l.createElement("img",{src:"img/logo-duo-security.svg",alt:"Duo Security Logo"})),l.createElement("div",{className:`${s.Z.heroIntegration} ${s.Z.heroIntegrationMore}`},l.createElement("p",null,"+ many more"))))),l.createElement("div",{className:"contentBlock"},l.createElement("div",{className:"enterpriseSsoBgImg bgImg"},l.createElement("img",{src:"/img/enterprise-sso-bg.png",alt:"Color Gradient"})),l.createElement("div",{className:"contentBlockHead"},l.createElement("h2",null,"No-code Enterprise SSO"),l.createElement("p",null,"Leap up market into enterprise adoption with seamless SSO support.")),l.createElement("div",{className:"contentBlockBody"},l.createElement("div",{className:s.Z.enterpriseSSO},l.createElement("div",{className:s.Z.enterpriseSSOL},l.createElement("img",{className:s.Z.listFeatsImg,src:"img/hero-feature-sso.png",alt:"SSO Login Examples"})),l.createElement("div",{className:s.Z.enterpriseSSOR},l.createElement("ul",{className:s.Z.listFeats},l.createElement("li",null,l.createElement("img",{className:s.Z.listFeatsPicto,src:"img/picto-5-min-integration.svg",alt:"Pictogram showing 5 minutes on a hour"}),l.createElement("h5",null,"5-minute integration"),l.createElement("p",null,"One integration adds all enterprise identity providers. With or without adopting our identity feature, you can support all popular identity providers.")),l.createElement("li",null,l.createElement("img",{className:s.Z.listFeatsPicto,src:"img/picto-integrate-once.svg",alt:"Pictogram showing puzzle pieces"}),l.createElement("h5",null,"Integrate Once"),l.createElement("p",null,"SAML, OIDC, OAuth2? Support all the standards without years of development and debugging.")),l.createElement("li",null,l.createElement("img",{className:s.Z.listFeatsPicto,src:"img/picto-no-variable-cost.svg",alt:"Pictogram showing US dollar sign"}),l.createElement("h5",null,"No variable cost"),l.createElement("p",null,"We're not a parasite on your business model. Unlimited SSO connections for a single price."))))))),l.createElement("div",{className:"contentBlock"},l.createElement("div",{className:"contentBlockHead"},l.createElement("h2",null,"Admin Portal"),l.createElement("p",null,"Seamless onboarding and self-management for your customer administrators and users. Empower your users and customers to easily manage every aspect of identity, organization and SSO. Drastically reduce customer support.")),l.createElement("div",{className:"contentBlockBody"},l.createElement("div",{className:s.Z.aportal},l.createElement("picture",null,l.createElement("source",{media:"(max-width: 767px)",srcset:"/img/img-admin-portal-new1-mobile.png"}),l.createElement("source",{media:"(min-width: 768px)",srcset:"/img/img-admin-portal-new1.png"}),l.createElement("img",{src:"/img/img-admin-portal-new1.png",alt:"Screenshots showing management of users, domains and SSO"}))))),l.createElement("div",{className:"contentBlock"},l.createElement("div",{className:"contentBlockHead"},l.createElement("h2",null,"By Developers, For Developers"),l.createElement("p",null,"Create delightful, seamless experiences for your customers. In just a few minutes!")),l.createElement("div",{className:"contentBlockBody"},l.createElement("div",{className:s.Z.devs},l.createElement("div",{className:s.Z.devsL},l.createElement("ul",{className:s.Z.listFeats},l.createElement("li",null,l.createElement("img",{className:s.Z.listFeatsPicto,src:"img/picto-simple-integration.svg",alt:"Pictogram showing a code"}),l.createElement("h5",null,"Simple Integration"),l.createElement("p",null,"Our goal is to make it as easy as possible for developers to integrate with our system so they can add SSO and other features quickly and then move on to what's important\u2014their app!")),l.createElement("li",null,l.createElement("img",{className:s.Z.listFeatsPicto,src:"img/picto-documentation.svg",alt:"Pictogram showing documents"}),l.createElement("h5",null,"Full Documentation"),l.createElement("p",null,"We are building great documentation, tutorials and modern SDKs, so implementation is easy regardless of skill level or technology stack.")),l.createElement("li",null,l.createElement("img",{className:s.Z.listFeatsPicto,src:"img/picto-secure.svg",alt:"Pictogram showing a key inside the shield"}),l.createElement("h5",null,"Secure and Standardized"),l.createElement("p",null,"Standards compliance and security are our strengths so you can focus on your your customers.")))),l.createElement("div",{className:s.Z.devsR},l.createElement(o.Z,{language:"javascript",title:"Protect a page"},"var auth = new Keycloak({\n url: 'https://{host}/auth',\n realm: '{realm}',\n clientId: '{clientId}'\n});\nauth.init({\n onLoad: 'login-required'\n}).then(function(authenticated) {\n alert(authenticated ? 'authenticated' :\n 'not authenticated');\n}).catch(function() {\n alert('failed to initialize');\n});\n"))),l.createElement("div",{class:"contentBlockCta"},l.createElement("button",{className:"btnPrimary",onClick:u},"Go to Documentation")))),l.createElement("div",{className:"contentBlock"},l.createElement("div",{className:"keycloakBgCircles bgImg"},l.createElement("img",{src:"/img/circles.svg",alt:"Concentric Circles"})),l.createElement("div",{className:"contentBlockHead"},l.createElement("h2",null,"Phase Two"," ",l.createElement("img",{className:s.Z.heart,src:"img/heart-filled.svg",alt:"Heart symbols"})," ","Keycloak"),l.createElement("p",null,"Phase Two is based on the Keycloak Open Source Identity and Access Management system, built and maintained by Red Hat.")),l.createElement("div",{className:"contentBlockBody"},l.createElement("div",{className:s.Z.keycloak},l.createElement("img",{className:s.Z.keycloakImg,src:"img/diagram-keycloak.svg",alt:"Diagram showing how Keycloak works with Phase Two"})),l.createElement("div",{className:s.Z.featCards},l.createElement("div",{class:s.Z.featCard},l.createElement("img",{className:s.Z.featCardPicto,src:"img/picto-open-source-alt.svg",alt:"Pictogram showing Open Source logo"}),l.createElement("h5",null,"Always Open Source"),l.createElement("p",null,"Phase Two is built as a collection of open source Keycloak extensions. While we endeavor to make Keycloak simple to use, operate and scale, in the cloud or on prem.")),l.createElement("div",{class:s.Z.featCard},l.createElement("img",{className:s.Z.featCardPicto,src:"img/picto-hardened.svg",alt:"Pictogram showing a fortress"}),l.createElement("h5",null,"Battle-tested and hardened"),l.createElement("p",null,"Keycloak has been battle-tested and hardened for over 7 years. Its security and reliability is depended on by organizations from small startups to governments and Fortune 500 companies.")),l.createElement("div",{class:s.Z.featCard},l.createElement("img",{className:s.Z.featCardPicto,src:"img/picto-community.svg",alt:"Pictogram showing a group of poeple interconnected"}),l.createElement("h5",null,"Community Superpower"),l.createElement("p",null,"We believe that community participation in building our software is a superpower, and can't wait to see what you will help us build."))))),l.createElement("div",{id:"pricing",className:"contentBlock"},l.createElement("div",{className:"plansBgImg bgImg"},l.createElement("img",{src:"/img/gradient-bg.png",alt:"Color Gradient"})),l.createElement("div",{className:"contentBlockHead"},l.createElement("h2",null,"Phase Two is One Price Per Project"),l.createElement("p",null,"No hidden fees, no unpredictable costs.")),l.createElement("div",{className:"contentBlockBody"},l.createElement("div",{className:s.Z.plansBlocks},l.createElement("div",{className:s.Z.plansBlock},l.createElement("div",{className:s.Z.plans},l.createElement("div",{className:s.Z.plan},l.createElement("div",{className:s.Z.planHead},l.createElement("img",{className:s.Z.featCardPicto,src:"img/plan-starter.svg",alt:"Starter plan"}),l.createElement("h3",null,"Starter"),l.createElement("p",null,"Always FREE ",l.createElement("sup",null,"1"))),l.createElement("div",{className:s.Z.planBody},l.createElement("ul",{className:s.Z.checklist},l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Shared cluster"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"<1,000 users"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"<10 SSO connections"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Community support"),l.createElement("li",null,"No SLA"))),l.createElement("div",{className:s.Z.planFoot},l.createElement("button",{className:`btnPrimary ${s.Z.btnPlan}`,onClick:i},"Get started"),l.createElement("button",{className:`btnSecondary ${s.Z.btnPlan}`,onClick:g},"Self Host"))),l.createElement("div",{className:s.Z.plan},l.createElement("div",{className:s.Z.planBadge},"Most Popular"),l.createElement("div",{className:s.Z.planHead},l.createElement("img",{className:s.Z.featCardPicto,src:"img/plan-supported.svg",alt:"Premium plan"}),l.createElement("h3",null,"Premium"),l.createElement("p",null,l.createElement("span",{className:s.Z.planFrom},"from")," ",l.createElement("strong",{className:s.Z.planPrice},"$499"),"/mo"," ",l.createElement("sup",null,"2"))),l.createElement("div",{className:s.Z.planBody},l.createElement("ul",{className:s.Z.checklist},l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Dedicated cluster"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Unlimited users"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Unlimited SSO connections"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Custom domain"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Email support"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"99.9% uptime guarantee"))),l.createElement("div",{className:s.Z.planFoot},l.createElement("button",{className:`btnPrimary ${s.Z.btnPlan}`,onClick:i},"Get started"))),l.createElement("div",{className:s.Z.plan},l.createElement("div",{className:s.Z.planHead},l.createElement("img",{className:s.Z.featCardPicto,src:"img/plan-premium.svg",alt:"Enterprise plan"}),l.createElement("h3",null,"Enterprise"),l.createElement("p",null,l.createElement("span",{className:s.Z.planFrom},"from")," ",l.createElement("strong",{className:s.Z.planPrice},"$1999"),"/mo"," ",l.createElement("sup",null,"2"))),l.createElement("div",{className:s.Z.planBody},l.createElement("ul",{className:s.Z.checklist},l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"All Premium features"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Global deployment"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Custom themes & extensions ",l.createElement("sup",null,"3")),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"Dedicated support"),l.createElement("li",null,l.createElement("img",{className:s.Z.checklistIcon,src:"img/checkmark.svg",alt:"Checkmark"}),"99.99% uptime guarantee"))),l.createElement("div",{className:s.Z.planFoot},l.createElement("button",{className:`btnPrimary ${s.Z.btnPlan}`,onClick:i},"Get started"))))))),l.createElement("div",{className:"contentBlockCta"},l.createElement("p",null,"(1) Subject to availabilty (2) When paid annually (3) Additional fees based on extension complexity"),l.createElement("p",null,"For on-prem support and bundling options, please"," ",l.createElement("a",{href:"mailto:sales@phasetwo.io"},"contact sales"),".")),l.createElement("div",{class:"contentBlockBody"},l.createElement("div",null,l.createElement("h2",{style:{textAlign:"center",marginTop:"3rem"}},"Phase Two are Keycloak Experts"," "),l.createElement("div",{className:s.Z.planBody,style:{maxWidth:"760px",margin:"0 auto"}},l.createElement("p",null,"Configuring, integrating and operating an Identity and Access Management system can be daunting."),l.createElement("p",null,"For both hosted, on-prem customers, or those with their own Keycloak deployment, our goal is to create an understanding in your organization of what is possible with Keycloak. We want to support your goals as you adopt and implement Keycloak in your products. Let us lend our expertise to every step of your journey."))),l.createElement("div",{className:s.Z.planSupport},l.createElement("div",{className:s.Z.plan},l.createElement("div",{className:s.Z.planHead},l.createElement("img",{className:s.Z.featCardPicto,src:"img/plan-community.svg",alt:"Enterprise plan"}),l.createElement("h3",null,"Enterprise Support Packages"),l.createElement("p",null,l.createElement("span",{className:s.Z.planFrom},"from")," ",l.createElement("strong",{className:s.Z.planPrice},"$3500"),"/mo"," ")),l.createElement("div",{className:s.Z.tableThemeWrapper},l.createElement("table",{className:s.Z.tableTheme},l.createElement("thead",null,l.createElement("tr",null,l.createElement("th",null),l.createElement("th",null,"Silver"),l.createElement("th",null,"Gold"))),l.createElement("tbody",null,E.map((e=>{let{feature:t,silver:a,gold:n}=e;return l.createElement("tr",null,l.createElement("td",null,t),l.createElement("td",null,a),l.createElement("td",null,n))}))))),l.createElement("div",{className:s.Z.planFoot},l.createElement("a",{href:"mailto:sales@phasetwo.io",className:s.Z.planFootA},l.createElement("button",{className:`btnPrimary ${s.Z.btnPlan}`,onClick:m},"Contact sales")))))))))}},94184:function(e,t){var a;!function(){"use strict";var l={}.hasOwnProperty;function n(){for(var e=[],t=0;t=d)&&Object.keys(n.O).every((function(e){return n.O[e](f[r])}))?f.splice(r--,1):(t=!1,d0&&e[u-1][2]>d;u--)e[u]=e[u-1];e[u]=[f,c,d]},n.n=function(e){var a=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(a,{a:a}),a},f=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__},n.t=function(e,c){if(1&c&&(e=this(e)),8&c)return e;if("object"==typeof e&&e){if(4&c&&e.__esModule)return e;if(16&c&&"function"==typeof e.then)return e}var d=Object.create(null);n.r(d);var b={};a=a||[null,f({}),f([]),f(f)];for(var t=2&c&&e;"object"==typeof t&&!~a.indexOf(t);t=f(t))Object.getOwnPropertyNames(t).forEach((function(a){b[a]=function(){return e[a]}}));return b.default=function(){return e},n.d(d,b),d},n.d=function(e,a){for(var f in a)n.o(a,f)&&!n.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},n.f={},n.e=function(e){return Promise.all(Object.keys(n.f).reduce((function(a,f){return n.f[f](e,a),a}),[]))},n.u=function(e){return"assets/js/"+({1:"8eb4e46b",25:"ab229c23",53:"935f2afb",55:"9eef18c5",99:"df663ac2",205:"83d480e9",252:"3795eec1",282:"da3073e7",297:"57717c00",515:"8dda653b",533:"b2b675dd",580:"1c51c605",598:"9807932d",757:"e70b8d1a",762:"f111dd4f",788:"137a0099",919:"9118b0ee",1005:"b339536e",1016:"5b5893bb",1157:"93c90cee",1173:"73d697cf",1233:"3d763b64",1296:"c4e095be",1339:"6925fd17",1343:"d0542246",1411:"ea8d0725",1466:"e9cd6c0a",1467:"4f92cac8",1477:"b2f554cd",1575:"6add8938",1625:"dd01b20a",1633:"1fadf4c6",1639:"9040041d",1646:"186bfc7b",1679:"54d9c174",1706:"c60d0e0d",1713:"a7023ddc",1724:"f76a685d",1790:"6a832b3f",1819:"eb46cfb5",1830:"a4cc42e0",1893:"4c5e977b",1969:"b651ea14",2089:"945c690d",2140:"3a7386de",2183:"ea93f2af",2195:"09ce9b62",2237:"c9a45575",2249:"68342485",2260:"fcc698e4",2304:"e6c29f14",2307:"bdff1965",2310:"baff0a6b",2324:"a5d99512",2340:"ea728368",2345:"9d02fae6",2370:"e5bba11b",2413:"162fdd78",2419:"969f074d",2478:"888c7064",2486:"1e31935b",2535:"814f3328",2582:"e714bb11",2614:"7f0ba2df",2639:"92560c8e",2652:"0be1204c",2677:"b93a927b",2686:"60e983a0",2742:"99a38c43",2775:"9b4185c1",2798:"9ce5be07",2816:"ddaf1d48",2864:"93942ebf",2941:"865b3da8",2943:"4d55c776",3089:"a6aa9e1f",3102:"5259fb4f",3125:"a236c681",3139:"807775e3",3190:"61f84ec3",3249:"21569584",3274:"5ef63d7c",3296:"6a13dacc",3432:"62a2d5e6",3436:"009f1e98",3457:"6a12d974",3512:"9a196c06",3531:"77095dad",3561:"80e32587",3595:"29ecd872",3608:"9e4087bc",3648:"4944721f",3652:"a5271b1b",3705:"6595c1a2",3804:"28115c64",3875:"31dbfbe7",3924:"6d85a02a",3942:"c540a7be",3998:"39554bfb",4013:"01a85c17",4033:"3af9437e",4125:"9a8fe877",4156:"64e125c9",4195:"c4f5d8e4",4321:"2b2bbb12",4326:"262675f7",4339:"43c94e8f",4372:"03009c40",4389:"9fff42f2",4396:"06fd0e28",4426:"0432f530",4441:"8de67ec2",4473:"4ef576ed",4475:"883c4d4f",4496:"098d0416",4526:"4815beb9",4545:"500a151f",4647:"c1fc6b11",4657:"70e17e24",4961:"182be7b3",4996:"d9df0bf6",5022:"06fa610c",5074:"77e23114",5140:"e976718f",5153:"5af0b08d",5159:"16cd130e",5180:"cdf9e396",5237:"b055b563",5305:"e3164b02",5311:"b944a966",5391:"369bd8f8",5408:"9e6f51b1",5431:"35d67a70",5470:"ecef1771",5516:"468f1317",5563:"0d88c12b",5587:"1a454eb9",5606:"a297020e",5657:"d0918020",5683:"f261cbff",5684:"a22951ee",5715:"5957b5de",5784:"5c1092b8",5789:"ddca3424",5897:"baeff1df",5901:"b4978fb9",5911:"f003a149",5949:"0687c47b",6011:"6eda3b8c",6037:"638ccf9c",6092:"dbdbec15",6098:"f91913ad",6103:"ccc49370",6169:"acac9cec",6174:"d04e4ddb",6216:"3ca151e4",6228:"84ba7ff4",6246:"864c2fde",6266:"2552aaea",6295:"ecd5d374",6321:"26d9f733",6329:"54c82979",6470:"3b144cae",6535:"3d8d21df",6551:"654eb3dd",6555:"bdbe8576",6560:"9a59e99d",6585:"91adab7a",6664:"cb47d9be",6672:"271c0664",6699:"4121287c",6700:"76d5d095",6721:"344b1943",6728:"3a3fe3dc",6761:"8988fa66",6783:"35e2f66e",6929:"d649951a",6961:"178e42b5",6976:"b6be3935",6998:"bcf25100",7015:"72a256a3",7121:"1a74496b",7155:"36bfddb4",7173:"f846590a",7322:"d732aeea",7324:"a947efd2",7338:"ef463254",7360:"a8474035",7411:"ea0ad9d6",7430:"8ea0d94e",7438:"9c021584",7530:"b7d8a8a4",7593:"af03536b",7597:"5e8c322a",7603:"0524ee09",7607:"3236be8b",7621:"1a8d9042",7660:"f4f2dadf",7777:"285a417f",7869:"6b0d6ba0",7893:"b554f5df",7918:"17896441",7920:"1a4e3797",7978:"a7ede793",7981:"5edc3306",8009:"52746141",8040:"ec27389b",8052:"303a773f",8080:"b021ad11",8134:"93335ba7",8161:"cf84a251",8178:"7b550aa7",8191:"29c87737",8229:"04babf9e",8382:"02d76774",8388:"0b8f31a9",8399:"eff04e5e",8442:"92999a1c",8504:"4afaba6f",8508:"086326e0",8558:"ac13a92a",8567:"f2b967d0",8599:"91fdfcd5",8610:"6875c492",8693:"b108cff2",8757:"47365e45",8769:"d0c61f26",8770:"cf5d009b",8772:"606027f0",8792:"ef01e626",8794:"8109af0e",8861:"69ecb8f2",8949:"81b45556",8972:"f7e5353b",8981:"5bf5c691",9078:"1f4c3064",9090:"4c9288f5",9115:"8a6a3abd",9143:"65516ce3",9149:"44d845c6",9169:"51e4ac6f",9183:"d93f4896",9187:"0ed9d336",9196:"9ae7587c",9198:"464833fa",9284:"4b1bb679",9310:"f526d2c4",9362:"1cda0596",9514:"1be78505",9564:"e25fcc0b",9752:"d3f7b578",9817:"14eb3368",9848:"986f7180",9893:"fbca6c8d",9902:"42303469",9909:"8f29052d"}[e]||e)+"."+{1:"5855950f",25:"db9b7367",53:"fb8ea56c",55:"6b755e12",99:"2234e66b",205:"cae104c6",252:"5be24ea0",282:"b1e3b277",297:"bc621c0b",515:"0f52beb4",533:"30d4e54c",580:"a3edf2a0",598:"7a7b28bb",615:"e858c932",757:"988344c1",762:"105a8a47",788:"d9f69299",844:"47bd4263",919:"b8561054",1005:"7bd558e1",1016:"de3bad75",1157:"91815884",1173:"cbb86659",1233:"c903e720",1296:"431ac402",1339:"48c4e3e6",1343:"ed0d64f6",1411:"fdc3922b",1466:"82c131cd",1467:"0ba089c3",1477:"a4b7814b",1575:"6916a50f",1625:"a9ff39f2",1633:"2357c289",1639:"ad96c94e",1646:"dcf72089",1679:"fbe7e2aa",1706:"1f3f6d48",1713:"73615047",1724:"014586bb",1790:"ccf54edd",1819:"dd20255d",1830:"97f59961",1893:"8793cd49",1969:"3752410b",2089:"bb9fb8c7",2139:"8c100866",2140:"8c27a5ca",2183:"5fdb75f4",2195:"4b3a301f",2237:"ce8daba6",2249:"e3f9ce60",2260:"33616b96",2304:"86add60f",2307:"0be63106",2310:"f75d3921",2324:"683c540e",2340:"76347a0e",2345:"1db7f3c0",2370:"12c74a94",2403:"05e426d5",2413:"20b84d93",2419:"37b667ea",2478:"4af3c667",2486:"ddf248cc",2535:"69abace1",2582:"17c6f59c",2614:"47017c5e",2639:"19bb115e",2652:"2f1c253c",2677:"dc5ca6b7",2686:"b931b7a5",2742:"4b5b8e80",2775:"bc7b20eb",2798:"9eb46b4e",2816:"2cf08678",2864:"f8c09ba9",2941:"f2ecb40b",2943:"f40db809",3089:"f042b7b8",3102:"ca9a7c50",3125:"3c70736d",3139:"9833bd09",3190:"6ae73f27",3249:"b3ca73ac",3274:"d0aa0ec1",3296:"08122cca",3432:"acab9215",3436:"cd240f14",3457:"6ad668ac",3512:"59e0a1ee",3525:"5b60a8ad",3531:"df08ef11",3561:"56a6e868",3595:"5036a39e",3608:"f25726cf",3648:"aea44bb7",3652:"0d222c7c",3705:"ad77536e",3764:"917edc90",3804:"93706603",3875:"193b2e61",3924:"77478470",3942:"9e8e13a2",3998:"2a053e8c",4013:"a42c90bb",4033:"d31f8b88",4125:"a21f9877",4156:"1f5d92a3",4162:"8943152c",4195:"92f7193e",4321:"052365f2",4326:"41ed9786",4339:"d8284dc8",4372:"7e8cbffd",4389:"df3fa385",4396:"6e632471",4426:"0c2cc35b",4441:"d0e1a800",4473:"f7bac276",4475:"beec2b1d",4496:"2aa15695",4526:"19d47b71",4545:"89c58ffb",4647:"81171c30",4657:"7e79dd51",4961:"3cb696ce",4972:"8b9239bf",4996:"6337f2ec",5022:"16888e86",5074:"3dfbd3b2",5140:"1e56ae10",5153:"3f2d04aa",5159:"015fb76c",5180:"4c1bdf85",5237:"48116430",5305:"b326d013",5311:"821d0a80",5391:"01929d15",5408:"92502cac",5431:"13b1788f",5470:"edb75d88",5516:"5ffa6a5e",5563:"1f5d2967",5587:"8c0dcaa9",5606:"81742cc2",5657:"8cf069e4",5683:"0ac93801",5684:"4e552d22",5715:"87ce1162",5784:"219c278b",5789:"98ae330e",5897:"f0b4996d",5901:"1275ad15",5911:"d6c03377",5949:"6a5f468b",6011:"288cd000",6037:"912c333b",6092:"f47f2968",6098:"4674e8ae",6103:"55a9fc0e",6169:"494ab597",6174:"51c2c131",6216:"f909bbf1",6228:"3bf5ccbe",6246:"d5975880",6266:"77485c7e",6295:"88a6cc6e",6321:"e49327d0",6329:"5bae61af",6470:"80ca4f3d",6535:"454cff13",6551:"a6b222e3",6555:"bce4e460",6560:"55db811b",6585:"79c53e3a",6630:"edbd6dc7",6664:"4bbb1e73",6672:"4441f674",6699:"035eb1d0",6700:"78b44ed3",6721:"627f8a38",6728:"3c5ce69d",6761:"ae76b2a6",6780:"4c7cc4fb",6783:"159ad482",6882:"4706f3c2",6929:"94f9932d",6945:"f057b342",6961:"04c28bc1",6976:"66d62890",6998:"b3cfdb5e",7015:"20e62ebb",7121:"f62c5160",7155:"3d819c9e",7173:"9c1e57b8",7322:"5364530f",7324:"ebcaec2d",7338:"a72a3ae7",7360:"364b03d2",7411:"a8678276",7430:"7d02f2ba",7438:"be2bfaa2",7530:"d7a31d88",7593:"1845e96e",7597:"4408a898",7603:"b990ca5b",7607:"bc1f1be1",7621:"80da8125",7660:"304cab1c",7777:"64dcfe7d",7869:"0f116aa3",7893:"8aa9b3db",7918:"a7f0c901",7920:"9c1ffe06",7978:"7ab956d0",7981:"37c53346",8009:"b059d219",8040:"2d413f24",8052:"1e52d0b7",8080:"74f0c5c5",8134:"4058340a",8161:"f3a56085",8178:"932dab10",8191:"76b269bb",8229:"c097f782",8382:"19a0236b",8383:"a698c1e7",8388:"0aab173d",8399:"ae304523",8442:"b0936543",8504:"4eddcc37",8508:"1cf12688",8558:"1c8f91fc",8567:"3ccce675",8599:"fb5b809d",8610:"2ec2b38d",8693:"9bee2495",8757:"ace737c7",8769:"3148ae46",8770:"8af4fca3",8772:"b96151da",8792:"ef88e7de",8794:"b52f51e1",8861:"4437b2d0",8894:"a7561097",8949:"3507347e",8972:"3ef2fb7d",8981:"83c3c868",9078:"270addf9",9090:"0edf9b4b",9115:"3c1c1743",9143:"771e66e7",9149:"4770cf01",9169:"838d8885",9183:"efc10ea4",9187:"3c4336cd",9196:"4e6d8d71",9198:"4a934d64",9284:"d645c32e",9310:"9f55c791",9362:"8ea4d415",9514:"1d2e02db",9564:"98efb6dc",9752:"50fb7285",9817:"2bd60e9e",9848:"f9091e36",9893:"4412be45",9902:"5e92f917",9909:"03a26695"}[e]+".js"},n.miniCssF=function(e){},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=function(e,a){return Object.prototype.hasOwnProperty.call(e,a)},c={},d="phasetwo-docs:",n.l=function(e,a,f,b){if(c[e])c[e].push(a);else{var t,r;if(void 0!==f)for(var o=document.getElementsByTagName("script"),u=0;u=d)&&Object.keys(n.O).every((function(e){return n.O[e](f[r])}))?f.splice(r--,1):(t=!1,d0&&e[u-1][2]>d;u--)e[u]=e[u-1];e[u]=[f,c,d]},n.n=function(e){var a=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(a,{a:a}),a},f=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__},n.t=function(e,c){if(1&c&&(e=this(e)),8&c)return e;if("object"==typeof e&&e){if(4&c&&e.__esModule)return e;if(16&c&&"function"==typeof e.then)return e}var d=Object.create(null);n.r(d);var b={};a=a||[null,f({}),f([]),f(f)];for(var t=2&c&&e;"object"==typeof t&&!~a.indexOf(t);t=f(t))Object.getOwnPropertyNames(t).forEach((function(a){b[a]=function(){return e[a]}}));return b.default=function(){return e},n.d(d,b),d},n.d=function(e,a){for(var f in a)n.o(a,f)&&!n.o(e,f)&&Object.defineProperty(e,f,{enumerable:!0,get:a[f]})},n.f={},n.e=function(e){return Promise.all(Object.keys(n.f).reduce((function(a,f){return n.f[f](e,a),a}),[]))},n.u=function(e){return"assets/js/"+({1:"8eb4e46b",25:"ab229c23",53:"935f2afb",55:"9eef18c5",99:"df663ac2",205:"83d480e9",252:"3795eec1",282:"da3073e7",297:"57717c00",515:"8dda653b",533:"b2b675dd",580:"1c51c605",598:"9807932d",757:"e70b8d1a",762:"f111dd4f",788:"137a0099",919:"9118b0ee",1005:"b339536e",1016:"5b5893bb",1157:"93c90cee",1173:"73d697cf",1233:"3d763b64",1296:"c4e095be",1339:"6925fd17",1343:"d0542246",1411:"ea8d0725",1466:"e9cd6c0a",1467:"4f92cac8",1477:"b2f554cd",1575:"6add8938",1625:"dd01b20a",1633:"1fadf4c6",1639:"9040041d",1646:"186bfc7b",1679:"54d9c174",1706:"c60d0e0d",1713:"a7023ddc",1724:"f76a685d",1790:"6a832b3f",1819:"eb46cfb5",1830:"a4cc42e0",1893:"4c5e977b",1969:"b651ea14",2089:"945c690d",2140:"3a7386de",2183:"ea93f2af",2195:"09ce9b62",2237:"c9a45575",2249:"68342485",2260:"fcc698e4",2304:"e6c29f14",2307:"bdff1965",2310:"baff0a6b",2324:"a5d99512",2340:"ea728368",2345:"9d02fae6",2370:"e5bba11b",2413:"162fdd78",2419:"969f074d",2478:"888c7064",2486:"1e31935b",2535:"814f3328",2582:"e714bb11",2614:"7f0ba2df",2639:"92560c8e",2652:"0be1204c",2677:"b93a927b",2686:"60e983a0",2742:"99a38c43",2775:"9b4185c1",2798:"9ce5be07",2816:"ddaf1d48",2864:"93942ebf",2941:"865b3da8",2943:"4d55c776",3089:"a6aa9e1f",3102:"5259fb4f",3125:"a236c681",3139:"807775e3",3190:"61f84ec3",3249:"21569584",3274:"5ef63d7c",3296:"6a13dacc",3432:"62a2d5e6",3436:"009f1e98",3457:"6a12d974",3512:"9a196c06",3531:"77095dad",3561:"80e32587",3595:"29ecd872",3608:"9e4087bc",3648:"4944721f",3652:"a5271b1b",3705:"6595c1a2",3804:"28115c64",3875:"31dbfbe7",3924:"6d85a02a",3942:"c540a7be",3998:"39554bfb",4013:"01a85c17",4033:"3af9437e",4125:"9a8fe877",4156:"64e125c9",4195:"c4f5d8e4",4321:"2b2bbb12",4326:"262675f7",4339:"43c94e8f",4372:"03009c40",4389:"9fff42f2",4396:"06fd0e28",4426:"0432f530",4441:"8de67ec2",4473:"4ef576ed",4475:"883c4d4f",4496:"098d0416",4526:"4815beb9",4545:"500a151f",4647:"c1fc6b11",4657:"70e17e24",4961:"182be7b3",4996:"d9df0bf6",5022:"06fa610c",5074:"77e23114",5140:"e976718f",5153:"5af0b08d",5159:"16cd130e",5180:"cdf9e396",5237:"b055b563",5305:"e3164b02",5311:"b944a966",5391:"369bd8f8",5408:"9e6f51b1",5431:"35d67a70",5470:"ecef1771",5516:"468f1317",5563:"0d88c12b",5587:"1a454eb9",5606:"a297020e",5657:"d0918020",5683:"f261cbff",5684:"a22951ee",5715:"5957b5de",5784:"5c1092b8",5789:"ddca3424",5897:"baeff1df",5901:"b4978fb9",5911:"f003a149",5949:"0687c47b",6011:"6eda3b8c",6037:"638ccf9c",6092:"dbdbec15",6098:"f91913ad",6103:"ccc49370",6169:"acac9cec",6174:"d04e4ddb",6216:"3ca151e4",6228:"84ba7ff4",6246:"864c2fde",6266:"2552aaea",6295:"ecd5d374",6321:"26d9f733",6329:"54c82979",6470:"3b144cae",6535:"3d8d21df",6551:"654eb3dd",6555:"bdbe8576",6560:"9a59e99d",6585:"91adab7a",6664:"cb47d9be",6672:"271c0664",6699:"4121287c",6700:"76d5d095",6721:"344b1943",6728:"3a3fe3dc",6761:"8988fa66",6783:"35e2f66e",6929:"d649951a",6961:"178e42b5",6976:"b6be3935",6998:"bcf25100",7015:"72a256a3",7121:"1a74496b",7155:"36bfddb4",7173:"f846590a",7322:"d732aeea",7324:"a947efd2",7338:"ef463254",7360:"a8474035",7411:"ea0ad9d6",7430:"8ea0d94e",7438:"9c021584",7530:"b7d8a8a4",7593:"af03536b",7597:"5e8c322a",7603:"0524ee09",7607:"3236be8b",7621:"1a8d9042",7660:"f4f2dadf",7777:"285a417f",7869:"6b0d6ba0",7893:"b554f5df",7918:"17896441",7920:"1a4e3797",7978:"a7ede793",7981:"5edc3306",8009:"52746141",8040:"ec27389b",8052:"303a773f",8080:"b021ad11",8134:"93335ba7",8161:"cf84a251",8178:"7b550aa7",8191:"29c87737",8229:"04babf9e",8382:"02d76774",8388:"0b8f31a9",8399:"eff04e5e",8442:"92999a1c",8504:"4afaba6f",8508:"086326e0",8558:"ac13a92a",8567:"f2b967d0",8599:"91fdfcd5",8610:"6875c492",8693:"b108cff2",8757:"47365e45",8769:"d0c61f26",8770:"cf5d009b",8772:"606027f0",8792:"ef01e626",8794:"8109af0e",8861:"69ecb8f2",8949:"81b45556",8972:"f7e5353b",8981:"5bf5c691",9078:"1f4c3064",9090:"4c9288f5",9115:"8a6a3abd",9143:"65516ce3",9149:"44d845c6",9169:"51e4ac6f",9183:"d93f4896",9187:"0ed9d336",9196:"9ae7587c",9198:"464833fa",9284:"4b1bb679",9310:"f526d2c4",9362:"1cda0596",9514:"1be78505",9564:"e25fcc0b",9752:"d3f7b578",9817:"14eb3368",9848:"986f7180",9893:"fbca6c8d",9902:"42303469",9909:"8f29052d"}[e]||e)+"."+{1:"5855950f",25:"db9b7367",53:"fb8ea56c",55:"6b755e12",99:"2234e66b",205:"cae104c6",252:"5be24ea0",282:"b1e3b277",297:"bc621c0b",515:"0f52beb4",533:"30d4e54c",580:"a3edf2a0",598:"7a7b28bb",615:"e858c932",757:"988344c1",762:"105a8a47",788:"d9f69299",844:"47bd4263",919:"b8561054",1005:"7bd558e1",1016:"de3bad75",1157:"91815884",1173:"cbb86659",1233:"c903e720",1296:"431ac402",1339:"48c4e3e6",1343:"ed0d64f6",1411:"fdc3922b",1466:"82c131cd",1467:"0ba089c3",1477:"a4b7814b",1575:"6916a50f",1625:"a9ff39f2",1633:"2357c289",1639:"ad96c94e",1646:"dcf72089",1679:"fbe7e2aa",1706:"1f3f6d48",1713:"73615047",1724:"014586bb",1790:"ccf54edd",1819:"dd20255d",1830:"97f59961",1893:"8793cd49",1969:"3752410b",2089:"bb9fb8c7",2139:"8c100866",2140:"8c27a5ca",2183:"5fdb75f4",2195:"4b3a301f",2237:"ce8daba6",2249:"e3f9ce60",2260:"33616b96",2304:"86add60f",2307:"0be63106",2310:"f75d3921",2324:"683c540e",2340:"76347a0e",2345:"1db7f3c0",2370:"12c74a94",2403:"05e426d5",2413:"20b84d93",2419:"37b667ea",2478:"4af3c667",2486:"ddf248cc",2535:"69abace1",2582:"17c6f59c",2614:"47017c5e",2639:"19bb115e",2652:"2f1c253c",2677:"dc5ca6b7",2686:"b931b7a5",2742:"4b5b8e80",2775:"bc7b20eb",2798:"9eb46b4e",2816:"2cf08678",2864:"f8c09ba9",2941:"f2ecb40b",2943:"f40db809",3089:"f042b7b8",3102:"ca9a7c50",3125:"3c70736d",3139:"9833bd09",3190:"6ae73f27",3249:"b3ca73ac",3274:"d0aa0ec1",3296:"08122cca",3432:"acab9215",3436:"cd240f14",3457:"6ad668ac",3512:"59e0a1ee",3525:"5b60a8ad",3531:"df08ef11",3561:"56a6e868",3595:"5036a39e",3608:"f25726cf",3648:"aea44bb7",3652:"0d222c7c",3705:"ad77536e",3764:"917edc90",3804:"93706603",3875:"193b2e61",3924:"77478470",3942:"9e8e13a2",3998:"2a053e8c",4013:"a42c90bb",4033:"d31f8b88",4125:"a21f9877",4156:"1f5d92a3",4162:"8943152c",4195:"17f06b3c",4321:"052365f2",4326:"41ed9786",4339:"d8284dc8",4372:"7e8cbffd",4389:"df3fa385",4396:"6e632471",4426:"0c2cc35b",4441:"d0e1a800",4473:"f7bac276",4475:"beec2b1d",4496:"2aa15695",4526:"19d47b71",4545:"89c58ffb",4647:"81171c30",4657:"7e79dd51",4961:"3cb696ce",4972:"8b9239bf",4996:"6337f2ec",5022:"16888e86",5074:"3dfbd3b2",5140:"1e56ae10",5153:"3f2d04aa",5159:"015fb76c",5180:"4c1bdf85",5237:"48116430",5305:"b326d013",5311:"821d0a80",5391:"01929d15",5408:"92502cac",5431:"13b1788f",5470:"edb75d88",5516:"5ffa6a5e",5563:"1f5d2967",5587:"8c0dcaa9",5606:"81742cc2",5657:"8cf069e4",5683:"0ac93801",5684:"4e552d22",5715:"87ce1162",5784:"219c278b",5789:"98ae330e",5897:"f0b4996d",5901:"1275ad15",5911:"d6c03377",5949:"6a5f468b",6011:"288cd000",6037:"912c333b",6092:"f47f2968",6098:"4674e8ae",6103:"55a9fc0e",6169:"494ab597",6174:"51c2c131",6216:"f909bbf1",6228:"3bf5ccbe",6246:"d5975880",6266:"77485c7e",6295:"88a6cc6e",6321:"e49327d0",6329:"5bae61af",6470:"80ca4f3d",6535:"454cff13",6551:"a6b222e3",6555:"bce4e460",6560:"55db811b",6585:"79c53e3a",6630:"edbd6dc7",6664:"4bbb1e73",6672:"4441f674",6699:"035eb1d0",6700:"78b44ed3",6721:"627f8a38",6728:"3c5ce69d",6761:"ae76b2a6",6780:"4c7cc4fb",6783:"159ad482",6882:"4706f3c2",6929:"94f9932d",6945:"f057b342",6961:"04c28bc1",6976:"66d62890",6998:"b3cfdb5e",7015:"20e62ebb",7121:"f62c5160",7155:"3d819c9e",7173:"9c1e57b8",7322:"5364530f",7324:"ebcaec2d",7338:"a72a3ae7",7360:"364b03d2",7411:"a8678276",7430:"7d02f2ba",7438:"be2bfaa2",7530:"d7a31d88",7593:"1845e96e",7597:"4408a898",7603:"b990ca5b",7607:"bc1f1be1",7621:"80da8125",7660:"304cab1c",7777:"64dcfe7d",7869:"0f116aa3",7893:"8aa9b3db",7918:"a7f0c901",7920:"9c1ffe06",7978:"7ab956d0",7981:"37c53346",8009:"b059d219",8040:"2d413f24",8052:"1e52d0b7",8080:"74f0c5c5",8134:"4058340a",8161:"f3a56085",8178:"932dab10",8191:"76b269bb",8229:"c097f782",8382:"19a0236b",8383:"a698c1e7",8388:"0aab173d",8399:"ae304523",8442:"b0936543",8504:"4eddcc37",8508:"1cf12688",8558:"1c8f91fc",8567:"3ccce675",8599:"fb5b809d",8610:"2ec2b38d",8693:"9bee2495",8757:"ace737c7",8769:"3148ae46",8770:"8af4fca3",8772:"b96151da",8792:"ef88e7de",8794:"b52f51e1",8861:"4437b2d0",8894:"a7561097",8949:"3507347e",8972:"3ef2fb7d",8981:"83c3c868",9078:"270addf9",9090:"0edf9b4b",9115:"3c1c1743",9143:"771e66e7",9149:"4770cf01",9169:"838d8885",9183:"efc10ea4",9187:"3c4336cd",9196:"4e6d8d71",9198:"4a934d64",9284:"d645c32e",9310:"9f55c791",9362:"8ea4d415",9514:"1d2e02db",9564:"98efb6dc",9752:"50fb7285",9817:"2bd60e9e",9848:"f9091e36",9893:"4412be45",9902:"5e92f917",9909:"03a26695"}[e]+".js"},n.miniCssF=function(e){},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=function(e,a){return Object.prototype.hasOwnProperty.call(e,a)},c={},d="phasetwo-docs:",n.l=function(e,a,f,b){if(c[e])c[e].push(a);else{var t,r;if(void 0!==f)for(var o=document.getElementsByTagName("script"),u=0;ufunction gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","UA-160183620-1",{}) - - + + @@ -21,7 +21,7 @@ - + \ No newline at end of file diff --git a/blog/connect/index.html b/blog/connect/index.html index 05bb5946d..668bd69f7 100644 --- a/blog/connect/index.html +++ b/blog/connect/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Introducing Phase Two Connect

    · One min read

    Following our post about our wizard product, we received an overwhelming amount of interest in it. Many customers of our cloud offering asked for it as a portal for their organization administrators to set up their identity providers. On-prem customers said that one consistent onboarding hurdle was SSO complexity, and asked for it to be included in the bundled distribution.

    Today we're pleased to report that we've listened to both use cases and completed embedding the "wizard" product into Phase Two. We're calling it "Connect", as it's the best way we could come up with characterizing its simplicity. It massively reduces the complexit of configuring SSO connections, and distills the process into something any member of the team can understand.

    Phase Two Connect is currently available by invitation only while we work out the final kinks. Contact sales for more information.

    - + \ No newline at end of file diff --git a/blog/customizing-login-pages/index.html b/blog/customizing-login-pages/index.html index 5296f5f19..9a341145b 100644 --- a/blog/customizing-login-pages/index.html +++ b/blog/customizing-login-pages/index.html @@ -12,8 +12,8 @@ - - + + @@ -24,7 +24,7 @@

    Images and other assets

    You'll notice that images, icons and fonts not found in the standard Keycloak themes are used. These are referenced by URL in the CSS. For example, if you are used to loading a custom font in the <head> of your HTML, it is possible to do it in CSS using @import:

    @import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Lexend+Deca:wght@300;400;500;600;700&family=Lexend:wght@300;400;500;600;700&family=Work+Sans:wght@300;400;500;600;700&display=swap");

    And background and other images can be referenced by their full URL using url(...):

    .login-pf body {
    background-image: url("https://raw.githubusercontent.com/p2-inc/keycloak-themes/main/examples/saas/assets/SaaS%20BG.webp");
    }

    Success!

    As always, our success is based on the success of our customers. We hope this extension and guide has helped you update the default Keycloak login branding to match that of your needs. If you have suggestions for further improvement of this feature, please reach out on GitHub!

    - + \ No newline at end of file diff --git a/blog/dedicated-launch/index.html b/blog/dedicated-launch/index.html index a0c59d016..185755e1a 100644 --- a/blog/dedicated-launch/index.html +++ b/blog/dedicated-launch/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Launching Dedicated Clusters

    · 4 min read

    We're excited today to announce the launch of our dedicated clusters offering. Our Phase Two enhanced Keycloak distribution is now available as a hosted, dedicated cluster in the region of your choice.

    About 9 months ago, we launched our self-service, shared deployments, offering customers the ability to create Phase Two enhanced Keycloak realms on our shared clusters. Over that period, we've provided over 700 free realms for testing and small production use cases. Many of you have reached out to us asking about an SLA, isolated resources, and ability to grow into larger use cases. Based on your requests and feedback, we built out our dedicated cluster offering.

    What is Phase Two again?

    Phase Two helps SaaS builders accelerate time-to-market and enterprise adoption with powerful SSO, identity and user management features. To that end, Phase Two has created an enhanced distribution of Keycloak that bundles several essential open source extensions for modern SaaS use cases. We support hosted and on-prem customers for a variety of use cases.

    Why dedicated?

    Dedicated clusters allow us to provide compute, network and storage isolation for customer workloads, and to easily deploy in the region where customer's users are. With dedicated clusters, we can guarantee an SLA that meets customer's needs with no resource contention from other customers.

    Furthermore, it allows us to support customer-provided domain names, and access to monitoring and management capabilities not available in the shared clusters.

    How did you do it?

    We built out our dedicated cluster offering using the best-of-breed open source tools and managed services.

    The core consists of Kubernetes clusters in each supported AWS and GCP region. New dedicated clusters are provisioned instantly using FluxCD, a continuous delivery solution that gives us the history and auditability of git. Monitoring and alerting is done using Prometheus, Grafana, and a suite of external services that give us a complete view of cluster health.

    The database tier uses a managed CockroachDB service provided by Cockroach Labs. Phase Two is the only provider that is capable of hosting the current Keycloak distribution (the "legacy" store) using CockroachDB. Working with Cockroach Labs gives us the expertise and reliability from hosting thousands of customer clusters at massive scale.

    Take me to it!

    Starting today, you'll be able to log into the updated dashboard, and create your dedicated cluster by selecting a region and setting up your billing information.

    Following successful billing setup, you will be returned to the Dashboard while your Cluster is provisioned. Once provisioned, you'll be able to create up to 20 realms per cluster, using the same easy setup as you are used to in the shared deployment offering.

    Most clusters will be provisioned within 30 minutes, but some requests may take up to 24 hours. Additionally, for payment types such as ACH or SEPA, cluster provisioning will begin following payment clearing (up to 4 days in some cases).

    How much does it cost?

    Plans start at $499US per month when paid annually. This provides a set of compute, network and storage resources that have been tested for common use cases for up to 20 realms and 1 million users. If your use case is uncommon or you plan to scale beyond that, our system is designed to scale up with your needs. Our plans scale linearly with resource demands beyond our minimums.

    We will continue to support a robust Free tier.

    What's next? (psst... Global clusters)

    Many of our on-prem, suppport customers with large use cases asked us to build out a solution for massive-scale, fault-tolerant, multi-region use cases. One of the great parts of building support for Keycloak's existing storage system for CockroacDB is that we've been able to explore use cases that were previously impossible using the standard Keycloak distribution, or required complex, error-prone configurations. For use cases with these requirements, plus global proximity to users and regional failover, we built global clusters, backed by CockroachDB multi-region database, for which we are now in beta.

    These clusters provide a minimum of 3 global regions, with 3 instances of Phase Two enhanced Keycloak per region. Global server load balancing provides geographic region affinity and failover to connect your users with the closest, available instances.

    There will be two price tiers for global clusters, depending on your use of our shared CockroachDB clusters, or your own dedicated clusters. We expect to launch general availability of global clusters later in Q3 2023.

    Please contact sales@phasetwo.io to talk to us about your global cluster use case.

    - + \ No newline at end of file diff --git a/blog/events/index.html b/blog/events/index.html index 022375c2b..39f9c510e 100644 --- a/blog/events/index.html +++ b/blog/events/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Events and Audit Logging Extensions

    · One min read

    Per our committment to keeping our core extensions open source, today we're releasing our Keycloak extensions to the event system. These extensions form the basis of how our Audit Log features are built.

    Additionally, we're providing several goodies that will be valuable to others building extensions on top of Keycloak, including a generic scriptable event listener, an event emitter to send events to any HTTP endpoint, a mechanism for retrieving event listener configurations from realm attributes, a mechanism for running multiple event listeners of the same type with different configurations, and a unified event model with facility for subscribing to webhooks.

    We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-events

    - + \ No newline at end of file diff --git a/blog/free-realm/index.html b/blog/free-realm/index.html index 68e338da9..def0374cb 100644 --- a/blog/free-realm/index.html +++ b/blog/free-realm/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Free as in Beer

    · 2 min read

    Since our release about basing Phase Two on the Keycloak Open Source Identity and Access Management system, an our committment to keeping our core extensions open source, we've received positive feedback from customers and interest from the the Keycloak community.

    We've noticed that support forums for Keycloak have many questions and requests around just getting started. Even though the software is mature, open source, and has a helpfu user community, just spinning up an environment and trying it out can be puzzling for first-time users. It's pretty clear that a lot of people just give up, because they can't get a server running, let alone configure their first realm1.

    Because of that, we've decided to offer developers a FREE realm in Phase Two, so those that are interested in trying it out can get successful quickly. Free realms are limited to fewer than 1000 users and 5 SSO connections. Otherwise, there are no restrictions beyond abiding by our terms of use. Sound good? Please fill out the contact form with your company information, and we'll respond with access information for your realm.


    1. In Keycloak, a "realm" manages a set of users, credentials, roles, and groups. A user belongs to and logs into a realm. Realms are isolated from one another and can only manage and authenticate the users that they control.
    - + \ No newline at end of file diff --git a/blog/index.html b/blog/index.html index 7e7bb453f..21e543dc9 100644 --- a/blog/index.html +++ b/blog/index.html @@ -12,8 +12,8 @@ - - + + @@ -28,7 +28,7 @@

    Images and other assets

    You'll notice that images, icons and fonts not found in the standard Keycloak themes are used. These are referenced by URL in the CSS. For example, if you are used to loading a custom font in the <head> of your HTML, it is possible to do it in CSS using @import:

    @import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Lexend+Deca:wght@300;400;500;600;700&family=Lexend:wght@300;400;500;600;700&family=Work+Sans:wght@300;400;500;600;700&display=swap");

    And background and other images can be referenced by their full URL using url(...):

    .login-pf body {
    background-image: url("https://raw.githubusercontent.com/p2-inc/keycloak-themes/main/examples/saas/assets/SaaS%20BG.webp");
    }

    Success!

    As always, our success is based on the success of our customers. We hope this extension and guide has helped you update the default Keycloak login branding to match that of your needs. If you have suggestions for further improvement of this feature, please reach out on GitHub!

    · 2 min read

    tl;dr

    We’ve changed the license of our core extensions from the AGPL v3 to the Elastic License v2. We wanted to share why we made this change and what it means for our customers and community.

    Why?

    From our earliest stages, Phase Two has been built as a set of extensions to Keycloak. We have made a commitment that source code for our core exetensions will always be available so that our customers can migrate to their own deployment, while maintaining the extension functionality provided by Phase Two.

    As our product matured and found a market fit, it became clear to us that our current license was failing to give our customers that guarantee, and failing to give our company the protection to ensure we could build a business and continue to invest in our extensions.

    We've had some very deep conversations with customers and contributors about the future of licensing, and learned a lot about what other companies in our shoes have done. We truly appreciate all of the engagement and feedback, and have done our best to make a decision that is in the best interest of our company, customers and community.

    What's allowed?

    1. Hosting and using the extensions by companies as part of their own product.
    2. Derivative works that maintain the same license.

    What's prohibited?

    1. Providing the extensions as a hosted or managed service. This includes bundling and distribution by companies who sell their products for on-prem and private cloud use.

    We believe that the Elastic License strikes a great balance, and we're excited for our next phase of growth!

    · 4 min read

    We've received a lot of support requests about the right way to set up SSO connections. We've published a 5 minute video showing you how to do it easily. Also, the script is included below in case you miss anything!

    Script

    Developers have been asking for concise setup instructions for SSO. You’re here for our enterprise SSO functionality. We hear you. Here’s a quick live setup to show you how easy it is.

    I’m assuming you have already created a self-serve deployment.

    • Start by opening the console.
    • Navigate to the Organizations tab.
    • Create a new organization. In this case, set a domain when creating an organization that corresponds to the email domains you want to match to their SSO provider.
    • Create a Portal Link by selecting it from the action menu of the organization. This is usually meant to be sent to the organization administrator, but in this case, we’ll open this link in an incognito window and configure the identity provider ourselves.

    We’ll use the identity provider wizard to setup a SSO connection to Azure AD. I’ve sped this up a bit, but you’ll get an idea of what is happening. Depending on your setup and target identity provider, this will be different.

    Now let’s secure an application and use our new SSO connection to log in. For this purpose, we’ve got a debugging application on Github you can use to quickly see how a front end application is secured and what data is shared between Phase Two and the application.

    • Clone our debug-app from github, and open up the frontend folder in your favorite editor or IDE.
    • Go back to the admin console and navigate to the Clients tab. Create a new client. Let’s call it frontend. This will be a public OIDC client, with localhost:3001 as the root and redirect uri.
    • Get the keycloak.json from the configuration and copy it. Paste it into keycloak.json to configure your debug-app.

    Before we continue, we need to configure an authentication flow that does our SSO redirect.

    • Navigate to the Authentication tab, and duplicate the Browser flow.
    • Add the Home IdP Discovery authenticator and move it into the position before the user forms. Configure it to not require a verified domain nor email.
    • Finally, using the action menu, bind it to the browser flow.

    Go back to the debug-app, and let’s try a login using an email domain that matches the one we configured.

    • First, run npm i and npm start to start the debug app, and navigate to http://localhost:3001 in your browser. See that it redirects to the default login.
    • Enter the email address in the new email only form.
    • We are redirected to the Azure identity provider we set up.
    • Log in to Azure, and then we are redirected to back to debug-app.
    • Let’s take a look at the token and see the data that came over from Azure.

    As a bonus, let’s map some information about the user’s organization memberships into the token in case we need to do something with that information in our application.

    • Go back to the Admin UI and navigate to the Client we created.
    • Select the frontend-dedicated client scope.
    • Add a mapper by configuration.
    • Select the Organization Role mapper and configure it as shown.
    • Save the configuration.

    Now let’s go back to the debug-app and reload.

    • Take a look at the token. It now contains information about the organization we created.
    • The user was automatically created and added as a member to the organization when we logged in through the Azure identity provider.

    You now have a fully working authentication and enterprise SSO setup for your application. It took about 5 minutes!

    · 6 min read

    There are a lot of guides out there, official and unofficial, for how to secure applications with Keycloak. The subject is rather broad, so it's difficult to know where to start. To begin, we'll be focusing on Keycloak's use of OpenID Connect (OIDC), and how to use that standard, along with some helpful libraries, to secure a simple but instructive application.

    For the purposes of the sample, we'll actually be using two common applications, a frontend single-page application (SPA) written in JavaScript, and a backend REST API written for Node.js. The language we selected for the sample is JavaScript, but the principles apply no matter the implementation technology you choose.

    What is OIDC?

    When learning about identity and access management technologies, you'll be confronted with an alphabet-soup of acronyms to learn. OIDC, or OpenID Connect is one of the most important ones for securing applications, be it browser-based, APIs, mobile or native. Our friend over at OneLogin does a great job of explaining OIDC in plain english for those that are curious.

    For the purpose of this guide, it is sufficient to know that OIDC is an open authentication protocol that works on top of the OAuth 2.0 framework. OIDC allows individuals to use single sign-on (SSO) to access relying party sites using OpenID Providers (OPs), such as an email provider or social network, to authenticate their identities. It provides the application or service with information about the user, the context of their authentication, and access to their profile information.

    Login flow

    A "flow" in OIDC terms is a mechanism of authenticating a user, and obtaning access tokens. The flow we'll be using in this guide is called the authorization code flow. Fortunately, the internals of the flow are not necessary to understand, as Keycloak handles the details for you.

    However, it is useful to see what is going on in the login process, so that you understand your user's experience.

    Setup

    We'll make an assumption for this guide that you are using a cloud deployment of Phase Two enhanced Keycloak. If you haven't already set one up, go over to the self-service launch announcement for details. You may also use your own Keycloak setup, but that setup is beyond the scope of this article.

    The sample applications are available in our demo repo on Github. Clone that repo to your local machine. You'll find the applications in the frontend and backend directory, and a set of supporting files for configuration and deployment.

    git clone https://github.com/p2-inc/debug-app.git
    cd debug-app

    Client

    Every application that Keycloak protects is considered a Client. Log into your Keycloak realm, and click on Clients in the left navigation, and click Create client.

    1. Enter frontend as the Client ID and click Next
    2. In the Capability config screen, keep the defaults and click Save
    3. In the Access settings screen, enter the following values:
      1. http://localhost:3001/* for Valid redirect URIs
      2. + in Valid post logout redirect URIs
      3. + in Web origins
    4. Click Save
    5. In the upper right corner, open the Action menu and select Download adapter config. Click Download and move the file to the debug-app repo you cloned under the frontend folder.

    Make a user

    Before we run the application, we need to create a user to log in. Click on Users in the left navigation, and click Add user. You only need to give the user a username and click Create. Find the Credentials tab and click Set password to give the user a password.

    Running the sample apps

    Open two terminal windows and go to the directory of the repo you cloned in both. To start, run the following commands in each terminal:

    Frontend:

    cd frontend/
    npm install
    npm start

    Backend:

    cd backend/
    npm install
    KC_REALM=<your-realm-name> KC_URL=<your-keycloak-url> npm start

    Be sure to replace the realm name, and the URL of your Keycloak installation (e.g. https://usw2.auth.ac/auth/).

    This will install the necessary components using npm, and will start the servers for both applications. Note: the applications use ports 3001 and 3002 by default. If you have other applications running on these ports, you may have to temporarily shut them down.

    Putting it all together

    Load http://localhost:3001/ in your browser. This will load the frontend application, which will be immediately redirected to the Keycloak login page. Log in with the user you created.

    Once you log in, you'll see your profile, and several menu items. First click to the Access token menu item. You'll see the information from the parsed access token that was returned by Keycloak. This contains information about the user, but also claims related to the users roles and groups. This is because access tokens are meant to be read and validated by resource servers (i.e. our backend service).

    Next, click ID token. You'll see similar information to what we saw in the access token, but limited to a standardized set of information that identifies the authentication state of the user. ID tokens are not meant for calling resource servers, and because of that, don't contain claims that are meant to be validated by backend services.

    Clicking Service will call the backend service. You'll see a message that indicates the frontend called the backend, passing the access token, and was authorized to access a secured service.

    You can also try the built-in Keycloak Account Management console by clicking Account, which gives the user a simple way to manage their information that is stored in Keycloak. It is not necessary to use this with your applications, as you may choose to build it in to your app. However, it's a good tool to have out of the box.

    Finally, clicking Logout will take you back to the login page. This is actually sending you to the frontend's initial page, which is redirecting you to the login page as its default behavior.

    What just happened?

    There's a lot that goes into implementation of the OIDC flow we used to secure our sample applications. Part of the reason to use Keycloak is the mature implementation and client libraries that make protecting applications in a secure way almost trivial.

    We encourage you to look at the source in the sample applications (specifically frontend/app.js and backend/app.js) and observe how the Keycloak client libraries are used to secure these applications. This will be a good place to start when you are working on securing your own applications.

    Your application

    Another incredible advantage to using standards like OIDC is that you are not constrained to using Keycloak libraries. Because your applications may not be written in JavaScript also, it's easy to use other language OIDC client libraries. We maintain a list of OIDC libraries, and the OpenID Foundation also maintains lists of certified and uncertified implementations

    Today you saw how to quickly secure an application using Keycloak, and learned more about the underlying OIDC standards. We look forward to seeing what you build!

    - + \ No newline at end of file diff --git a/blog/instant-user-managemenet-and-sso-for-nextjs/index.html b/blog/instant-user-managemenet-and-sso-for-nextjs/index.html index 4ae5f42a3..f15e27dfa 100644 --- a/blog/instant-user-managemenet-and-sso-for-nextjs/index.html +++ b/blog/instant-user-managemenet-and-sso-for-nextjs/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Securing Next.js Apps with Keycloak

    · 4 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Next.js example. We also have a plain React example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Next.js Project

    info

    We will use the Phase Two Next.js example code here, but the logic could easily be applied to any existing application.

    This example uses Next.js 13 and splits server and client components accordingly.

    1. Clone the Phase Two example repo.

    2. Open the Next.js folder within /frameworks/nextjs.

    3. Run npm install and then npm run dev. This example leverages NextAuth.js to provide hook and HOC support.

    4. NextAuth.js configures an API route that is uses for the Authentication of the Client. It generates the routes automatically for you. These are added to Next.js in the api/auth/[...nextauth]/route.ts file.

    5. Open the src/lib/auth.ts file. This is a server only file. We will be updating a few values from the prior section where we set up our OIDC client. Taking the values from the OIDC Client Config section, set those values in the code. While it is recommended to use Environment variables for the secret, for the purpose of this tutorial, paste in the Client secret from the OIDC client creation section for the value of clientSecret

      const authServerUrl = "https://euc1.auth.ac/auth/";
      const realm = "shared-deployment-001";
      const clientId = "reg-example-1";
      const clientSecret = "CLIENT_SECRET"; // Paste "Client secret" here. Use Environment variables in prod

      Those are used to popluate the AuthOptions config for the KeycloakProvider:

      export const AuthOptions: NextAuthOptions = {
      providers: [
      KeycloakProvider({
      clientId,
      clientSecret,
      issuer: `${authServerUrl}realms/${realm}`,
      }),
      ],
      };

      The config is then provided to the AuthProvider in the /src/app/layout.tsx file. Next.js uses this file to generate an HTML view for this page.

      import { NextAuthProvider as AuthProvider } from "./providers";
      ...
      <AuthProvider {...oidcConfig}>
      <App />
      </AuthProvider>

      At this point our entire application will be able to access all information and methods needed to perform authentication. View the providers.tsx file for additional information about how the SessionProvider is used. The SessionProvider enables use of Hooks to derive the authenticated state. View user.component.tsx for exactly how the code is authenticating your user. The sections rendering the "Log in" and "Log out" buttons are conditional areas based on the authenticated context. The buttons invoke functions provided by NextAuth.

      The logic using the hook to conditionally determine the Authenticated state, can be used to secure routes, components, and more.

    6. Open localhost:3000. You will see the Phase Two example landing page. You current state should be "Not authenticated". Click Log In. This will redirect you to your login page.

      info

      Use the non-admin user created in the previous section to sign in.

    7. Enter the credentials of the non-admin user you created. Click Submit. You will then be redirected to the application. The Phase Two example landing page now loads your "Authenticated" state, displaying your user's email and their Token.

    8. After your first log in, click Log out. Then click Log in again. Notice how this time you will not be redirected to sign in as your state is already in the browser. Neat! If you clear the browser state for that tab, then you will have to be redirected away to sign-in again.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/instant-user-managemenet-and-sso-for-nuxt/index.html b/blog/instant-user-managemenet-and-sso-for-nuxt/index.html index 95d8680a4..af7db766d 100644 --- a/blog/instant-user-managemenet-and-sso-for-nuxt/index.html +++ b/blog/instant-user-managemenet-and-sso-for-nuxt/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Securing Nuxt Apps with Keycloak

    · 7 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Nuxt example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Nuxt Project

    info

    We will use the Phase Two Nuxt example code here, but the logic could easily be applied to any existing application.

    This example uses Nuxt3. There are a couple methods by which you can integrate Keycloak to your Nuxt application. We're going to explore two methods here, one uses keycloak-js and the other leverages oidc-client-ts. The keycloak-js library provides a simple, client-only method, but lacks some of the sophistication provided by the oidc-client library that is heavily supported and more widely used.

    Using keycloak-js

    info

    For this example, we need to disable "Client Authentication" in the OIDC client that was setup earlier. This is available under Client > Settings > Capability config > Client authentication to OFF.

    1. Clone the Phase Two example repo.

    2. Open the Nuxt folder within /frameworks/nuxt and open the keycloak-js folder within /frameworks/nuxt/keycloak-js.

    3. Run npm install and then npm run dev. keycloak-js is a Javascript library that provides a fast way to secure an application.

    4. The project makes use of the following Nuxt items: components, composables, layouts, and plugins. We'll review each in kind.

    5. The main component that shows the User's authenticated state is in /components/User. In this component we call the useKeycloak composable, which let's us key into the keycloak-js functions that we've wrapped to make easily availble.

      const { keycloak, authState } = useKeycloak();

      function login() {
      keycloak.login();
      }

      function logout() {
      keycloak.logout();
      }

      Lower in the file the component leverages v-if checks to determine if the authState is authenticated or not. Depending on the state, a Log in or Log out button is available.

    6. Let's take a look at the setup for the composable next. Our composable is in /composables/keycloak-c. A composable is a function defined that can be called anywhere in the Nuxt application. It's a good way to abstract logic to be reused. In our case we use it to wrap a keycloak-js plugin (more on that in the next step) and help provided a state value for the authenticated state.

      export const useKeycloak = () => {
      const nuxtApp = useNuxtApp();
      const keycloak = nuxtApp.$keycloak as Keycloak;
      const authState = useState("authState", () => "unAuthenticated");

      keycloak.onAuthSuccess = () => (authState.value = "authenticated");
      keycloak.onAuthError = () => (authState.value = "error");

      return {
      keycloak,
      authState,
      };
      };
    7. In the plugin, /plugins/keycloak.client.ts we instantiate the keycloak-js library. We can then attach that instance to the NuxtApp instance. Substitute the correct values for your Keycloak instance that we created earlier in the tutorial.

      export default defineNuxtPlugin((nuxtApp) => {
      const initOptions: KeycloakConfig = {
      url: "https://euc1.auth.ac/auth/",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      };

      const keycloak = new Keycloak(initOptions);

      nuxtApp.$keycloak = keycloak;

      keycloak.init({
      onLoad: "check-sso",
      });
      });
    8. The logic for checking the authenticated state can be used to expand in ways to secure your site in a number of ways.

    Using oidc-client

    The oidc-client-ts package is a well-maintained and used library. It provides a lot of utilities for building out a fully production app.

    1. Clone the Phase Two example repo.

    2. Open the Nuxt folder within /frameworks/nuxt and open the /nuxt/oidc-client-ts folder.

    3. Run npm install and then npm run dev.

    4. The structure of the project is similar to the keycloak-js version but with a the use of services, stores, and middleware.

    5. We'll review where we configure out Keycloak instance. First open /services/keycloak-config.ts. In this file you will want to update it with the values for the Keycloak instance we set-up earlier in the tutorial. Make sure you are using the one with Client Authentication enabled. Update the clientSecret with the value. Use and environment variable here if you wish.

      export const keycloakConfig = {
      authorityUrl: "https://euc1.auth.ac",
      applicationUrl: "http://localhost:3000",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      clientSecret: "CLIENT_SECRET",
      };
    6. Switch over to the /services/auth-service now to see how the Oidc instance is started. The class pulls in values from the keycloakConfig to use in the constructor. The other functions are wrappers around methods provided by the oidc-client library. This allows us to key into things like signInRedirect and signoutRedirect.

      How the settings are integrated:

      const settings = {
      authority: `${keycloakConfig.authorityUrl}/auth/realms/${keycloakConfig.realm}`,
      client_id: keycloakConfig.clientId,
      client_secret: keycloakConfig.clientSecret,
      redirect_uri: `${window.location.origin}/auth`,
      silent_redirect_uri: `${window.location.origin}/silent-refresh`,
      post_logout_redirect_uri: `${window.location.origin}`,
      response_type: "code",
      userStore: new WebStorageStateStore(),
      loadUserInfo: true,
      };
      this.userManager = new UserManager(settings);

      Example function wrapper:

      public signInRedirect() {
      return this.userManager.signinRedirect();
      }
    7. With the AuthService defined, we can now expose that through a composable. Switch to the /composables/useServices file. The file is simple but provides a way for any component to hook into the service instance.

      import AuthService from "@/services/auth-service";
      import ApplicationService from "@/services/application-service";
      import { useAuth } from "@/stores/auth";

      export const useServices = () => {
      const authStore = useAuth();

      return {
      $auth: new AuthService(),
      $application: new ApplicationService(authStore.access_token),
      };
      };

      We pull in the AuthService then expose it through the $auth variable. The $application variable exposes the ApplicationService which is provided as an example of how you could secure API calls.

    8. We leverage the pinia library to make store User information to make it easily accessible. Open /stores/auth/index. From within this file, we can wrap the User object exposed by the oidc-client package. This can then be leveraged in the middleware function we want to define or to pull information quickly about the user.

    9. There are a few main pages in play here that we define to create paths the library can leverage. The /pages/auth, /pages/logout, /pages/silent-refresh create paths at the same name. These are used to do the redirection during authentication or log out. From within these we use the AuthService to direct the user around within the app. For instance in /auth:

      const authenticateOidc = async () => {
      try {
      await services.$auth.signInCallback();
      router.push("/");
      } catch (error) {
      console.error(error);
      }
      };

      await authenticateOidc();

      The router.push naively sends someone to the home page. This could be updated to go to any number of places, including the page one started the login flow from if you were to store that information to be retrieved.

    10. We have also created a middleware file in /middleware/auth.global to be used in a couple of ways. It checks if the user is authenticated and based on that knowledge, stores the user information in the store (if not there) or could be used to send someone to login. For our example, we created buttons to initiate that but there is a comment which shows how you could force a set of paths to require login.

      const authFlowRoutes = ["/auth", "/silent-refresh", "/logout"];

      export default defineNuxtRouteMiddleware(async (to, from) => {
      const authStore = useAuth();
      const services = useServices();
      const user = (await services.$auth.getUser()) as User;

      if (!user && !authFlowRoutes.includes(to.path)) {
      // use this to automatically force a sign in and redirect
      // services.$auth.signInRedirect();
      } else {
      authStore.setUpUserCredentials(user);
      }
      });
    11. Now that we have all the things setup, we can define the user component /components/User to easily pull information about the user's state and display the appropriate UI.

      const authStore = useAuth();
      const user = authStore.user;

      const signIn = () => services.$auth.signInRedirect();
      const signOut = () => services.$auth.logout();

      With this, the user object is now easily available. A simple v-if="user" allows the app to determine what UI to show.

    12. A bit more complicated of a setup, but more elegant in the handling of the logged in flow. The oidc-client allows for much better fine-tuning of the experience.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/instant-user-managemenet-and-sso-for-reactjs/index.html b/blog/instant-user-managemenet-and-sso-for-reactjs/index.html index 477baf1cf..4423fca26 100644 --- a/blog/instant-user-managemenet-and-sso-for-reactjs/index.html +++ b/blog/instant-user-managemenet-and-sso-for-reactjs/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Instant User Management, SSO, and Secure Pages for ReactJS

    · 3 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two ReactJS example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to Off.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a ReactJS Project

    info

    We will use the Phase Two ReactJS example code here, but the logic could easily be applied to any existing application.

    1. Clone the Phase Two example repo.

    2. Open the ReactJS folder within /frameworks/reactjs.

    3. Run npm install and then npm start. This example leverages react-oidc-context (which uses oidc-client-ts) to provide hook and HOC support.

    4. Open the index.tsx file. We will be updating a few values from the prior section where we set up our OIDC client. Taking the values from the OIDC Client Config section, set those values in the code.

      const authServerUrl = "https://euc1.auth.ac/auth/";
      const realm = "shared-deployment-001";
      const client = "reg-example-1";

      Those are used to popluate the OIDC config

      const oidcConfig = {
      authority: `${authServerUrl}realms/${realm}`,
      client_id: client,
      redirect_uri: "http://localhost:3000/authenticated",
      onSigninCallback: (args: any) =>
      window.history.replaceState(
      {},
      document.title,
      window.location.pathname
      ),
      };

      The config is then provided to the AuthProvider.

      <AuthProvider {...oidcConfig}>
      <App />
      </AuthProvider>

      At this point our entire applicationw will be able to access all information and methods needed to perform authentication. View Auth.tsx for exactly how the code is authenticating your user. The sections rendering the "Log in" and "Log out" buttons are conditional areas based on the authenticated context.

      The logic using the hook to conditionally determine the Authenticated state, can be used to secure routes, components, and more.

    5. Open localhost:3000. You will see the Phase Two example landing page. You current state should be "Not authenticated". Click Log In. This will redirect you to your login page.

      info

      Use the non-admin user created in the previous section to sign in.

    6. Enter the credentials of the non-admin user you created. Click Submit. You will then be redirected to the application. The Phase Two example landing page now loads your "Authenticated" state, displaying your user's email and their Token.

    7. After your first log in, click Log out. Then click Log in again. Notice how this time you will not be redirected to sign in as your state is already in the browser. Neat! If you clear the browser state for that tab, then you will have to be redirected away to sign-in again.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/instant-user-managemenet-and-sso-for-vue/index.html b/blog/instant-user-managemenet-and-sso-for-vue/index.html index a8ab24b40..88fc176e7 100644 --- a/blog/instant-user-managemenet-and-sso-for-vue/index.html +++ b/blog/instant-user-managemenet-and-sso-for-vue/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Securing Vue Apps with Keycloak

    · One min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Vue example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Vue.js Project

    info

    We will use the Phase Two Vue example code here, but the logic could easily be applied to any existing application.

    This example uses Vue.js. We're going to leverage oidc-client-ts to integrate OIDC authentication with the Vue app. The oidc-client-ts package is a well-maintained and used library. It provides a lot of utilities for building out a fully production app.

    1. Clone the Phase Two example repo.

    2. Open the Vue folder within /frameworks/vue and open the /nuxt/oidc-client-ts folder.

    3. Run npm install and then npm run dev.

    4. We'll review where we configure out Keycloak instance. First open /auth.ts. In this file you will want to update it with the values for the Keycloak instance we set-up earlier in the tutorial. Update the clientSecret with the value. Use and environment variable here if you wish.

      export const keycloakConfig = {
      authorityUrl: "https://euc1.auth.ac",
      applicationUrl: "http://localhost:3000",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      clientSecret: "CLIENT_SECRET",
      };

      After the config, you can see how the OIDC instance is started.

      const settings = {
      authority: `${keycloakConfig.authorityUrl}/auth/realms/${keycloakConfig.realm}`,
      client_id: keycloakConfig.clientId,
      client_secret: keycloakConfig.clientSecret,
      redirect_uri: `${window.location.origin}/auth`,
      silent_redirect_uri: `${window.location.origin}/silent-refresh`,
      post_logout_redirect_uri: `${window.location.origin}`,
      response_type: "code",
      userStore: new WebStorageStateStore(),
      loadUserInfo: true,
      };
      this.userManager = new UserManager(settings);
    5. With the Keycloak instance defined, we attach this to the app instance for Vue. Switch to /main.ts

      import Auth from "@/auth";
      // ...
      app.config.globalProperties.$auth = Auth;

      We pull in the Auth instance then expose it through the $auth variable.

    6. There are a few main pages in play here that we define to create paths the library can leverage. The /view/auth and /view/silent-refresh create paths at the same name. These are used to do the redirection during authentication. From within these we use the Auth instance to direct the user around within the app. For instance in /views/AuthView:

      export default {
      name: "AuthAuthenticated",
      async mounted() {
      try {
      await this.$auth.signinCallback();
      this.$router.push("/");
      } catch (e) {
      console.error(e);
      }
      },
      };

      The router.push naively sends someone to the home page. This could be updated to go to any number of places, including the page one started the login flow from if you were to store that information to be retrieved.

    7. Now that we have all the things setup, we can define the user component /components/User to easily pull information about the user's state and display the appropriate UI.

      export default {
      name: "UserComponent",
      data() {
      return {
      user: null,
      signIn: () => this.$auth.signinRedirect(),
      logout: () => this.$auth.signoutRedirect(),
      };
      },
      async created() {
      const user = await this.$auth.getUser();
      if (user) {
      this.user = user;
      }
      },
      };

      With this, the user object is now easily available. A simple v-if="user" allows the app to determine what UI to show.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/keycloak/index.html b/blog/keycloak/index.html index 665a3bfb8..6ef53bd27 100644 --- a/blog/keycloak/index.html +++ b/blog/keycloak/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Keycloak, and Our Commitment to Open Source

    · 2 min read

    Following the initial release of Phase Two's authentication and SSO tools 3 months ago, we had a warm reception by several early- to mid- stage SaaS companies. The message was consistent. SSO was a key barrier to unlocking enterprise customers, and we had made it much easier to quickly integrate the alphabet-soup of enterprise identity providers.

    Furthermore, many of our customers have responded well to our "one price per project" idea, citing that competitors and other enterprise authentication companies had pricing models that ramped on a per-user and per-SSO connection basis, making them economically unattractive to companies with business and pricing models that couldn't support that.

    One of the other points that we heard loud and clear from our first customers, was the fear of vendor lock-in. Integrating tools like this can be a large effort, and can be difficult to unwind if the terms or service fall short. While our adoption of standards such as OpenID and SAML allayed some of those fears, we wanted to go a step further.

    We built the initial verison of Phase Two as a set of extensions to the Keycloak Open Source Identity and Access Management system, built and maintained by Red Hat. After several months of developing for it, and operating it for our customers, we've decided to continue using it. Keycloak has been battle-tested and hardened for over 6 years. It's security and reliability is depended on by organizations from small startups to Fortune 500 companies and governments.

    To put to rest any future concerns about vendor lock-in, we're committing to making our core extensions to Keycloak open source. While we will endeavor to make Phase Two simple to use, operate and scale, we will maintain compatibility so that customers can migrate to their own Keycloak deployment. Updates and links to our open source extensions will be published in the Open Source section of the documentation, and will be available in our p2-inc GitHub organization page.

    We have benefitted immensely from the open source communitiy, and we are excited to give back!

    - + \ No newline at end of file diff --git a/blog/licensing-change/index.html b/blog/licensing-change/index.html index 817e6e8de..44164fdd7 100644 --- a/blog/licensing-change/index.html +++ b/blog/licensing-change/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    We're Changing Our License

    · 2 min read

    tl;dr

    We’ve changed the license of our core extensions from the AGPL v3 to the Elastic License v2. We wanted to share why we made this change and what it means for our customers and community.

    Why?

    From our earliest stages, Phase Two has been built as a set of extensions to Keycloak. We have made a commitment that source code for our core exetensions will always be available so that our customers can migrate to their own deployment, while maintaining the extension functionality provided by Phase Two.

    As our product matured and found a market fit, it became clear to us that our current license was failing to give our customers that guarantee, and failing to give our company the protection to ensure we could build a business and continue to invest in our extensions.

    We've had some very deep conversations with customers and contributors about the future of licensing, and learned a lot about what other companies in our shoes have done. We truly appreciate all of the engagement and feedback, and have done our best to make a decision that is in the best interest of our company, customers and community.

    What's allowed?

    1. Hosting and using the extensions by companies as part of their own product.
    2. Derivative works that maintain the same license.

    What's prohibited?

    1. Providing the extensions as a hosted or managed service. This includes bundling and distribution by companies who sell their products for on-prem and private cloud use.

    We believe that the Elastic License strikes a great balance, and we're excited for our next phase of growth!

    - + \ No newline at end of file diff --git a/blog/magic-link/index.html b/blog/magic-link/index.html index 489567aa9..362d99386 100644 --- a/blog/magic-link/index.html +++ b/blog/magic-link/index.html @@ -12,8 +12,8 @@ - - + + @@ -23,7 +23,7 @@ This mechanism inserts a authenticator in the login flow that intercepts the email address and sends the magic link in an email to to the user.

    We've also implemented a web service that allows you to create a magic link without necessarily sending an email. This will allow you to send the link through another channel. Specification for the new endpoint can be found in the Magic Link API Documentation.

    Both methods have the option of forcing the creation of a new user when an unknown email address is used. This allows a combination login/registration flow that combines an email verification. We think this really nails reducing friction in a new user flow.

    We're open sourcing the Keycloak extensionsso that the broad Keycloak community can benefit right away. We are doing this in line with our committment to keeping our core extensions open source. We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-magic-link

    - + \ No newline at end of file diff --git a/blog/orgs/index.html b/blog/orgs/index.html index c71f2b49a..65a845cee 100644 --- a/blog/orgs/index.html +++ b/blog/orgs/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Multi-tenant Extensions

    · One min read

    Today we're open sourcing set of Keycloak extensions that are focused on solving several of the common use cases of multi-tenant, SaaS applications that Keycloak does not solve out of the box. We are doing this in line with our committment to keeping our core extensions open source. These extensions are the basis of our Organizations features, which allow Phase Two customers to model their own customers in their systems and create enterprise "team" functionality that suits their business case.

    A variation of this code has been built, enhanced and used in production by several customers for almost two years. It is now available as open source for members of the broader Keycloak community. We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-orgs

    - + \ No newline at end of file diff --git a/blog/page/2/index.html b/blog/page/2/index.html index 2b5937559..a48d0f34a 100644 --- a/blog/page/2/index.html +++ b/blog/page/2/index.html @@ -12,8 +12,8 @@ - - + + @@ -24,7 +24,7 @@ This mechanism inserts a authenticator in the login flow that intercepts the email address and sends the magic link in an email to to the user.

    We've also implemented a web service that allows you to create a magic link without necessarily sending an email. This will allow you to send the link through another channel. Specification for the new endpoint can be found in the Magic Link API Documentation.

    Both methods have the option of forcing the creation of a new user when an unknown email address is used. This allows a combination login/registration flow that combines an email verification. We think this really nails reducing friction in a new user flow.

    We're open sourcing the Keycloak extensionsso that the broad Keycloak community can benefit right away. We are doing this in line with our committment to keeping our core extensions open source. We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-magic-link

    · One min read

    Today we're open sourcing set of Keycloak extensions that are focused on solving several of the common use cases of multi-tenant, SaaS applications that Keycloak does not solve out of the box. We are doing this in line with our committment to keeping our core extensions open source. These extensions are the basis of our Organizations features, which allow Phase Two customers to model their own customers in their systems and create enterprise "team" functionality that suits their business case.

    A variation of this code has been built, enhanced and used in production by several customers for almost two years. It is now available as open source for members of the broader Keycloak community. We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-orgs

    · One min read

    Following our post about our wizard product, we received an overwhelming amount of interest in it. Many customers of our cloud offering asked for it as a portal for their organization administrators to set up their identity providers. On-prem customers said that one consistent onboarding hurdle was SSO complexity, and asked for it to be included in the bundled distribution.

    Today we're pleased to report that we've listened to both use cases and completed embedding the "wizard" product into Phase Two. We're calling it "Connect", as it's the best way we could come up with characterizing its simplicity. It massively reduces the complexit of configuring SSO connections, and distills the process into something any member of the team can understand.

    Phase Two Connect is currently available by invitation only while we work out the final kinks. Contact sales for more information.

    · One min read

    Working with one of our customers, we discovered that even the most technically literate developer or ops professional could look at the configuration for an SSO connection like it was a foreign language. While our configuration interface attempts to cover all possible options, and document clearly what each option means, it can still be entirely unclear what is required during a setup.

    Furthermore, the identity provider that is being integrated can present a similarly extensive interface that may not use the same terms and language. However, after investigation into the most common identity providers, we found that most of the configuration options can simply be set by convention if the vendor is known.

    Based on that observation, we've built what we call a "wizard" UI on top of our identity provider configuration to make it easy to integration the top commercial identity provider vendors. Take a look at a quick video of a setup using our most recent prototype.

    If you're interested in early access to our "wizards", please contact us today.

    · One min read

    Per our committment to keeping our core extensions open source, today we're releasing our Keycloak extensions to the event system. These extensions form the basis of how our Audit Log features are built.

    Additionally, we're providing several goodies that will be valuable to others building extensions on top of Keycloak, including a generic scriptable event listener, an event emitter to send events to any HTTP endpoint, a mechanism for retrieving event listener configurations from realm attributes, a mechanism for running multiple event listeners of the same type with different configurations, and a unified event model with facility for subscribing to webhooks.

    We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-events

    · 2 min read

    Since our release about basing Phase Two on the Keycloak Open Source Identity and Access Management system, an our committment to keeping our core extensions open source, we've received positive feedback from customers and interest from the the Keycloak community.

    We've noticed that support forums for Keycloak have many questions and requests around just getting started. Even though the software is mature, open source, and has a helpfu user community, just spinning up an environment and trying it out can be puzzling for first-time users. It's pretty clear that a lot of people just give up, because they can't get a server running, let alone configure their first realm1.

    Because of that, we've decided to offer developers a FREE realm in Phase Two, so those that are interested in trying it out can get successful quickly. Free realms are limited to fewer than 1000 users and 5 SSO connections. Otherwise, there are no restrictions beyond abiding by our terms of use. Sound good? Please fill out the contact form with your company information, and we'll respond with access information for your realm.


    1. In Keycloak, a "realm" manages a set of users, credentials, roles, and groups. A user belongs to and logs into a realm. Realms are isolated from one another and can only manage and authenticate the users that they control.

    · 2 min read

    Following the initial release of Phase Two's authentication and SSO tools 3 months ago, we had a warm reception by several early- to mid- stage SaaS companies. The message was consistent. SSO was a key barrier to unlocking enterprise customers, and we had made it much easier to quickly integrate the alphabet-soup of enterprise identity providers.

    Furthermore, many of our customers have responded well to our "one price per project" idea, citing that competitors and other enterprise authentication companies had pricing models that ramped on a per-user and per-SSO connection basis, making them economically unattractive to companies with business and pricing models that couldn't support that.

    One of the other points that we heard loud and clear from our first customers, was the fear of vendor lock-in. Integrating tools like this can be a large effort, and can be difficult to unwind if the terms or service fall short. While our adoption of standards such as OpenID and SAML allayed some of those fears, we wanted to go a step further.

    We built the initial verison of Phase Two as a set of extensions to the Keycloak Open Source Identity and Access Management system, built and maintained by Red Hat. After several months of developing for it, and operating it for our customers, we've decided to continue using it. Keycloak has been battle-tested and hardened for over 6 years. It's security and reliability is depended on by organizations from small startups to Fortune 500 companies and governments.

    To put to rest any future concerns about vendor lock-in, we're committing to making our core extensions to Keycloak open source. While we will endeavor to make Phase Two simple to use, operate and scale, we will maintain compatibility so that customers can migrate to their own Keycloak deployment. Updates and links to our open source extensions will be published in the Open Source section of the documentation, and will be available in our p2-inc GitHub organization page.

    We have benefitted immensely from the open source communitiy, and we are excited to give back!

    - + \ No newline at end of file diff --git a/blog/page/3/index.html b/blog/page/3/index.html index 25b06e6a3..cdac18ad1 100644 --- a/blog/page/3/index.html +++ b/blog/page/3/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    · 3 min read

    After building and working for startups and technology companies for almost 25 years, I found myself having a sense of déjà-vu.

    Had I really built the same features and functionality over and over?

    Everyone of us who has been in the industry for any length of time probably has the same feeling. Whether or not we are fully conscious of it, we probably built a (bad) version of login, registration, user management, authorization, organizations and invitations, audit logging, etc. at least one time for every company we've worked for.

    In early 2018, I joined an enterprise SaaS startup, where I built the initial team and product over 18 months. Analyzing tickets and epics in the project tracking system we used, I found that over 60% of our first 18 months was spent building features and functionality like this -- essential building blocks, but not the core competency we sought to test in the marketplace. And the result of that effort was only adequate versions of those common features, which resulted in less time spent on what we were trying to prove. I began to refer to this heavy tax a "SaaS CRUD".

    Was this really what everyone else was doing? I was lucky to have a large network of engineering leaders at companies that ranged from the earliest stages to the largest public companies, so I asked them. The responses were remarkably consisitent. Early stage companies wished there was something comprehensive they could adopt, and later stage and large companies lamented not adopting external tools earlier that gave them guarantees around uptime and compliance. All were aware of or had tried to knit together a mish-mash of "feature company" products, and all expressed dissatisfaction with "model mismatch" of most of the tools in the marketplace, which demanded more integration overhead than the perceived benefit allowed.

    I was lucky to find others that had observed the same thing. We joined forces and spent the next 6 months interviewing companies of a range of sizes, developing a playbook for companies building new products. Based on that playbook, today we are releasing our first version of tooling designed to help application developers avoid rebuilding "SaaS CRUD".

    Phase Two is designed to help SaaS companies accelerate time-to-market. Authentication and SSO are our first targets, but we plan to expand to many other areas of pain for the growing company seeking enterprise adoption. We'd love for you to join us on this journey. For product demos and to become a beta customer, please contact us!

    - + \ No newline at end of file diff --git a/blog/secure-django/index.html b/blog/secure-django/index.html index bbe40e191..5e2d39782 100644 --- a/blog/secure-django/index.html +++ b/blog/secure-django/index.html @@ -12,8 +12,8 @@ - - + + @@ -25,7 +25,7 @@ Update your Django app's urls.py to include the authentication URLs provided by mozilla-django-oidc:

    urlpatterns += [
    path('oidc/', include('mozilla_django_oidc.urls')),
    ]

    Using it in your app

    Protect your views

    Use Decorators for Access Control. You can now use the @oidc_protected decorator to protect views that require authentication and potentially specific roles:

    from mozilla_django_oidc.decorators import oidc_protected

    @oidc_protected
    def protected_view(request):
    # Your view logic

    Accessing user information

    You can access user information after authentication using the request.oidc_user attribute. For example:

    def profile_view(request):
    user_info = request.oidc_user.userinfo
    # Access user_info['sub'], user_info['email'], etc.
    # Your view logic

    By default, mozilla-django-oidc looks up a Django user matching the email field to the email address returned in the user info data from Keycloak.

    If a user logs into your site and doesn’t already have an account, by default, mozilla-django-oidc will create a new Django user account. It will create the User instance filling in the username (hash of the email address) and email fields.

    Use Username rather than Email

    mozilla-django-oidc defaults to setting up Django users using the email address as the user name from keycloak was required. Fortunately, preferred_username is set up by default in Keycloak as a claim. The claim can used by overriding the OIDCAuthenticationBackend class in mozilla_django_oidc.auth and referring to this in AUTHENTICATION_BACKENDS as below:


    # Classes to override default OIDCAuthenticationBackend (Keycloak authentication)
    from mozilla_django_oidc.auth import OIDCAuthenticationBackend

    class KeycloakOIDCAuthenticationBackend(OIDCAuthenticationBackend):

    def create_user(self, claims):
    """ Overrides Authentication Backend so that Django users are
    created with the keycloak preferred_username.
    If nothing found matching the email, then try the username.
    """
    user = super(KeycloakOIDCAuthenticationBackend, self).create_user(claims)
    user.first_name = claims.get('given_name', '')
    user.last_name = claims.get('family_name', '')
    user.email = claims.get('email')
    user.username = claims.get('preferred_username')
    user.save()
    return user

    def filter_users_by_claims(self, claims):
    """ Return all users matching the specified email.
    If nothing found matching the email, then try the username
    """
    email = claims.get('email')
    preferred_username = claims.get('preferred_username')

    if not email:
    return self.UserModel.objects.none()
    users = self.UserModel.objects.filter(email__iexact=email)

    if len(users) < 1:
    if not preferred_username:
    return self.UserModel.objects.none()
    users = self.UserModel.objects.filter(username__iexact=preferred_username)
    return users

    def update_user(self, user, claims):
    user.first_name = claims.get('given_name', '')
    user.last_name = claims.get('family_name', '')
    user.email = claims.get('email')
    user.username = claims.get('preferred_username')
    user.save()
    return user

    In settings.py, overide the new library you have just added in AUTHENTICATION_BACKENDS :

     # mozilla_django_oidc - Keycloak authentication
    "fragalysis.auth.KeycloakOIDCAuthenticationBackend",

    Logging out

    You can use the @oidc_logout decorator to log the user out of both your app and Keycloak:

    from mozilla_django_oidc.decorators import oidc_logout

    @oidc_logout
    def logout_view(request):
    # Your logout view logic

    Add support for Django Rest Framework

    Django Rest Framework (DRF) is a flexible toolkit built on top of Django, specifically designed for building RESTful APIs.

    If you want DRF to authenticate users based on an OAuth access token provided in the Authorization header, you can use the DRF-specific authentication class which ships with the package.

    Add this to your settings:

    REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
    'mozilla_django_oidc.contrib.drf.OIDCAuthentication',
    'rest_framework.authentication.SessionAuthentication',
    # other authentication classes, if needed
    ],
    }

    Note that this only takes care of authenticating against an access token, and provides no options to create or renew tokens.

    If you’ve created a custom Django OIDCAuthenticationBackend and added that to your AUTHENTICATION_BACKENDS, the DRF class should be smart enough to figure that out. Alternatively, you can manually set the OIDC backend to use:

    OIDC_DRF_AUTH_BACKEND = 'mozilla_django_oidc.auth.OIDCAuthenticationBackend'

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/securing-apps-with-keycloak/index.html b/blog/securing-apps-with-keycloak/index.html index 5d4b3f71e..66e7274ab 100644 --- a/blog/securing-apps-with-keycloak/index.html +++ b/blog/securing-apps-with-keycloak/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Protecting Your Application With Keycloak

    · 6 min read

    There are a lot of guides out there, official and unofficial, for how to secure applications with Keycloak. The subject is rather broad, so it's difficult to know where to start. To begin, we'll be focusing on Keycloak's use of OpenID Connect (OIDC), and how to use that standard, along with some helpful libraries, to secure a simple but instructive application.

    For the purposes of the sample, we'll actually be using two common applications, a frontend single-page application (SPA) written in JavaScript, and a backend REST API written for Node.js. The language we selected for the sample is JavaScript, but the principles apply no matter the implementation technology you choose.

    What is OIDC?

    When learning about identity and access management technologies, you'll be confronted with an alphabet-soup of acronyms to learn. OIDC, or OpenID Connect is one of the most important ones for securing applications, be it browser-based, APIs, mobile or native. Our friend over at OneLogin does a great job of explaining OIDC in plain english for those that are curious.

    For the purpose of this guide, it is sufficient to know that OIDC is an open authentication protocol that works on top of the OAuth 2.0 framework. OIDC allows individuals to use single sign-on (SSO) to access relying party sites using OpenID Providers (OPs), such as an email provider or social network, to authenticate their identities. It provides the application or service with information about the user, the context of their authentication, and access to their profile information.

    Login flow

    A "flow" in OIDC terms is a mechanism of authenticating a user, and obtaning access tokens. The flow we'll be using in this guide is called the authorization code flow. Fortunately, the internals of the flow are not necessary to understand, as Keycloak handles the details for you.

    However, it is useful to see what is going on in the login process, so that you understand your user's experience.

    Setup

    We'll make an assumption for this guide that you are using a cloud deployment of Phase Two enhanced Keycloak. If you haven't already set one up, go over to the self-service launch announcement for details. You may also use your own Keycloak setup, but that setup is beyond the scope of this article.

    The sample applications are available in our demo repo on Github. Clone that repo to your local machine. You'll find the applications in the frontend and backend directory, and a set of supporting files for configuration and deployment.

    git clone https://github.com/p2-inc/debug-app.git
    cd debug-app

    Client

    Every application that Keycloak protects is considered a Client. Log into your Keycloak realm, and click on Clients in the left navigation, and click Create client.

    1. Enter frontend as the Client ID and click Next
    2. In the Capability config screen, keep the defaults and click Save
    3. In the Access settings screen, enter the following values:
      1. http://localhost:3001/* for Valid redirect URIs
      2. + in Valid post logout redirect URIs
      3. + in Web origins
    4. Click Save
    5. In the upper right corner, open the Action menu and select Download adapter config. Click Download and move the file to the debug-app repo you cloned under the frontend folder.

    Make a user

    Before we run the application, we need to create a user to log in. Click on Users in the left navigation, and click Add user. You only need to give the user a username and click Create. Find the Credentials tab and click Set password to give the user a password.

    Running the sample apps

    Open two terminal windows and go to the directory of the repo you cloned in both. To start, run the following commands in each terminal:

    Frontend:

    cd frontend/
    npm install
    npm start

    Backend:

    cd backend/
    npm install
    KC_REALM=<your-realm-name> KC_URL=<your-keycloak-url> npm start

    Be sure to replace the realm name, and the URL of your Keycloak installation (e.g. https://usw2.auth.ac/auth/).

    This will install the necessary components using npm, and will start the servers for both applications. Note: the applications use ports 3001 and 3002 by default. If you have other applications running on these ports, you may have to temporarily shut them down.

    Putting it all together

    Load http://localhost:3001/ in your browser. This will load the frontend application, which will be immediately redirected to the Keycloak login page. Log in with the user you created.

    Once you log in, you'll see your profile, and several menu items. First click to the Access token menu item. You'll see the information from the parsed access token that was returned by Keycloak. This contains information about the user, but also claims related to the users roles and groups. This is because access tokens are meant to be read and validated by resource servers (i.e. our backend service).

    Next, click ID token. You'll see similar information to what we saw in the access token, but limited to a standardized set of information that identifies the authentication state of the user. ID tokens are not meant for calling resource servers, and because of that, don't contain claims that are meant to be validated by backend services.

    Clicking Service will call the backend service. You'll see a message that indicates the frontend called the backend, passing the access token, and was authorized to access a secured service.

    You can also try the built-in Keycloak Account Management console by clicking Account, which gives the user a simple way to manage their information that is stored in Keycloak. It is not necessary to use this with your applications, as you may choose to build it in to your app. However, it's a good tool to have out of the box.

    Finally, clicking Logout will take you back to the login page. This is actually sending you to the frontend's initial page, which is redirecting you to the login page as its default behavior.

    What just happened?

    There's a lot that goes into implementation of the OIDC flow we used to secure our sample applications. Part of the reason to use Keycloak is the mature implementation and client libraries that make protecting applications in a secure way almost trivial.

    We encourage you to look at the source in the sample applications (specifically frontend/app.js and backend/app.js) and observe how the Keycloak client libraries are used to secure these applications. This will be a good place to start when you are working on securing your own applications.

    Your application

    Another incredible advantage to using standards like OIDC is that you are not constrained to using Keycloak libraries. Because your applications may not be written in JavaScript also, it's easy to use other language OIDC client libraries. We maintain a list of OIDC libraries, and the OpenID Foundation also maintains lists of certified and uncertified implementations

    Today you saw how to quickly secure an application using Keycloak, and learned more about the underlying OIDC standards. We look forward to seeing what you build!

    - + \ No newline at end of file diff --git a/blog/self-service/index.html b/blog/self-service/index.html index 0af3eeb84..87ace57e5 100644 --- a/blog/self-service/index.html +++ b/blog/self-service/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Self-service (beta) Launch

    · 2 min read

    We've been pretty quiet over the summer. Since we released the Organizations and Magic Link extensions and open sourced them, there has been a lot of interest in using Phase Two.

    We were flattered by the inbound interest, but our small team wasn't able to keep up with demand for trial accounts. Rather than scramble against that demand, we opted to pause new accounts, and instead build a self-service tool to allow anyone to quickly provision a new deployment a try it out.

    Today we're announcing the beta launch of the Phase Two Self-service deployment tool. This tool allows you to easily create new deployments of the Phase Two enhanced version of Keycloak in our secure, highly-available clusters. In the future, it will also allow you to deploy dedicated instances that use your own database.

    Take a look at how easy it is to get started:

    The clusters that run our deployments are available in two regions (AWS, us-west-2 and eu-central-1), and are backed by CockroachDB, giving you scale, resilience and low-latency performance. In the future, clusters and dedicated instances will be available in other regions based on demand.

    We hope you find this new tool valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    TRY IT NOW!

    - + \ No newline at end of file diff --git a/blog/set-up-email/index.html b/blog/set-up-email/index.html index 5b42de34d..6dbe48ce7 100644 --- a/blog/set-up-email/index.html +++ b/blog/set-up-email/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Set Up Email in Phase Two

    · 3 min read

    One of the first things you will need to do when getting a Keycloak Realm ready for use is to set up your email server configuration. There are many system emails that are sent to users in the course of verifying and updating user accounts: Email address verification, magic links, password reset, account update, login failure notifications, identity provider linking, etc.

    In order to provide your users with a positive experience, these messages need a way to get to them. Keycloak supports any internet reachable SMTP server. If you are currently testing, and don't have an email server or service that you currently use, SendGrid provides free accounts that allow you to send up to 100 emails per day forever. For debugging, you can also use a service like MailTrap to give you a catch-all for emails coming from Keycloak.

    If you are using a Phase Two Deployment, log in to the self-service dashboard, and click on the Open Console link for the Deployment you wish to use. Once in the Keycloak admin console, click Realm settings in the left menu, and then click the Email tab.

    In the first section, labeled Template, you will set options that will be used in the templates for the emails that are sent to your users. The only required field is the From field, which must contain the email address the user will see the email originating from. This should be an email address that your email server is expecting, and it will not block for authorization reasons.

    The other fields in the Template section are not required, but will enhance how your emails look:

    • From address used to send emails (required)
    • From display name a user-friendly name displayed along From
    • Reply to an email address that will be used by email clients when your user replies to an email
    • Reply to display name a user-friendly name displayed along Reply to
    • Envelope from Bounce Address used for the mails that are rejected

    In the Connection & Authentication section, you will provide details of your SMTP server:

    • Host indicates the SMTP server hostname used for sending emails
    • Port indicates the SMTP server port (usually 25, 465, 587, or 2525)
    • Encryption support encryption for communication with your SMTP server
    • Authentication if your SMTP server requires authentication, and supply the Username and Password

    Finally, before you click Save, click the Test connection button to send a test email to the email address of the currently logged in user. If you don't have that set, you might have click Save and edit your user before you come back. You'll receive a success message, or information that will help you resolve problems.

    Once you do that, you'll have accomplished a significant task which enables lots of other functionality!

    Also, stay tuned for another post on how to customize your email templates to match your branding and messaging.

    - + \ No newline at end of file diff --git a/blog/set-up-magic-links/index.html b/blog/set-up-magic-links/index.html index 0963b31eb..c4d55efbd 100644 --- a/blog/set-up-magic-links/index.html +++ b/blog/set-up-magic-links/index.html @@ -12,8 +12,8 @@ - - + + @@ -22,7 +22,7 @@

    Go back to the admin console in the other browser window, and navigate to the Users section. You will be able to find the user that was just created.

    Magic links are a great way to streamline your user onboarding and experience to help you easily drive engagement across your application. Phase Two makes it quick and easy to integrate magic links (and social login, and enterprise SSO, and much more). Stay tuned for more guides that will help you build the authentication experience that is right for your app.

    - + \ No newline at end of file diff --git a/blog/sso-setup/index.html b/blog/sso-setup/index.html index 20e6f5722..fdeac20b2 100644 --- a/blog/sso-setup/index.html +++ b/blog/sso-setup/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Setting up SSO with Phase Two

    · 4 min read

    We've received a lot of support requests about the right way to set up SSO connections. We've published a 5 minute video showing you how to do it easily. Also, the script is included below in case you miss anything!

    Script

    Developers have been asking for concise setup instructions for SSO. You’re here for our enterprise SSO functionality. We hear you. Here’s a quick live setup to show you how easy it is.

    I’m assuming you have already created a self-serve deployment.

    • Start by opening the console.
    • Navigate to the Organizations tab.
    • Create a new organization. In this case, set a domain when creating an organization that corresponds to the email domains you want to match to their SSO provider.
    • Create a Portal Link by selecting it from the action menu of the organization. This is usually meant to be sent to the organization administrator, but in this case, we’ll open this link in an incognito window and configure the identity provider ourselves.

    We’ll use the identity provider wizard to setup a SSO connection to Azure AD. I’ve sped this up a bit, but you’ll get an idea of what is happening. Depending on your setup and target identity provider, this will be different.

    Now let’s secure an application and use our new SSO connection to log in. For this purpose, we’ve got a debugging application on Github you can use to quickly see how a front end application is secured and what data is shared between Phase Two and the application.

    • Clone our debug-app from github, and open up the frontend folder in your favorite editor or IDE.
    • Go back to the admin console and navigate to the Clients tab. Create a new client. Let’s call it frontend. This will be a public OIDC client, with localhost:3001 as the root and redirect uri.
    • Get the keycloak.json from the configuration and copy it. Paste it into keycloak.json to configure your debug-app.

    Before we continue, we need to configure an authentication flow that does our SSO redirect.

    • Navigate to the Authentication tab, and duplicate the Browser flow.
    • Add the Home IdP Discovery authenticator and move it into the position before the user forms. Configure it to not require a verified domain nor email.
    • Finally, using the action menu, bind it to the browser flow.

    Go back to the debug-app, and let’s try a login using an email domain that matches the one we configured.

    • First, run npm i and npm start to start the debug app, and navigate to http://localhost:3001 in your browser. See that it redirects to the default login.
    • Enter the email address in the new email only form.
    • We are redirected to the Azure identity provider we set up.
    • Log in to Azure, and then we are redirected to back to debug-app.
    • Let’s take a look at the token and see the data that came over from Azure.

    As a bonus, let’s map some information about the user’s organization memberships into the token in case we need to do something with that information in our application.

    • Go back to the Admin UI and navigate to the Client we created.
    • Select the frontend-dedicated client scope.
    • Add a mapper by configuration.
    • Select the Organization Role mapper and configure it as shown.
    • Save the configuration.

    Now let’s go back to the debug-app and reload.

    • Take a look at the token. It now contains information about the organization we created.
    • The user was automatically created and added as a member to the organization when we logged in through the Azure identity provider.

    You now have a fully working authentication and enterprise SSO setup for your application. It took about 5 minutes!

    - + \ No newline at end of file diff --git a/blog/tags/cockroach/index.html b/blog/tags/cockroach/index.html index ae1f43534..f5cee25cf 100644 --- a/blog/tags/cockroach/index.html +++ b/blog/tags/cockroach/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    2 posts tagged with "cockroach"

    View All Tags

    · 4 min read

    We're excited today to announce the launch of our dedicated clusters offering. Our Phase Two enhanced Keycloak distribution is now available as a hosted, dedicated cluster in the region of your choice.

    About 9 months ago, we launched our self-service, shared deployments, offering customers the ability to create Phase Two enhanced Keycloak realms on our shared clusters. Over that period, we've provided over 700 free realms for testing and small production use cases. Many of you have reached out to us asking about an SLA, isolated resources, and ability to grow into larger use cases. Based on your requests and feedback, we built out our dedicated cluster offering.

    What is Phase Two again?

    Phase Two helps SaaS builders accelerate time-to-market and enterprise adoption with powerful SSO, identity and user management features. To that end, Phase Two has created an enhanced distribution of Keycloak that bundles several essential open source extensions for modern SaaS use cases. We support hosted and on-prem customers for a variety of use cases.

    Why dedicated?

    Dedicated clusters allow us to provide compute, network and storage isolation for customer workloads, and to easily deploy in the region where customer's users are. With dedicated clusters, we can guarantee an SLA that meets customer's needs with no resource contention from other customers.

    Furthermore, it allows us to support customer-provided domain names, and access to monitoring and management capabilities not available in the shared clusters.

    How did you do it?

    We built out our dedicated cluster offering using the best-of-breed open source tools and managed services.

    The core consists of Kubernetes clusters in each supported AWS and GCP region. New dedicated clusters are provisioned instantly using FluxCD, a continuous delivery solution that gives us the history and auditability of git. Monitoring and alerting is done using Prometheus, Grafana, and a suite of external services that give us a complete view of cluster health.

    The database tier uses a managed CockroachDB service provided by Cockroach Labs. Phase Two is the only provider that is capable of hosting the current Keycloak distribution (the "legacy" store) using CockroachDB. Working with Cockroach Labs gives us the expertise and reliability from hosting thousands of customer clusters at massive scale.

    Take me to it!

    Starting today, you'll be able to log into the updated dashboard, and create your dedicated cluster by selecting a region and setting up your billing information.

    Following successful billing setup, you will be returned to the Dashboard while your Cluster is provisioned. Once provisioned, you'll be able to create up to 20 realms per cluster, using the same easy setup as you are used to in the shared deployment offering.

    Most clusters will be provisioned within 30 minutes, but some requests may take up to 24 hours. Additionally, for payment types such as ACH or SEPA, cluster provisioning will begin following payment clearing (up to 4 days in some cases).

    How much does it cost?

    Plans start at $499US per month when paid annually. This provides a set of compute, network and storage resources that have been tested for common use cases for up to 20 realms and 1 million users. If your use case is uncommon or you plan to scale beyond that, our system is designed to scale up with your needs. Our plans scale linearly with resource demands beyond our minimums.

    We will continue to support a robust Free tier.

    What's next? (psst... Global clusters)

    Many of our on-prem, suppport customers with large use cases asked us to build out a solution for massive-scale, fault-tolerant, multi-region use cases. One of the great parts of building support for Keycloak's existing storage system for CockroacDB is that we've been able to explore use cases that were previously impossible using the standard Keycloak distribution, or required complex, error-prone configurations. For use cases with these requirements, plus global proximity to users and regional failover, we built global clusters, backed by CockroachDB multi-region database, for which we are now in beta.

    These clusters provide a minimum of 3 global regions, with 3 instances of Phase Two enhanced Keycloak per region. Global server load balancing provides geographic region affinity and failover to connect your users with the closest, available instances.

    There will be two price tiers for global clusters, depending on your use of our shared CockroachDB clusters, or your own dedicated clusters. We expect to launch general availability of global clusters later in Q3 2023.

    Please contact sales@phasetwo.io to talk to us about your global cluster use case.

    · 2 min read

    We've been pretty quiet over the summer. Since we released the Organizations and Magic Link extensions and open sourced them, there has been a lot of interest in using Phase Two.

    We were flattered by the inbound interest, but our small team wasn't able to keep up with demand for trial accounts. Rather than scramble against that demand, we opted to pause new accounts, and instead build a self-service tool to allow anyone to quickly provision a new deployment a try it out.

    Today we're announcing the beta launch of the Phase Two Self-service deployment tool. This tool allows you to easily create new deployments of the Phase Two enhanced version of Keycloak in our secure, highly-available clusters. In the future, it will also allow you to deploy dedicated instances that use your own database.

    Take a look at how easy it is to get started:

    The clusters that run our deployments are available in two regions (AWS, us-west-2 and eu-central-1), and are backed by CockroachDB, giving you scale, resilience and low-latency performance. In the future, clusters and dedicated instances will be available in other regions based on demand.

    We hope you find this new tool valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    TRY IT NOW!

    - + \ No newline at end of file diff --git a/blog/tags/connect/index.html b/blog/tags/connect/index.html index 719ac5e72..f423d6beb 100644 --- a/blog/tags/connect/index.html +++ b/blog/tags/connect/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    One post tagged with "connect"

    View All Tags

    · One min read

    Following our post about our wizard product, we received an overwhelming amount of interest in it. Many customers of our cloud offering asked for it as a portal for their organization administrators to set up their identity providers. On-prem customers said that one consistent onboarding hurdle was SSO complexity, and asked for it to be included in the bundled distribution.

    Today we're pleased to report that we've listened to both use cases and completed embedding the "wizard" product into Phase Two. We're calling it "Connect", as it's the best way we could come up with characterizing its simplicity. It massively reduces the complexit of configuring SSO connections, and distills the process into something any member of the team can understand.

    Phase Two Connect is currently available by invitation only while we work out the final kinks. Contact sales for more information.

    - + \ No newline at end of file diff --git a/blog/tags/django/index.html b/blog/tags/django/index.html index 12e8bb521..a121030a2 100644 --- a/blog/tags/django/index.html +++ b/blog/tags/django/index.html @@ -12,8 +12,8 @@ - - + + @@ -25,7 +25,7 @@ Update your Django app's urls.py to include the authentication URLs provided by mozilla-django-oidc:

    urlpatterns += [
    path('oidc/', include('mozilla_django_oidc.urls')),
    ]

    Using it in your app

    Protect your views

    Use Decorators for Access Control. You can now use the @oidc_protected decorator to protect views that require authentication and potentially specific roles:

    from mozilla_django_oidc.decorators import oidc_protected

    @oidc_protected
    def protected_view(request):
    # Your view logic

    Accessing user information

    You can access user information after authentication using the request.oidc_user attribute. For example:

    def profile_view(request):
    user_info = request.oidc_user.userinfo
    # Access user_info['sub'], user_info['email'], etc.
    # Your view logic

    By default, mozilla-django-oidc looks up a Django user matching the email field to the email address returned in the user info data from Keycloak.

    If a user logs into your site and doesn’t already have an account, by default, mozilla-django-oidc will create a new Django user account. It will create the User instance filling in the username (hash of the email address) and email fields.

    Use Username rather than Email

    mozilla-django-oidc defaults to setting up Django users using the email address as the user name from keycloak was required. Fortunately, preferred_username is set up by default in Keycloak as a claim. The claim can used by overriding the OIDCAuthenticationBackend class in mozilla_django_oidc.auth and referring to this in AUTHENTICATION_BACKENDS as below:


    # Classes to override default OIDCAuthenticationBackend (Keycloak authentication)
    from mozilla_django_oidc.auth import OIDCAuthenticationBackend

    class KeycloakOIDCAuthenticationBackend(OIDCAuthenticationBackend):

    def create_user(self, claims):
    """ Overrides Authentication Backend so that Django users are
    created with the keycloak preferred_username.
    If nothing found matching the email, then try the username.
    """
    user = super(KeycloakOIDCAuthenticationBackend, self).create_user(claims)
    user.first_name = claims.get('given_name', '')
    user.last_name = claims.get('family_name', '')
    user.email = claims.get('email')
    user.username = claims.get('preferred_username')
    user.save()
    return user

    def filter_users_by_claims(self, claims):
    """ Return all users matching the specified email.
    If nothing found matching the email, then try the username
    """
    email = claims.get('email')
    preferred_username = claims.get('preferred_username')

    if not email:
    return self.UserModel.objects.none()
    users = self.UserModel.objects.filter(email__iexact=email)

    if len(users) < 1:
    if not preferred_username:
    return self.UserModel.objects.none()
    users = self.UserModel.objects.filter(username__iexact=preferred_username)
    return users

    def update_user(self, user, claims):
    user.first_name = claims.get('given_name', '')
    user.last_name = claims.get('family_name', '')
    user.email = claims.get('email')
    user.username = claims.get('preferred_username')
    user.save()
    return user

    In settings.py, overide the new library you have just added in AUTHENTICATION_BACKENDS :

     # mozilla_django_oidc - Keycloak authentication
    "fragalysis.auth.KeycloakOIDCAuthenticationBackend",

    Logging out

    You can use the @oidc_logout decorator to log the user out of both your app and Keycloak:

    from mozilla_django_oidc.decorators import oidc_logout

    @oidc_logout
    def logout_view(request):
    # Your logout view logic

    Add support for Django Rest Framework

    Django Rest Framework (DRF) is a flexible toolkit built on top of Django, specifically designed for building RESTful APIs.

    If you want DRF to authenticate users based on an OAuth access token provided in the Authorization header, you can use the DRF-specific authentication class which ships with the package.

    Add this to your settings:

    REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
    'mozilla_django_oidc.contrib.drf.OIDCAuthentication',
    'rest_framework.authentication.SessionAuthentication',
    # other authentication classes, if needed
    ],
    }

    Note that this only takes care of authenticating against an access token, and provides no options to create or renew tokens.

    If you’ve created a custom Django OIDCAuthenticationBackend and added that to your AUTHENTICATION_BACKENDS, the DRF class should be smart enough to figure that out. Alternatively, you can manually set the OIDC backend to use:

    OIDC_DRF_AUTH_BACKEND = 'mozilla_django_oidc.auth.OIDCAuthenticationBackend'

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/tags/email/index.html b/blog/tags/email/index.html index bcba0f772..790807e5f 100644 --- a/blog/tags/email/index.html +++ b/blog/tags/email/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    One post tagged with "email"

    View All Tags

    · 3 min read

    One of the first things you will need to do when getting a Keycloak Realm ready for use is to set up your email server configuration. There are many system emails that are sent to users in the course of verifying and updating user accounts: Email address verification, magic links, password reset, account update, login failure notifications, identity provider linking, etc.

    In order to provide your users with a positive experience, these messages need a way to get to them. Keycloak supports any internet reachable SMTP server. If you are currently testing, and don't have an email server or service that you currently use, SendGrid provides free accounts that allow you to send up to 100 emails per day forever. For debugging, you can also use a service like MailTrap to give you a catch-all for emails coming from Keycloak.

    If you are using a Phase Two Deployment, log in to the self-service dashboard, and click on the Open Console link for the Deployment you wish to use. Once in the Keycloak admin console, click Realm settings in the left menu, and then click the Email tab.

    In the first section, labeled Template, you will set options that will be used in the templates for the emails that are sent to your users. The only required field is the From field, which must contain the email address the user will see the email originating from. This should be an email address that your email server is expecting, and it will not block for authorization reasons.

    The other fields in the Template section are not required, but will enhance how your emails look:

    • From address used to send emails (required)
    • From display name a user-friendly name displayed along From
    • Reply to an email address that will be used by email clients when your user replies to an email
    • Reply to display name a user-friendly name displayed along Reply to
    • Envelope from Bounce Address used for the mails that are rejected

    In the Connection & Authentication section, you will provide details of your SMTP server:

    • Host indicates the SMTP server hostname used for sending emails
    • Port indicates the SMTP server port (usually 25, 465, 587, or 2525)
    • Encryption support encryption for communication with your SMTP server
    • Authentication if your SMTP server requires authentication, and supply the Username and Password

    Finally, before you click Save, click the Test connection button to send a test email to the email address of the currently logged in user. If you don't have that set, you might have click Save and edit your user before you come back. You'll receive a success message, or information that will help you resolve problems.

    Once you do that, you'll have accomplished a significant task which enables lots of other functionality!

    Also, stay tuned for another post on how to customize your email templates to match your branding and messaging.

    - + \ No newline at end of file diff --git a/blog/tags/frameworks/index.html b/blog/tags/frameworks/index.html index 874183c70..61ecd5563 100644 --- a/blog/tags/frameworks/index.html +++ b/blog/tags/frameworks/index.html @@ -12,8 +12,8 @@ - - + + @@ -25,7 +25,7 @@ Update your Django app's urls.py to include the authentication URLs provided by mozilla-django-oidc:

    urlpatterns += [
    path('oidc/', include('mozilla_django_oidc.urls')),
    ]

    Using it in your app

    Protect your views

    Use Decorators for Access Control. You can now use the @oidc_protected decorator to protect views that require authentication and potentially specific roles:

    from mozilla_django_oidc.decorators import oidc_protected

    @oidc_protected
    def protected_view(request):
    # Your view logic

    Accessing user information

    You can access user information after authentication using the request.oidc_user attribute. For example:

    def profile_view(request):
    user_info = request.oidc_user.userinfo
    # Access user_info['sub'], user_info['email'], etc.
    # Your view logic

    By default, mozilla-django-oidc looks up a Django user matching the email field to the email address returned in the user info data from Keycloak.

    If a user logs into your site and doesn’t already have an account, by default, mozilla-django-oidc will create a new Django user account. It will create the User instance filling in the username (hash of the email address) and email fields.

    Use Username rather than Email

    mozilla-django-oidc defaults to setting up Django users using the email address as the user name from keycloak was required. Fortunately, preferred_username is set up by default in Keycloak as a claim. The claim can used by overriding the OIDCAuthenticationBackend class in mozilla_django_oidc.auth and referring to this in AUTHENTICATION_BACKENDS as below:


    # Classes to override default OIDCAuthenticationBackend (Keycloak authentication)
    from mozilla_django_oidc.auth import OIDCAuthenticationBackend

    class KeycloakOIDCAuthenticationBackend(OIDCAuthenticationBackend):

    def create_user(self, claims):
    """ Overrides Authentication Backend so that Django users are
    created with the keycloak preferred_username.
    If nothing found matching the email, then try the username.
    """
    user = super(KeycloakOIDCAuthenticationBackend, self).create_user(claims)
    user.first_name = claims.get('given_name', '')
    user.last_name = claims.get('family_name', '')
    user.email = claims.get('email')
    user.username = claims.get('preferred_username')
    user.save()
    return user

    def filter_users_by_claims(self, claims):
    """ Return all users matching the specified email.
    If nothing found matching the email, then try the username
    """
    email = claims.get('email')
    preferred_username = claims.get('preferred_username')

    if not email:
    return self.UserModel.objects.none()
    users = self.UserModel.objects.filter(email__iexact=email)

    if len(users) < 1:
    if not preferred_username:
    return self.UserModel.objects.none()
    users = self.UserModel.objects.filter(username__iexact=preferred_username)
    return users

    def update_user(self, user, claims):
    user.first_name = claims.get('given_name', '')
    user.last_name = claims.get('family_name', '')
    user.email = claims.get('email')
    user.username = claims.get('preferred_username')
    user.save()
    return user

    In settings.py, overide the new library you have just added in AUTHENTICATION_BACKENDS :

     # mozilla_django_oidc - Keycloak authentication
    "fragalysis.auth.KeycloakOIDCAuthenticationBackend",

    Logging out

    You can use the @oidc_logout decorator to log the user out of both your app and Keycloak:

    from mozilla_django_oidc.decorators import oidc_logout

    @oidc_logout
    def logout_view(request):
    # Your logout view logic

    Add support for Django Rest Framework

    Django Rest Framework (DRF) is a flexible toolkit built on top of Django, specifically designed for building RESTful APIs.

    If you want DRF to authenticate users based on an OAuth access token provided in the Authorization header, you can use the DRF-specific authentication class which ships with the package.

    Add this to your settings:

    REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
    'mozilla_django_oidc.contrib.drf.OIDCAuthentication',
    'rest_framework.authentication.SessionAuthentication',
    # other authentication classes, if needed
    ],
    }

    Note that this only takes care of authenticating against an access token, and provides no options to create or renew tokens.

    If you’ve created a custom Django OIDCAuthenticationBackend and added that to your AUTHENTICATION_BACKENDS, the DRF class should be smart enough to figure that out. Alternatively, you can manually set the OIDC backend to use:

    OIDC_DRF_AUTH_BACKEND = 'mozilla_django_oidc.auth.OIDCAuthenticationBackend'

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    · 4 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Next.js example. We also have a plain React example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Next.js Project

    info

    We will use the Phase Two Next.js example code here, but the logic could easily be applied to any existing application.

    This example uses Next.js 13 and splits server and client components accordingly.

    1. Clone the Phase Two example repo.

    2. Open the Next.js folder within /frameworks/nextjs.

    3. Run npm install and then npm run dev. This example leverages NextAuth.js to provide hook and HOC support.

    4. NextAuth.js configures an API route that is uses for the Authentication of the Client. It generates the routes automatically for you. These are added to Next.js in the api/auth/[...nextauth]/route.ts file.

    5. Open the src/lib/auth.ts file. This is a server only file. We will be updating a few values from the prior section where we set up our OIDC client. Taking the values from the OIDC Client Config section, set those values in the code. While it is recommended to use Environment variables for the secret, for the purpose of this tutorial, paste in the Client secret from the OIDC client creation section for the value of clientSecret

      const authServerUrl = "https://euc1.auth.ac/auth/";
      const realm = "shared-deployment-001";
      const clientId = "reg-example-1";
      const clientSecret = "CLIENT_SECRET"; // Paste "Client secret" here. Use Environment variables in prod

      Those are used to popluate the AuthOptions config for the KeycloakProvider:

      export const AuthOptions: NextAuthOptions = {
      providers: [
      KeycloakProvider({
      clientId,
      clientSecret,
      issuer: `${authServerUrl}realms/${realm}`,
      }),
      ],
      };

      The config is then provided to the AuthProvider in the /src/app/layout.tsx file. Next.js uses this file to generate an HTML view for this page.

      import { NextAuthProvider as AuthProvider } from "./providers";
      ...
      <AuthProvider {...oidcConfig}>
      <App />
      </AuthProvider>

      At this point our entire application will be able to access all information and methods needed to perform authentication. View the providers.tsx file for additional information about how the SessionProvider is used. The SessionProvider enables use of Hooks to derive the authenticated state. View user.component.tsx for exactly how the code is authenticating your user. The sections rendering the "Log in" and "Log out" buttons are conditional areas based on the authenticated context. The buttons invoke functions provided by NextAuth.

      The logic using the hook to conditionally determine the Authenticated state, can be used to secure routes, components, and more.

    6. Open localhost:3000. You will see the Phase Two example landing page. You current state should be "Not authenticated". Click Log In. This will redirect you to your login page.

      info

      Use the non-admin user created in the previous section to sign in.

    7. Enter the credentials of the non-admin user you created. Click Submit. You will then be redirected to the application. The Phase Two example landing page now loads your "Authenticated" state, displaying your user's email and their Token.

    8. After your first log in, click Log out. Then click Log in again. Notice how this time you will not be redirected to sign in as your state is already in the browser. Neat! If you clear the browser state for that tab, then you will have to be redirected away to sign-in again.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    · 3 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two ReactJS example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to Off.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a ReactJS Project

    info

    We will use the Phase Two ReactJS example code here, but the logic could easily be applied to any existing application.

    1. Clone the Phase Two example repo.

    2. Open the ReactJS folder within /frameworks/reactjs.

    3. Run npm install and then npm start. This example leverages react-oidc-context (which uses oidc-client-ts) to provide hook and HOC support.

    4. Open the index.tsx file. We will be updating a few values from the prior section where we set up our OIDC client. Taking the values from the OIDC Client Config section, set those values in the code.

      const authServerUrl = "https://euc1.auth.ac/auth/";
      const realm = "shared-deployment-001";
      const client = "reg-example-1";

      Those are used to popluate the OIDC config

      const oidcConfig = {
      authority: `${authServerUrl}realms/${realm}`,
      client_id: client,
      redirect_uri: "http://localhost:3000/authenticated",
      onSigninCallback: (args: any) =>
      window.history.replaceState(
      {},
      document.title,
      window.location.pathname
      ),
      };

      The config is then provided to the AuthProvider.

      <AuthProvider {...oidcConfig}>
      <App />
      </AuthProvider>

      At this point our entire applicationw will be able to access all information and methods needed to perform authentication. View Auth.tsx for exactly how the code is authenticating your user. The sections rendering the "Log in" and "Log out" buttons are conditional areas based on the authenticated context.

      The logic using the hook to conditionally determine the Authenticated state, can be used to secure routes, components, and more.

    5. Open localhost:3000. You will see the Phase Two example landing page. You current state should be "Not authenticated". Click Log In. This will redirect you to your login page.

      info

      Use the non-admin user created in the previous section to sign in.

    6. Enter the credentials of the non-admin user you created. Click Submit. You will then be redirected to the application. The Phase Two example landing page now loads your "Authenticated" state, displaying your user's email and their Token.

    7. After your first log in, click Log out. Then click Log in again. Notice how this time you will not be redirected to sign in as your state is already in the browser. Neat! If you clear the browser state for that tab, then you will have to be redirected away to sign-in again.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/tags/index.html b/blog/tags/index.html index 4213de910..71bc6fe1a 100644 --- a/blog/tags/index.html +++ b/blog/tags/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@ - + \ No newline at end of file diff --git a/blog/tags/keycloak/index.html b/blog/tags/keycloak/index.html index ecadbefef..54c24e673 100644 --- a/blog/tags/keycloak/index.html +++ b/blog/tags/keycloak/index.html @@ -12,8 +12,8 @@ - - + + @@ -27,7 +27,7 @@ This mechanism inserts a authenticator in the login flow that intercepts the email address and sends the magic link in an email to to the user.

    We've also implemented a web service that allows you to create a magic link without necessarily sending an email. This will allow you to send the link through another channel. Specification for the new endpoint can be found in the Magic Link API Documentation.

    Both methods have the option of forcing the creation of a new user when an unknown email address is used. This allows a combination login/registration flow that combines an email verification. We think this really nails reducing friction in a new user flow.

    We're open sourcing the Keycloak extensionsso that the broad Keycloak community can benefit right away. We are doing this in line with our committment to keeping our core extensions open source. We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-magic-link

    · One min read

    Today we're open sourcing set of Keycloak extensions that are focused on solving several of the common use cases of multi-tenant, SaaS applications that Keycloak does not solve out of the box. We are doing this in line with our committment to keeping our core extensions open source. These extensions are the basis of our Organizations features, which allow Phase Two customers to model their own customers in their systems and create enterprise "team" functionality that suits their business case.

    A variation of this code has been built, enhanced and used in production by several customers for almost two years. It is now available as open source for members of the broader Keycloak community. We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-orgs

    · One min read

    Per our committment to keeping our core extensions open source, today we're releasing our Keycloak extensions to the event system. These extensions form the basis of how our Audit Log features are built.

    Additionally, we're providing several goodies that will be valuable to others building extensions on top of Keycloak, including a generic scriptable event listener, an event emitter to send events to any HTTP endpoint, a mechanism for retrieving event listener configurations from realm attributes, a mechanism for running multiple event listeners of the same type with different configurations, and a unified event model with facility for subscribing to webhooks.

    We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-events

    - + \ No newline at end of file diff --git a/blog/tags/keycloak/page/2/index.html b/blog/tags/keycloak/page/2/index.html index a89f948f7..149b124e5 100644 --- a/blog/tags/keycloak/page/2/index.html +++ b/blog/tags/keycloak/page/2/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    11 posts tagged with "keycloak"

    View All Tags

    · 2 min read

    Following the initial release of Phase Two's authentication and SSO tools 3 months ago, we had a warm reception by several early- to mid- stage SaaS companies. The message was consistent. SSO was a key barrier to unlocking enterprise customers, and we had made it much easier to quickly integrate the alphabet-soup of enterprise identity providers.

    Furthermore, many of our customers have responded well to our "one price per project" idea, citing that competitors and other enterprise authentication companies had pricing models that ramped on a per-user and per-SSO connection basis, making them economically unattractive to companies with business and pricing models that couldn't support that.

    One of the other points that we heard loud and clear from our first customers, was the fear of vendor lock-in. Integrating tools like this can be a large effort, and can be difficult to unwind if the terms or service fall short. While our adoption of standards such as OpenID and SAML allayed some of those fears, we wanted to go a step further.

    We built the initial verison of Phase Two as a set of extensions to the Keycloak Open Source Identity and Access Management system, built and maintained by Red Hat. After several months of developing for it, and operating it for our customers, we've decided to continue using it. Keycloak has been battle-tested and hardened for over 6 years. It's security and reliability is depended on by organizations from small startups to Fortune 500 companies and governments.

    To put to rest any future concerns about vendor lock-in, we're committing to making our core extensions to Keycloak open source. While we will endeavor to make Phase Two simple to use, operate and scale, we will maintain compatibility so that customers can migrate to their own Keycloak deployment. Updates and links to our open source extensions will be published in the Open Source section of the documentation, and will be available in our p2-inc GitHub organization page.

    We have benefitted immensely from the open source communitiy, and we are excited to give back!

    - + \ No newline at end of file diff --git a/blog/tags/license/index.html b/blog/tags/license/index.html index b7bb9ee8b..bcd36c8ab 100644 --- a/blog/tags/license/index.html +++ b/blog/tags/license/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    One post tagged with "license"

    View All Tags

    · 2 min read

    tl;dr

    We’ve changed the license of our core extensions from the AGPL v3 to the Elastic License v2. We wanted to share why we made this change and what it means for our customers and community.

    Why?

    From our earliest stages, Phase Two has been built as a set of extensions to Keycloak. We have made a commitment that source code for our core exetensions will always be available so that our customers can migrate to their own deployment, while maintaining the extension functionality provided by Phase Two.

    As our product matured and found a market fit, it became clear to us that our current license was failing to give our customers that guarantee, and failing to give our company the protection to ensure we could build a business and continue to invest in our extensions.

    We've had some very deep conversations with customers and contributors about the future of licensing, and learned a lot about what other companies in our shoes have done. We truly appreciate all of the engagement and feedback, and have done our best to make a decision that is in the best interest of our company, customers and community.

    What's allowed?

    1. Hosting and using the extensions by companies as part of their own product.
    2. Derivative works that maintain the same license.

    What's prohibited?

    1. Providing the extensions as a hosted or managed service. This includes bundling and distribution by companies who sell their products for on-prem and private cloud use.

    We believe that the Elastic License strikes a great balance, and we're excited for our next phase of growth!

    - + \ No newline at end of file diff --git a/blog/tags/login/index.html b/blog/tags/login/index.html index d8a6b3959..8bf4d0260 100644 --- a/blog/tags/login/index.html +++ b/blog/tags/login/index.html @@ -12,8 +12,8 @@ - - + + @@ -24,7 +24,7 @@

    Images and other assets

    You'll notice that images, icons and fonts not found in the standard Keycloak themes are used. These are referenced by URL in the CSS. For example, if you are used to loading a custom font in the <head> of your HTML, it is possible to do it in CSS using @import:

    @import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Lexend+Deca:wght@300;400;500;600;700&family=Lexend:wght@300;400;500;600;700&family=Work+Sans:wght@300;400;500;600;700&display=swap");

    And background and other images can be referenced by their full URL using url(...):

    .login-pf body {
    background-image: url("https://raw.githubusercontent.com/p2-inc/keycloak-themes/main/examples/saas/assets/SaaS%20BG.webp");
    }

    Success!

    As always, our success is based on the success of our customers. We hope this extension and guide has helped you update the default Keycloak login branding to match that of your needs. If you have suggestions for further improvement of this feature, please reach out on GitHub!

    - + \ No newline at end of file diff --git a/blog/tags/magic-links/index.html b/blog/tags/magic-links/index.html index 2f656f507..2020b1be4 100644 --- a/blog/tags/magic-links/index.html +++ b/blog/tags/magic-links/index.html @@ -12,8 +12,8 @@ - - + + @@ -22,7 +22,7 @@

    Go back to the admin console in the other browser window, and navigate to the Users section. You will be able to find the user that was just created.

    Magic links are a great way to streamline your user onboarding and experience to help you easily drive engagement across your application. Phase Two makes it quick and easy to integrate magic links (and social login, and enterprise SSO, and much more). Stay tuned for more guides that will help you build the authentication experience that is right for your app.

    - + \ No newline at end of file diff --git a/blog/tags/nextjs/index.html b/blog/tags/nextjs/index.html index 84ecefba8..81be182a1 100644 --- a/blog/tags/nextjs/index.html +++ b/blog/tags/nextjs/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    One post tagged with "nextjs"

    View All Tags

    · 4 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Next.js example. We also have a plain React example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Next.js Project

    info

    We will use the Phase Two Next.js example code here, but the logic could easily be applied to any existing application.

    This example uses Next.js 13 and splits server and client components accordingly.

    1. Clone the Phase Two example repo.

    2. Open the Next.js folder within /frameworks/nextjs.

    3. Run npm install and then npm run dev. This example leverages NextAuth.js to provide hook and HOC support.

    4. NextAuth.js configures an API route that is uses for the Authentication of the Client. It generates the routes automatically for you. These are added to Next.js in the api/auth/[...nextauth]/route.ts file.

    5. Open the src/lib/auth.ts file. This is a server only file. We will be updating a few values from the prior section where we set up our OIDC client. Taking the values from the OIDC Client Config section, set those values in the code. While it is recommended to use Environment variables for the secret, for the purpose of this tutorial, paste in the Client secret from the OIDC client creation section for the value of clientSecret

      const authServerUrl = "https://euc1.auth.ac/auth/";
      const realm = "shared-deployment-001";
      const clientId = "reg-example-1";
      const clientSecret = "CLIENT_SECRET"; // Paste "Client secret" here. Use Environment variables in prod

      Those are used to popluate the AuthOptions config for the KeycloakProvider:

      export const AuthOptions: NextAuthOptions = {
      providers: [
      KeycloakProvider({
      clientId,
      clientSecret,
      issuer: `${authServerUrl}realms/${realm}`,
      }),
      ],
      };

      The config is then provided to the AuthProvider in the /src/app/layout.tsx file. Next.js uses this file to generate an HTML view for this page.

      import { NextAuthProvider as AuthProvider } from "./providers";
      ...
      <AuthProvider {...oidcConfig}>
      <App />
      </AuthProvider>

      At this point our entire application will be able to access all information and methods needed to perform authentication. View the providers.tsx file for additional information about how the SessionProvider is used. The SessionProvider enables use of Hooks to derive the authenticated state. View user.component.tsx for exactly how the code is authenticating your user. The sections rendering the "Log in" and "Log out" buttons are conditional areas based on the authenticated context. The buttons invoke functions provided by NextAuth.

      The logic using the hook to conditionally determine the Authenticated state, can be used to secure routes, components, and more.

    6. Open localhost:3000. You will see the Phase Two example landing page. You current state should be "Not authenticated". Click Log In. This will redirect you to your login page.

      info

      Use the non-admin user created in the previous section to sign in.

    7. Enter the credentials of the non-admin user you created. Click Submit. You will then be redirected to the application. The Phase Two example landing page now loads your "Authenticated" state, displaying your user's email and their Token.

    8. After your first log in, click Log out. Then click Log in again. Notice how this time you will not be redirected to sign in as your state is already in the browser. Neat! If you clear the browser state for that tab, then you will have to be redirected away to sign-in again.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/tags/nuxt/index.html b/blog/tags/nuxt/index.html index 6095f002c..c1be26dc3 100644 --- a/blog/tags/nuxt/index.html +++ b/blog/tags/nuxt/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    2 posts tagged with "nuxt"

    View All Tags

    · One min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Vue example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Vue.js Project

    info

    We will use the Phase Two Vue example code here, but the logic could easily be applied to any existing application.

    This example uses Vue.js. We're going to leverage oidc-client-ts to integrate OIDC authentication with the Vue app. The oidc-client-ts package is a well-maintained and used library. It provides a lot of utilities for building out a fully production app.

    1. Clone the Phase Two example repo.

    2. Open the Vue folder within /frameworks/vue and open the /nuxt/oidc-client-ts folder.

    3. Run npm install and then npm run dev.

    4. We'll review where we configure out Keycloak instance. First open /auth.ts. In this file you will want to update it with the values for the Keycloak instance we set-up earlier in the tutorial. Update the clientSecret with the value. Use and environment variable here if you wish.

      export const keycloakConfig = {
      authorityUrl: "https://euc1.auth.ac",
      applicationUrl: "http://localhost:3000",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      clientSecret: "CLIENT_SECRET",
      };

      After the config, you can see how the OIDC instance is started.

      const settings = {
      authority: `${keycloakConfig.authorityUrl}/auth/realms/${keycloakConfig.realm}`,
      client_id: keycloakConfig.clientId,
      client_secret: keycloakConfig.clientSecret,
      redirect_uri: `${window.location.origin}/auth`,
      silent_redirect_uri: `${window.location.origin}/silent-refresh`,
      post_logout_redirect_uri: `${window.location.origin}`,
      response_type: "code",
      userStore: new WebStorageStateStore(),
      loadUserInfo: true,
      };
      this.userManager = new UserManager(settings);
    5. With the Keycloak instance defined, we attach this to the app instance for Vue. Switch to /main.ts

      import Auth from "@/auth";
      // ...
      app.config.globalProperties.$auth = Auth;

      We pull in the Auth instance then expose it through the $auth variable.

    6. There are a few main pages in play here that we define to create paths the library can leverage. The /view/auth and /view/silent-refresh create paths at the same name. These are used to do the redirection during authentication. From within these we use the Auth instance to direct the user around within the app. For instance in /views/AuthView:

      export default {
      name: "AuthAuthenticated",
      async mounted() {
      try {
      await this.$auth.signinCallback();
      this.$router.push("/");
      } catch (e) {
      console.error(e);
      }
      },
      };

      The router.push naively sends someone to the home page. This could be updated to go to any number of places, including the page one started the login flow from if you were to store that information to be retrieved.

    7. Now that we have all the things setup, we can define the user component /components/User to easily pull information about the user's state and display the appropriate UI.

      export default {
      name: "UserComponent",
      data() {
      return {
      user: null,
      signIn: () => this.$auth.signinRedirect(),
      logout: () => this.$auth.signoutRedirect(),
      };
      },
      async created() {
      const user = await this.$auth.getUser();
      if (user) {
      this.user = user;
      }
      },
      };

      With this, the user object is now easily available. A simple v-if="user" allows the app to determine what UI to show.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    · 7 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Nuxt example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Nuxt Project

    info

    We will use the Phase Two Nuxt example code here, but the logic could easily be applied to any existing application.

    This example uses Nuxt3. There are a couple methods by which you can integrate Keycloak to your Nuxt application. We're going to explore two methods here, one uses keycloak-js and the other leverages oidc-client-ts. The keycloak-js library provides a simple, client-only method, but lacks some of the sophistication provided by the oidc-client library that is heavily supported and more widely used.

    Using keycloak-js

    info

    For this example, we need to disable "Client Authentication" in the OIDC client that was setup earlier. This is available under Client > Settings > Capability config > Client authentication to OFF.

    1. Clone the Phase Two example repo.

    2. Open the Nuxt folder within /frameworks/nuxt and open the keycloak-js folder within /frameworks/nuxt/keycloak-js.

    3. Run npm install and then npm run dev. keycloak-js is a Javascript library that provides a fast way to secure an application.

    4. The project makes use of the following Nuxt items: components, composables, layouts, and plugins. We'll review each in kind.

    5. The main component that shows the User's authenticated state is in /components/User. In this component we call the useKeycloak composable, which let's us key into the keycloak-js functions that we've wrapped to make easily availble.

      const { keycloak, authState } = useKeycloak();

      function login() {
      keycloak.login();
      }

      function logout() {
      keycloak.logout();
      }

      Lower in the file the component leverages v-if checks to determine if the authState is authenticated or not. Depending on the state, a Log in or Log out button is available.

    6. Let's take a look at the setup for the composable next. Our composable is in /composables/keycloak-c. A composable is a function defined that can be called anywhere in the Nuxt application. It's a good way to abstract logic to be reused. In our case we use it to wrap a keycloak-js plugin (more on that in the next step) and help provided a state value for the authenticated state.

      export const useKeycloak = () => {
      const nuxtApp = useNuxtApp();
      const keycloak = nuxtApp.$keycloak as Keycloak;
      const authState = useState("authState", () => "unAuthenticated");

      keycloak.onAuthSuccess = () => (authState.value = "authenticated");
      keycloak.onAuthError = () => (authState.value = "error");

      return {
      keycloak,
      authState,
      };
      };
    7. In the plugin, /plugins/keycloak.client.ts we instantiate the keycloak-js library. We can then attach that instance to the NuxtApp instance. Substitute the correct values for your Keycloak instance that we created earlier in the tutorial.

      export default defineNuxtPlugin((nuxtApp) => {
      const initOptions: KeycloakConfig = {
      url: "https://euc1.auth.ac/auth/",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      };

      const keycloak = new Keycloak(initOptions);

      nuxtApp.$keycloak = keycloak;

      keycloak.init({
      onLoad: "check-sso",
      });
      });
    8. The logic for checking the authenticated state can be used to expand in ways to secure your site in a number of ways.

    Using oidc-client

    The oidc-client-ts package is a well-maintained and used library. It provides a lot of utilities for building out a fully production app.

    1. Clone the Phase Two example repo.

    2. Open the Nuxt folder within /frameworks/nuxt and open the /nuxt/oidc-client-ts folder.

    3. Run npm install and then npm run dev.

    4. The structure of the project is similar to the keycloak-js version but with a the use of services, stores, and middleware.

    5. We'll review where we configure out Keycloak instance. First open /services/keycloak-config.ts. In this file you will want to update it with the values for the Keycloak instance we set-up earlier in the tutorial. Make sure you are using the one with Client Authentication enabled. Update the clientSecret with the value. Use and environment variable here if you wish.

      export const keycloakConfig = {
      authorityUrl: "https://euc1.auth.ac",
      applicationUrl: "http://localhost:3000",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      clientSecret: "CLIENT_SECRET",
      };
    6. Switch over to the /services/auth-service now to see how the Oidc instance is started. The class pulls in values from the keycloakConfig to use in the constructor. The other functions are wrappers around methods provided by the oidc-client library. This allows us to key into things like signInRedirect and signoutRedirect.

      How the settings are integrated:

      const settings = {
      authority: `${keycloakConfig.authorityUrl}/auth/realms/${keycloakConfig.realm}`,
      client_id: keycloakConfig.clientId,
      client_secret: keycloakConfig.clientSecret,
      redirect_uri: `${window.location.origin}/auth`,
      silent_redirect_uri: `${window.location.origin}/silent-refresh`,
      post_logout_redirect_uri: `${window.location.origin}`,
      response_type: "code",
      userStore: new WebStorageStateStore(),
      loadUserInfo: true,
      };
      this.userManager = new UserManager(settings);

      Example function wrapper:

      public signInRedirect() {
      return this.userManager.signinRedirect();
      }
    7. With the AuthService defined, we can now expose that through a composable. Switch to the /composables/useServices file. The file is simple but provides a way for any component to hook into the service instance.

      import AuthService from "@/services/auth-service";
      import ApplicationService from "@/services/application-service";
      import { useAuth } from "@/stores/auth";

      export const useServices = () => {
      const authStore = useAuth();

      return {
      $auth: new AuthService(),
      $application: new ApplicationService(authStore.access_token),
      };
      };

      We pull in the AuthService then expose it through the $auth variable. The $application variable exposes the ApplicationService which is provided as an example of how you could secure API calls.

    8. We leverage the pinia library to make store User information to make it easily accessible. Open /stores/auth/index. From within this file, we can wrap the User object exposed by the oidc-client package. This can then be leveraged in the middleware function we want to define or to pull information quickly about the user.

    9. There are a few main pages in play here that we define to create paths the library can leverage. The /pages/auth, /pages/logout, /pages/silent-refresh create paths at the same name. These are used to do the redirection during authentication or log out. From within these we use the AuthService to direct the user around within the app. For instance in /auth:

      const authenticateOidc = async () => {
      try {
      await services.$auth.signInCallback();
      router.push("/");
      } catch (error) {
      console.error(error);
      }
      };

      await authenticateOidc();

      The router.push naively sends someone to the home page. This could be updated to go to any number of places, including the page one started the login flow from if you were to store that information to be retrieved.

    10. We have also created a middleware file in /middleware/auth.global to be used in a couple of ways. It checks if the user is authenticated and based on that knowledge, stores the user information in the store (if not there) or could be used to send someone to login. For our example, we created buttons to initiate that but there is a comment which shows how you could force a set of paths to require login.

      const authFlowRoutes = ["/auth", "/silent-refresh", "/logout"];

      export default defineNuxtRouteMiddleware(async (to, from) => {
      const authStore = useAuth();
      const services = useServices();
      const user = (await services.$auth.getUser()) as User;

      if (!user && !authFlowRoutes.includes(to.path)) {
      // use this to automatically force a sign in and redirect
      // services.$auth.signInRedirect();
      } else {
      authStore.setUpUserCredentials(user);
      }
      });
    11. Now that we have all the things setup, we can define the user component /components/User to easily pull information about the user's state and display the appropriate UI.

      const authStore = useAuth();
      const user = authStore.user;

      const signIn = () => services.$auth.signInRedirect();
      const signOut = () => services.$auth.logout();

      With this, the user object is now easily available. A simple v-if="user" allows the app to determine what UI to show.

    12. A bit more complicated of a setup, but more elegant in the handling of the logged in flow. The oidc-client allows for much better fine-tuning of the experience.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/tags/open-source/index.html b/blog/tags/open-source/index.html index 991111746..bb7961967 100644 --- a/blog/tags/open-source/index.html +++ b/blog/tags/open-source/index.html @@ -12,8 +12,8 @@ - - + + @@ -23,7 +23,7 @@ This mechanism inserts a authenticator in the login flow that intercepts the email address and sends the magic link in an email to to the user.

    We've also implemented a web service that allows you to create a magic link without necessarily sending an email. This will allow you to send the link through another channel. Specification for the new endpoint can be found in the Magic Link API Documentation.

    Both methods have the option of forcing the creation of a new user when an unknown email address is used. This allows a combination login/registration flow that combines an email verification. We think this really nails reducing friction in a new user flow.

    We're open sourcing the Keycloak extensionsso that the broad Keycloak community can benefit right away. We are doing this in line with our committment to keeping our core extensions open source. We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-magic-link

    · One min read

    Today we're open sourcing set of Keycloak extensions that are focused on solving several of the common use cases of multi-tenant, SaaS applications that Keycloak does not solve out of the box. We are doing this in line with our committment to keeping our core extensions open source. These extensions are the basis of our Organizations features, which allow Phase Two customers to model their own customers in their systems and create enterprise "team" functionality that suits their business case.

    A variation of this code has been built, enhanced and used in production by several customers for almost two years. It is now available as open source for members of the broader Keycloak community. We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-orgs

    · One min read

    Per our committment to keeping our core extensions open source, today we're releasing our Keycloak extensions to the event system. These extensions form the basis of how our Audit Log features are built.

    Additionally, we're providing several goodies that will be valuable to others building extensions on top of Keycloak, including a generic scriptable event listener, an event emitter to send events to any HTTP endpoint, a mechanism for retrieving event listener configurations from realm attributes, a mechanism for running multiple event listeners of the same type with different configurations, and a unified event model with facility for subscribing to webhooks.

    We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-events

    · 2 min read

    Following the initial release of Phase Two's authentication and SSO tools 3 months ago, we had a warm reception by several early- to mid- stage SaaS companies. The message was consistent. SSO was a key barrier to unlocking enterprise customers, and we had made it much easier to quickly integrate the alphabet-soup of enterprise identity providers.

    Furthermore, many of our customers have responded well to our "one price per project" idea, citing that competitors and other enterprise authentication companies had pricing models that ramped on a per-user and per-SSO connection basis, making them economically unattractive to companies with business and pricing models that couldn't support that.

    One of the other points that we heard loud and clear from our first customers, was the fear of vendor lock-in. Integrating tools like this can be a large effort, and can be difficult to unwind if the terms or service fall short. While our adoption of standards such as OpenID and SAML allayed some of those fears, we wanted to go a step further.

    We built the initial verison of Phase Two as a set of extensions to the Keycloak Open Source Identity and Access Management system, built and maintained by Red Hat. After several months of developing for it, and operating it for our customers, we've decided to continue using it. Keycloak has been battle-tested and hardened for over 6 years. It's security and reliability is depended on by organizations from small startups to Fortune 500 companies and governments.

    To put to rest any future concerns about vendor lock-in, we're committing to making our core extensions to Keycloak open source. While we will endeavor to make Phase Two simple to use, operate and scale, we will maintain compatibility so that customers can migrate to their own Keycloak deployment. Updates and links to our open source extensions will be published in the Open Source section of the documentation, and will be available in our p2-inc GitHub organization page.

    We have benefitted immensely from the open source communitiy, and we are excited to give back!

    - + \ No newline at end of file diff --git a/blog/tags/oss/index.html b/blog/tags/oss/index.html index 34a2e2ed7..3abff8640 100644 --- a/blog/tags/oss/index.html +++ b/blog/tags/oss/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    One post tagged with "oss"

    View All Tags

    · 2 min read

    tl;dr

    We’ve changed the license of our core extensions from the AGPL v3 to the Elastic License v2. We wanted to share why we made this change and what it means for our customers and community.

    Why?

    From our earliest stages, Phase Two has been built as a set of extensions to Keycloak. We have made a commitment that source code for our core exetensions will always be available so that our customers can migrate to their own deployment, while maintaining the extension functionality provided by Phase Two.

    As our product matured and found a market fit, it became clear to us that our current license was failing to give our customers that guarantee, and failing to give our company the protection to ensure we could build a business and continue to invest in our extensions.

    We've had some very deep conversations with customers and contributors about the future of licensing, and learned a lot about what other companies in our shoes have done. We truly appreciate all of the engagement and feedback, and have done our best to make a decision that is in the best interest of our company, customers and community.

    What's allowed?

    1. Hosting and using the extensions by companies as part of their own product.
    2. Derivative works that maintain the same license.

    What's prohibited?

    1. Providing the extensions as a hosted or managed service. This includes bundling and distribution by companies who sell their products for on-prem and private cloud use.

    We believe that the Elastic License strikes a great balance, and we're excited for our next phase of growth!

    - + \ No newline at end of file diff --git a/blog/tags/phase-two/index.html b/blog/tags/phase-two/index.html index a7430ada9..a5d6bea4b 100644 --- a/blog/tags/phase-two/index.html +++ b/blog/tags/phase-two/index.html @@ -12,8 +12,8 @@ - - + + @@ -28,7 +28,7 @@

    Images and other assets

    You'll notice that images, icons and fonts not found in the standard Keycloak themes are used. These are referenced by URL in the CSS. For example, if you are used to loading a custom font in the <head> of your HTML, it is possible to do it in CSS using @import:

    @import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Lexend+Deca:wght@300;400;500;600;700&family=Lexend:wght@300;400;500;600;700&family=Work+Sans:wght@300;400;500;600;700&display=swap");

    And background and other images can be referenced by their full URL using url(...):

    .login-pf body {
    background-image: url("https://raw.githubusercontent.com/p2-inc/keycloak-themes/main/examples/saas/assets/SaaS%20BG.webp");
    }

    Success!

    As always, our success is based on the success of our customers. We hope this extension and guide has helped you update the default Keycloak login branding to match that of your needs. If you have suggestions for further improvement of this feature, please reach out on GitHub!

    · 2 min read

    tl;dr

    We’ve changed the license of our core extensions from the AGPL v3 to the Elastic License v2. We wanted to share why we made this change and what it means for our customers and community.

    Why?

    From our earliest stages, Phase Two has been built as a set of extensions to Keycloak. We have made a commitment that source code for our core exetensions will always be available so that our customers can migrate to their own deployment, while maintaining the extension functionality provided by Phase Two.

    As our product matured and found a market fit, it became clear to us that our current license was failing to give our customers that guarantee, and failing to give our company the protection to ensure we could build a business and continue to invest in our extensions.

    We've had some very deep conversations with customers and contributors about the future of licensing, and learned a lot about what other companies in our shoes have done. We truly appreciate all of the engagement and feedback, and have done our best to make a decision that is in the best interest of our company, customers and community.

    What's allowed?

    1. Hosting and using the extensions by companies as part of their own product.
    2. Derivative works that maintain the same license.

    What's prohibited?

    1. Providing the extensions as a hosted or managed service. This includes bundling and distribution by companies who sell their products for on-prem and private cloud use.

    We believe that the Elastic License strikes a great balance, and we're excited for our next phase of growth!

    · 4 min read

    We've received a lot of support requests about the right way to set up SSO connections. We've published a 5 minute video showing you how to do it easily. Also, the script is included below in case you miss anything!

    Script

    Developers have been asking for concise setup instructions for SSO. You’re here for our enterprise SSO functionality. We hear you. Here’s a quick live setup to show you how easy it is.

    I’m assuming you have already created a self-serve deployment.

    • Start by opening the console.
    • Navigate to the Organizations tab.
    • Create a new organization. In this case, set a domain when creating an organization that corresponds to the email domains you want to match to their SSO provider.
    • Create a Portal Link by selecting it from the action menu of the organization. This is usually meant to be sent to the organization administrator, but in this case, we’ll open this link in an incognito window and configure the identity provider ourselves.

    We’ll use the identity provider wizard to setup a SSO connection to Azure AD. I’ve sped this up a bit, but you’ll get an idea of what is happening. Depending on your setup and target identity provider, this will be different.

    Now let’s secure an application and use our new SSO connection to log in. For this purpose, we’ve got a debugging application on Github you can use to quickly see how a front end application is secured and what data is shared between Phase Two and the application.

    • Clone our debug-app from github, and open up the frontend folder in your favorite editor or IDE.
    • Go back to the admin console and navigate to the Clients tab. Create a new client. Let’s call it frontend. This will be a public OIDC client, with localhost:3001 as the root and redirect uri.
    • Get the keycloak.json from the configuration and copy it. Paste it into keycloak.json to configure your debug-app.

    Before we continue, we need to configure an authentication flow that does our SSO redirect.

    • Navigate to the Authentication tab, and duplicate the Browser flow.
    • Add the Home IdP Discovery authenticator and move it into the position before the user forms. Configure it to not require a verified domain nor email.
    • Finally, using the action menu, bind it to the browser flow.

    Go back to the debug-app, and let’s try a login using an email domain that matches the one we configured.

    • First, run npm i and npm start to start the debug app, and navigate to http://localhost:3001 in your browser. See that it redirects to the default login.
    • Enter the email address in the new email only form.
    • We are redirected to the Azure identity provider we set up.
    • Log in to Azure, and then we are redirected to back to debug-app.
    • Let’s take a look at the token and see the data that came over from Azure.

    As a bonus, let’s map some information about the user’s organization memberships into the token in case we need to do something with that information in our application.

    • Go back to the Admin UI and navigate to the Client we created.
    • Select the frontend-dedicated client scope.
    • Add a mapper by configuration.
    • Select the Organization Role mapper and configure it as shown.
    • Save the configuration.

    Now let’s go back to the debug-app and reload.

    • Take a look at the token. It now contains information about the organization we created.
    • The user was automatically created and added as a member to the organization when we logged in through the Azure identity provider.

    You now have a fully working authentication and enterprise SSO setup for your application. It took about 5 minutes!

    · 6 min read

    There are a lot of guides out there, official and unofficial, for how to secure applications with Keycloak. The subject is rather broad, so it's difficult to know where to start. To begin, we'll be focusing on Keycloak's use of OpenID Connect (OIDC), and how to use that standard, along with some helpful libraries, to secure a simple but instructive application.

    For the purposes of the sample, we'll actually be using two common applications, a frontend single-page application (SPA) written in JavaScript, and a backend REST API written for Node.js. The language we selected for the sample is JavaScript, but the principles apply no matter the implementation technology you choose.

    What is OIDC?

    When learning about identity and access management technologies, you'll be confronted with an alphabet-soup of acronyms to learn. OIDC, or OpenID Connect is one of the most important ones for securing applications, be it browser-based, APIs, mobile or native. Our friend over at OneLogin does a great job of explaining OIDC in plain english for those that are curious.

    For the purpose of this guide, it is sufficient to know that OIDC is an open authentication protocol that works on top of the OAuth 2.0 framework. OIDC allows individuals to use single sign-on (SSO) to access relying party sites using OpenID Providers (OPs), such as an email provider or social network, to authenticate their identities. It provides the application or service with information about the user, the context of their authentication, and access to their profile information.

    Login flow

    A "flow" in OIDC terms is a mechanism of authenticating a user, and obtaning access tokens. The flow we'll be using in this guide is called the authorization code flow. Fortunately, the internals of the flow are not necessary to understand, as Keycloak handles the details for you.

    However, it is useful to see what is going on in the login process, so that you understand your user's experience.

    Setup

    We'll make an assumption for this guide that you are using a cloud deployment of Phase Two enhanced Keycloak. If you haven't already set one up, go over to the self-service launch announcement for details. You may also use your own Keycloak setup, but that setup is beyond the scope of this article.

    The sample applications are available in our demo repo on Github. Clone that repo to your local machine. You'll find the applications in the frontend and backend directory, and a set of supporting files for configuration and deployment.

    git clone https://github.com/p2-inc/debug-app.git
    cd debug-app

    Client

    Every application that Keycloak protects is considered a Client. Log into your Keycloak realm, and click on Clients in the left navigation, and click Create client.

    1. Enter frontend as the Client ID and click Next
    2. In the Capability config screen, keep the defaults and click Save
    3. In the Access settings screen, enter the following values:
      1. http://localhost:3001/* for Valid redirect URIs
      2. + in Valid post logout redirect URIs
      3. + in Web origins
    4. Click Save
    5. In the upper right corner, open the Action menu and select Download adapter config. Click Download and move the file to the debug-app repo you cloned under the frontend folder.

    Make a user

    Before we run the application, we need to create a user to log in. Click on Users in the left navigation, and click Add user. You only need to give the user a username and click Create. Find the Credentials tab and click Set password to give the user a password.

    Running the sample apps

    Open two terminal windows and go to the directory of the repo you cloned in both. To start, run the following commands in each terminal:

    Frontend:

    cd frontend/
    npm install
    npm start

    Backend:

    cd backend/
    npm install
    KC_REALM=<your-realm-name> KC_URL=<your-keycloak-url> npm start

    Be sure to replace the realm name, and the URL of your Keycloak installation (e.g. https://usw2.auth.ac/auth/).

    This will install the necessary components using npm, and will start the servers for both applications. Note: the applications use ports 3001 and 3002 by default. If you have other applications running on these ports, you may have to temporarily shut them down.

    Putting it all together

    Load http://localhost:3001/ in your browser. This will load the frontend application, which will be immediately redirected to the Keycloak login page. Log in with the user you created.

    Once you log in, you'll see your profile, and several menu items. First click to the Access token menu item. You'll see the information from the parsed access token that was returned by Keycloak. This contains information about the user, but also claims related to the users roles and groups. This is because access tokens are meant to be read and validated by resource servers (i.e. our backend service).

    Next, click ID token. You'll see similar information to what we saw in the access token, but limited to a standardized set of information that identifies the authentication state of the user. ID tokens are not meant for calling resource servers, and because of that, don't contain claims that are meant to be validated by backend services.

    Clicking Service will call the backend service. You'll see a message that indicates the frontend called the backend, passing the access token, and was authorized to access a secured service.

    You can also try the built-in Keycloak Account Management console by clicking Account, which gives the user a simple way to manage their information that is stored in Keycloak. It is not necessary to use this with your applications, as you may choose to build it in to your app. However, it's a good tool to have out of the box.

    Finally, clicking Logout will take you back to the login page. This is actually sending you to the frontend's initial page, which is redirecting you to the login page as its default behavior.

    What just happened?

    There's a lot that goes into implementation of the OIDC flow we used to secure our sample applications. Part of the reason to use Keycloak is the mature implementation and client libraries that make protecting applications in a secure way almost trivial.

    We encourage you to look at the source in the sample applications (specifically frontend/app.js and backend/app.js) and observe how the Keycloak client libraries are used to secure these applications. This will be a good place to start when you are working on securing your own applications.

    Your application

    Another incredible advantage to using standards like OIDC is that you are not constrained to using Keycloak libraries. Because your applications may not be written in JavaScript also, it's easy to use other language OIDC client libraries. We maintain a list of OIDC libraries, and the OpenID Foundation also maintains lists of certified and uncertified implementations

    Today you saw how to quickly secure an application using Keycloak, and learned more about the underlying OIDC standards. We look forward to seeing what you build!

    - + \ No newline at end of file diff --git a/blog/tags/phase-two/page/2/index.html b/blog/tags/phase-two/page/2/index.html index 2b9371c93..6a060289c 100644 --- a/blog/tags/phase-two/page/2/index.html +++ b/blog/tags/phase-two/page/2/index.html @@ -12,8 +12,8 @@ - - + + @@ -24,7 +24,7 @@ This mechanism inserts a authenticator in the login flow that intercepts the email address and sends the magic link in an email to to the user.

    We've also implemented a web service that allows you to create a magic link without necessarily sending an email. This will allow you to send the link through another channel. Specification for the new endpoint can be found in the Magic Link API Documentation.

    Both methods have the option of forcing the creation of a new user when an unknown email address is used. This allows a combination login/registration flow that combines an email verification. We think this really nails reducing friction in a new user flow.

    We're open sourcing the Keycloak extensionsso that the broad Keycloak community can benefit right away. We are doing this in line with our committment to keeping our core extensions open source. We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-magic-link

    · One min read

    Today we're open sourcing set of Keycloak extensions that are focused on solving several of the common use cases of multi-tenant, SaaS applications that Keycloak does not solve out of the box. We are doing this in line with our committment to keeping our core extensions open source. These extensions are the basis of our Organizations features, which allow Phase Two customers to model their own customers in their systems and create enterprise "team" functionality that suits their business case.

    A variation of this code has been built, enhanced and used in production by several customers for almost two years. It is now available as open source for members of the broader Keycloak community. We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-orgs

    · One min read

    Following our post about our wizard product, we received an overwhelming amount of interest in it. Many customers of our cloud offering asked for it as a portal for their organization administrators to set up their identity providers. On-prem customers said that one consistent onboarding hurdle was SSO complexity, and asked for it to be included in the bundled distribution.

    Today we're pleased to report that we've listened to both use cases and completed embedding the "wizard" product into Phase Two. We're calling it "Connect", as it's the best way we could come up with characterizing its simplicity. It massively reduces the complexit of configuring SSO connections, and distills the process into something any member of the team can understand.

    Phase Two Connect is currently available by invitation only while we work out the final kinks. Contact sales for more information.

    · One min read

    Working with one of our customers, we discovered that even the most technically literate developer or ops professional could look at the configuration for an SSO connection like it was a foreign language. While our configuration interface attempts to cover all possible options, and document clearly what each option means, it can still be entirely unclear what is required during a setup.

    Furthermore, the identity provider that is being integrated can present a similarly extensive interface that may not use the same terms and language. However, after investigation into the most common identity providers, we found that most of the configuration options can simply be set by convention if the vendor is known.

    Based on that observation, we've built what we call a "wizard" UI on top of our identity provider configuration to make it easy to integration the top commercial identity provider vendors. Take a look at a quick video of a setup using our most recent prototype.

    If you're interested in early access to our "wizards", please contact us today.

    · One min read

    Per our committment to keeping our core extensions open source, today we're releasing our Keycloak extensions to the event system. These extensions form the basis of how our Audit Log features are built.

    Additionally, we're providing several goodies that will be valuable to others building extensions on top of Keycloak, including a generic scriptable event listener, an event emitter to send events to any HTTP endpoint, a mechanism for retrieving event listener configurations from realm attributes, a mechanism for running multiple event listeners of the same type with different configurations, and a unified event model with facility for subscribing to webhooks.

    We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-events

    · 2 min read

    Since our release about basing Phase Two on the Keycloak Open Source Identity and Access Management system, an our committment to keeping our core extensions open source, we've received positive feedback from customers and interest from the the Keycloak community.

    We've noticed that support forums for Keycloak have many questions and requests around just getting started. Even though the software is mature, open source, and has a helpfu user community, just spinning up an environment and trying it out can be puzzling for first-time users. It's pretty clear that a lot of people just give up, because they can't get a server running, let alone configure their first realm1.

    Because of that, we've decided to offer developers a FREE realm in Phase Two, so those that are interested in trying it out can get successful quickly. Free realms are limited to fewer than 1000 users and 5 SSO connections. Otherwise, there are no restrictions beyond abiding by our terms of use. Sound good? Please fill out the contact form with your company information, and we'll respond with access information for your realm.


    1. In Keycloak, a "realm" manages a set of users, credentials, roles, and groups. A user belongs to and logs into a realm. Realms are isolated from one another and can only manage and authenticate the users that they control.

    · 2 min read

    Following the initial release of Phase Two's authentication and SSO tools 3 months ago, we had a warm reception by several early- to mid- stage SaaS companies. The message was consistent. SSO was a key barrier to unlocking enterprise customers, and we had made it much easier to quickly integrate the alphabet-soup of enterprise identity providers.

    Furthermore, many of our customers have responded well to our "one price per project" idea, citing that competitors and other enterprise authentication companies had pricing models that ramped on a per-user and per-SSO connection basis, making them economically unattractive to companies with business and pricing models that couldn't support that.

    One of the other points that we heard loud and clear from our first customers, was the fear of vendor lock-in. Integrating tools like this can be a large effort, and can be difficult to unwind if the terms or service fall short. While our adoption of standards such as OpenID and SAML allayed some of those fears, we wanted to go a step further.

    We built the initial verison of Phase Two as a set of extensions to the Keycloak Open Source Identity and Access Management system, built and maintained by Red Hat. After several months of developing for it, and operating it for our customers, we've decided to continue using it. Keycloak has been battle-tested and hardened for over 6 years. It's security and reliability is depended on by organizations from small startups to Fortune 500 companies and governments.

    To put to rest any future concerns about vendor lock-in, we're committing to making our core extensions to Keycloak open source. While we will endeavor to make Phase Two simple to use, operate and scale, we will maintain compatibility so that customers can migrate to their own Keycloak deployment. Updates and links to our open source extensions will be published in the Open Source section of the documentation, and will be available in our p2-inc GitHub organization page.

    We have benefitted immensely from the open source communitiy, and we are excited to give back!

    - + \ No newline at end of file diff --git a/blog/tags/phase-two/page/3/index.html b/blog/tags/phase-two/page/3/index.html index d6146eaa4..7e31ede6e 100644 --- a/blog/tags/phase-two/page/3/index.html +++ b/blog/tags/phase-two/page/3/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    21 posts tagged with "phase_two"

    View All Tags

    · 3 min read

    After building and working for startups and technology companies for almost 25 years, I found myself having a sense of déjà-vu.

    Had I really built the same features and functionality over and over?

    Everyone of us who has been in the industry for any length of time probably has the same feeling. Whether or not we are fully conscious of it, we probably built a (bad) version of login, registration, user management, authorization, organizations and invitations, audit logging, etc. at least one time for every company we've worked for.

    In early 2018, I joined an enterprise SaaS startup, where I built the initial team and product over 18 months. Analyzing tickets and epics in the project tracking system we used, I found that over 60% of our first 18 months was spent building features and functionality like this -- essential building blocks, but not the core competency we sought to test in the marketplace. And the result of that effort was only adequate versions of those common features, which resulted in less time spent on what we were trying to prove. I began to refer to this heavy tax a "SaaS CRUD".

    Was this really what everyone else was doing? I was lucky to have a large network of engineering leaders at companies that ranged from the earliest stages to the largest public companies, so I asked them. The responses were remarkably consisitent. Early stage companies wished there was something comprehensive they could adopt, and later stage and large companies lamented not adopting external tools earlier that gave them guarantees around uptime and compliance. All were aware of or had tried to knit together a mish-mash of "feature company" products, and all expressed dissatisfaction with "model mismatch" of most of the tools in the marketplace, which demanded more integration overhead than the perceived benefit allowed.

    I was lucky to find others that had observed the same thing. We joined forces and spent the next 6 months interviewing companies of a range of sizes, developing a playbook for companies building new products. Based on that playbook, today we are releasing our first version of tooling designed to help application developers avoid rebuilding "SaaS CRUD".

    Phase Two is designed to help SaaS companies accelerate time-to-market. Authentication and SSO are our first targets, but we plan to expand to many other areas of pain for the growing company seeking enterprise adoption. We'd love for you to join us on this journey. For product demos and to become a beta customer, please contact us!

    - + \ No newline at end of file diff --git a/blog/tags/react/index.html b/blog/tags/react/index.html index c1461cb7c..b48e3164c 100644 --- a/blog/tags/react/index.html +++ b/blog/tags/react/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    2 posts tagged with "react"

    View All Tags

    · 4 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Next.js example. We also have a plain React example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Next.js Project

    info

    We will use the Phase Two Next.js example code here, but the logic could easily be applied to any existing application.

    This example uses Next.js 13 and splits server and client components accordingly.

    1. Clone the Phase Two example repo.

    2. Open the Next.js folder within /frameworks/nextjs.

    3. Run npm install and then npm run dev. This example leverages NextAuth.js to provide hook and HOC support.

    4. NextAuth.js configures an API route that is uses for the Authentication of the Client. It generates the routes automatically for you. These are added to Next.js in the api/auth/[...nextauth]/route.ts file.

    5. Open the src/lib/auth.ts file. This is a server only file. We will be updating a few values from the prior section where we set up our OIDC client. Taking the values from the OIDC Client Config section, set those values in the code. While it is recommended to use Environment variables for the secret, for the purpose of this tutorial, paste in the Client secret from the OIDC client creation section for the value of clientSecret

      const authServerUrl = "https://euc1.auth.ac/auth/";
      const realm = "shared-deployment-001";
      const clientId = "reg-example-1";
      const clientSecret = "CLIENT_SECRET"; // Paste "Client secret" here. Use Environment variables in prod

      Those are used to popluate the AuthOptions config for the KeycloakProvider:

      export const AuthOptions: NextAuthOptions = {
      providers: [
      KeycloakProvider({
      clientId,
      clientSecret,
      issuer: `${authServerUrl}realms/${realm}`,
      }),
      ],
      };

      The config is then provided to the AuthProvider in the /src/app/layout.tsx file. Next.js uses this file to generate an HTML view for this page.

      import { NextAuthProvider as AuthProvider } from "./providers";
      ...
      <AuthProvider {...oidcConfig}>
      <App />
      </AuthProvider>

      At this point our entire application will be able to access all information and methods needed to perform authentication. View the providers.tsx file for additional information about how the SessionProvider is used. The SessionProvider enables use of Hooks to derive the authenticated state. View user.component.tsx for exactly how the code is authenticating your user. The sections rendering the "Log in" and "Log out" buttons are conditional areas based on the authenticated context. The buttons invoke functions provided by NextAuth.

      The logic using the hook to conditionally determine the Authenticated state, can be used to secure routes, components, and more.

    6. Open localhost:3000. You will see the Phase Two example landing page. You current state should be "Not authenticated". Click Log In. This will redirect you to your login page.

      info

      Use the non-admin user created in the previous section to sign in.

    7. Enter the credentials of the non-admin user you created. Click Submit. You will then be redirected to the application. The Phase Two example landing page now loads your "Authenticated" state, displaying your user's email and their Token.

    8. After your first log in, click Log out. Then click Log in again. Notice how this time you will not be redirected to sign in as your state is already in the browser. Neat! If you clear the browser state for that tab, then you will have to be redirected away to sign-in again.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    · 3 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two ReactJS example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to Off.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a ReactJS Project

    info

    We will use the Phase Two ReactJS example code here, but the logic could easily be applied to any existing application.

    1. Clone the Phase Two example repo.

    2. Open the ReactJS folder within /frameworks/reactjs.

    3. Run npm install and then npm start. This example leverages react-oidc-context (which uses oidc-client-ts) to provide hook and HOC support.

    4. Open the index.tsx file. We will be updating a few values from the prior section where we set up our OIDC client. Taking the values from the OIDC Client Config section, set those values in the code.

      const authServerUrl = "https://euc1.auth.ac/auth/";
      const realm = "shared-deployment-001";
      const client = "reg-example-1";

      Those are used to popluate the OIDC config

      const oidcConfig = {
      authority: `${authServerUrl}realms/${realm}`,
      client_id: client,
      redirect_uri: "http://localhost:3000/authenticated",
      onSigninCallback: (args: any) =>
      window.history.replaceState(
      {},
      document.title,
      window.location.pathname
      ),
      };

      The config is then provided to the AuthProvider.

      <AuthProvider {...oidcConfig}>
      <App />
      </AuthProvider>

      At this point our entire applicationw will be able to access all information and methods needed to perform authentication. View Auth.tsx for exactly how the code is authenticating your user. The sections rendering the "Log in" and "Log out" buttons are conditional areas based on the authenticated context.

      The logic using the hook to conditionally determine the Authenticated state, can be used to secure routes, components, and more.

    5. Open localhost:3000. You will see the Phase Two example landing page. You current state should be "Not authenticated". Click Log In. This will redirect you to your login page.

      info

      Use the non-admin user created in the previous section to sign in.

    6. Enter the credentials of the non-admin user you created. Click Submit. You will then be redirected to the application. The Phase Two example landing page now loads your "Authenticated" state, displaying your user's email and their Token.

    7. After your first log in, click Log out. Then click Log in again. Notice how this time you will not be redirected to sign in as your state is already in the browser. Neat! If you clear the browser state for that tab, then you will have to be redirected away to sign-in again.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/tags/reactjs/index.html b/blog/tags/reactjs/index.html index e54bac895..f41dc4aa5 100644 --- a/blog/tags/reactjs/index.html +++ b/blog/tags/reactjs/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    2 posts tagged with "reactjs"

    View All Tags

    · 4 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Next.js example. We also have a plain React example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Next.js Project

    info

    We will use the Phase Two Next.js example code here, but the logic could easily be applied to any existing application.

    This example uses Next.js 13 and splits server and client components accordingly.

    1. Clone the Phase Two example repo.

    2. Open the Next.js folder within /frameworks/nextjs.

    3. Run npm install and then npm run dev. This example leverages NextAuth.js to provide hook and HOC support.

    4. NextAuth.js configures an API route that is uses for the Authentication of the Client. It generates the routes automatically for you. These are added to Next.js in the api/auth/[...nextauth]/route.ts file.

    5. Open the src/lib/auth.ts file. This is a server only file. We will be updating a few values from the prior section where we set up our OIDC client. Taking the values from the OIDC Client Config section, set those values in the code. While it is recommended to use Environment variables for the secret, for the purpose of this tutorial, paste in the Client secret from the OIDC client creation section for the value of clientSecret

      const authServerUrl = "https://euc1.auth.ac/auth/";
      const realm = "shared-deployment-001";
      const clientId = "reg-example-1";
      const clientSecret = "CLIENT_SECRET"; // Paste "Client secret" here. Use Environment variables in prod

      Those are used to popluate the AuthOptions config for the KeycloakProvider:

      export const AuthOptions: NextAuthOptions = {
      providers: [
      KeycloakProvider({
      clientId,
      clientSecret,
      issuer: `${authServerUrl}realms/${realm}`,
      }),
      ],
      };

      The config is then provided to the AuthProvider in the /src/app/layout.tsx file. Next.js uses this file to generate an HTML view for this page.

      import { NextAuthProvider as AuthProvider } from "./providers";
      ...
      <AuthProvider {...oidcConfig}>
      <App />
      </AuthProvider>

      At this point our entire application will be able to access all information and methods needed to perform authentication. View the providers.tsx file for additional information about how the SessionProvider is used. The SessionProvider enables use of Hooks to derive the authenticated state. View user.component.tsx for exactly how the code is authenticating your user. The sections rendering the "Log in" and "Log out" buttons are conditional areas based on the authenticated context. The buttons invoke functions provided by NextAuth.

      The logic using the hook to conditionally determine the Authenticated state, can be used to secure routes, components, and more.

    6. Open localhost:3000. You will see the Phase Two example landing page. You current state should be "Not authenticated". Click Log In. This will redirect you to your login page.

      info

      Use the non-admin user created in the previous section to sign in.

    7. Enter the credentials of the non-admin user you created. Click Submit. You will then be redirected to the application. The Phase Two example landing page now loads your "Authenticated" state, displaying your user's email and their Token.

    8. After your first log in, click Log out. Then click Log in again. Notice how this time you will not be redirected to sign in as your state is already in the browser. Neat! If you clear the browser state for that tab, then you will have to be redirected away to sign-in again.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    · 3 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two ReactJS example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to Off.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a ReactJS Project

    info

    We will use the Phase Two ReactJS example code here, but the logic could easily be applied to any existing application.

    1. Clone the Phase Two example repo.

    2. Open the ReactJS folder within /frameworks/reactjs.

    3. Run npm install and then npm start. This example leverages react-oidc-context (which uses oidc-client-ts) to provide hook and HOC support.

    4. Open the index.tsx file. We will be updating a few values from the prior section where we set up our OIDC client. Taking the values from the OIDC Client Config section, set those values in the code.

      const authServerUrl = "https://euc1.auth.ac/auth/";
      const realm = "shared-deployment-001";
      const client = "reg-example-1";

      Those are used to popluate the OIDC config

      const oidcConfig = {
      authority: `${authServerUrl}realms/${realm}`,
      client_id: client,
      redirect_uri: "http://localhost:3000/authenticated",
      onSigninCallback: (args: any) =>
      window.history.replaceState(
      {},
      document.title,
      window.location.pathname
      ),
      };

      The config is then provided to the AuthProvider.

      <AuthProvider {...oidcConfig}>
      <App />
      </AuthProvider>

      At this point our entire applicationw will be able to access all information and methods needed to perform authentication. View Auth.tsx for exactly how the code is authenticating your user. The sections rendering the "Log in" and "Log out" buttons are conditional areas based on the authenticated context.

      The logic using the hook to conditionally determine the Authenticated state, can be used to secure routes, components, and more.

    5. Open localhost:3000. You will see the Phase Two example landing page. You current state should be "Not authenticated". Click Log In. This will redirect you to your login page.

      info

      Use the non-admin user created in the previous section to sign in.

    6. Enter the credentials of the non-admin user you created. Click Submit. You will then be redirected to the application. The Phase Two example landing page now loads your "Authenticated" state, displaying your user's email and their Token.

    7. After your first log in, click Log out. Then click Log in again. Notice how this time you will not be redirected to sign in as your state is already in the browser. Neat! If you clear the browser state for that tab, then you will have to be redirected away to sign-in again.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/tags/release/index.html b/blog/tags/release/index.html index e962b2e1a..3fd067bcb 100644 --- a/blog/tags/release/index.html +++ b/blog/tags/release/index.html @@ -12,8 +12,8 @@ - - + + @@ -23,7 +23,7 @@ This mechanism inserts a authenticator in the login flow that intercepts the email address and sends the magic link in an email to to the user.

    We've also implemented a web service that allows you to create a magic link without necessarily sending an email. This will allow you to send the link through another channel. Specification for the new endpoint can be found in the Magic Link API Documentation.

    Both methods have the option of forcing the creation of a new user when an unknown email address is used. This allows a combination login/registration flow that combines an email verification. We think this really nails reducing friction in a new user flow.

    We're open sourcing the Keycloak extensionsso that the broad Keycloak community can benefit right away. We are doing this in line with our committment to keeping our core extensions open source. We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-magic-link

    · One min read

    Today we're open sourcing set of Keycloak extensions that are focused on solving several of the common use cases of multi-tenant, SaaS applications that Keycloak does not solve out of the box. We are doing this in line with our committment to keeping our core extensions open source. These extensions are the basis of our Organizations features, which allow Phase Two customers to model their own customers in their systems and create enterprise "team" functionality that suits their business case.

    A variation of this code has been built, enhanced and used in production by several customers for almost two years. It is now available as open source for members of the broader Keycloak community. We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-orgs

    · One min read

    Following our post about our wizard product, we received an overwhelming amount of interest in it. Many customers of our cloud offering asked for it as a portal for their organization administrators to set up their identity providers. On-prem customers said that one consistent onboarding hurdle was SSO complexity, and asked for it to be included in the bundled distribution.

    Today we're pleased to report that we've listened to both use cases and completed embedding the "wizard" product into Phase Two. We're calling it "Connect", as it's the best way we could come up with characterizing its simplicity. It massively reduces the complexit of configuring SSO connections, and distills the process into something any member of the team can understand.

    Phase Two Connect is currently available by invitation only while we work out the final kinks. Contact sales for more information.

    · One min read

    Working with one of our customers, we discovered that even the most technically literate developer or ops professional could look at the configuration for an SSO connection like it was a foreign language. While our configuration interface attempts to cover all possible options, and document clearly what each option means, it can still be entirely unclear what is required during a setup.

    Furthermore, the identity provider that is being integrated can present a similarly extensive interface that may not use the same terms and language. However, after investigation into the most common identity providers, we found that most of the configuration options can simply be set by convention if the vendor is known.

    Based on that observation, we've built what we call a "wizard" UI on top of our identity provider configuration to make it easy to integration the top commercial identity provider vendors. Take a look at a quick video of a setup using our most recent prototype.

    If you're interested in early access to our "wizards", please contact us today.

    · One min read

    Per our committment to keeping our core extensions open source, today we're releasing our Keycloak extensions to the event system. These extensions form the basis of how our Audit Log features are built.

    Additionally, we're providing several goodies that will be valuable to others building extensions on top of Keycloak, including a generic scriptable event listener, an event emitter to send events to any HTTP endpoint, a mechanism for retrieving event listener configurations from realm attributes, a mechanism for running multiple event listeners of the same type with different configurations, and a unified event model with facility for subscribing to webhooks.

    We hope you find these extensions valuable, and we look forward to feedback and participation from both our customers and the wider Keycloak community.

    The extension is available on GitHub https://github.com/p2-inc/keycloak-events

    · 3 min read

    After building and working for startups and technology companies for almost 25 years, I found myself having a sense of déjà-vu.

    Had I really built the same features and functionality over and over?

    Everyone of us who has been in the industry for any length of time probably has the same feeling. Whether or not we are fully conscious of it, we probably built a (bad) version of login, registration, user management, authorization, organizations and invitations, audit logging, etc. at least one time for every company we've worked for.

    In early 2018, I joined an enterprise SaaS startup, where I built the initial team and product over 18 months. Analyzing tickets and epics in the project tracking system we used, I found that over 60% of our first 18 months was spent building features and functionality like this -- essential building blocks, but not the core competency we sought to test in the marketplace. And the result of that effort was only adequate versions of those common features, which resulted in less time spent on what we were trying to prove. I began to refer to this heavy tax a "SaaS CRUD".

    Was this really what everyone else was doing? I was lucky to have a large network of engineering leaders at companies that ranged from the earliest stages to the largest public companies, so I asked them. The responses were remarkably consisitent. Early stage companies wished there was something comprehensive they could adopt, and later stage and large companies lamented not adopting external tools earlier that gave them guarantees around uptime and compliance. All were aware of or had tried to knit together a mish-mash of "feature company" products, and all expressed dissatisfaction with "model mismatch" of most of the tools in the marketplace, which demanded more integration overhead than the perceived benefit allowed.

    I was lucky to find others that had observed the same thing. We joined forces and spent the next 6 months interviewing companies of a range of sizes, developing a playbook for companies building new products. Based on that playbook, today we are releasing our first version of tooling designed to help application developers avoid rebuilding "SaaS CRUD".

    Phase Two is designed to help SaaS companies accelerate time-to-market. Authentication and SSO are our first targets, but we plan to expand to many other areas of pain for the growing company seeking enterprise adoption. We'd love for you to join us on this journey. For product demos and to become a beta customer, please contact us!

    - + \ No newline at end of file diff --git a/blog/tags/sso/index.html b/blog/tags/sso/index.html index 034caa82b..8fe382af5 100644 --- a/blog/tags/sso/index.html +++ b/blog/tags/sso/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    2 posts tagged with "sso"

    View All Tags

    · 4 min read

    We've received a lot of support requests about the right way to set up SSO connections. We've published a 5 minute video showing you how to do it easily. Also, the script is included below in case you miss anything!

    Script

    Developers have been asking for concise setup instructions for SSO. You’re here for our enterprise SSO functionality. We hear you. Here’s a quick live setup to show you how easy it is.

    I’m assuming you have already created a self-serve deployment.

    • Start by opening the console.
    • Navigate to the Organizations tab.
    • Create a new organization. In this case, set a domain when creating an organization that corresponds to the email domains you want to match to their SSO provider.
    • Create a Portal Link by selecting it from the action menu of the organization. This is usually meant to be sent to the organization administrator, but in this case, we’ll open this link in an incognito window and configure the identity provider ourselves.

    We’ll use the identity provider wizard to setup a SSO connection to Azure AD. I’ve sped this up a bit, but you’ll get an idea of what is happening. Depending on your setup and target identity provider, this will be different.

    Now let’s secure an application and use our new SSO connection to log in. For this purpose, we’ve got a debugging application on Github you can use to quickly see how a front end application is secured and what data is shared between Phase Two and the application.

    • Clone our debug-app from github, and open up the frontend folder in your favorite editor or IDE.
    • Go back to the admin console and navigate to the Clients tab. Create a new client. Let’s call it frontend. This will be a public OIDC client, with localhost:3001 as the root and redirect uri.
    • Get the keycloak.json from the configuration and copy it. Paste it into keycloak.json to configure your debug-app.

    Before we continue, we need to configure an authentication flow that does our SSO redirect.

    • Navigate to the Authentication tab, and duplicate the Browser flow.
    • Add the Home IdP Discovery authenticator and move it into the position before the user forms. Configure it to not require a verified domain nor email.
    • Finally, using the action menu, bind it to the browser flow.

    Go back to the debug-app, and let’s try a login using an email domain that matches the one we configured.

    • First, run npm i and npm start to start the debug app, and navigate to http://localhost:3001 in your browser. See that it redirects to the default login.
    • Enter the email address in the new email only form.
    • We are redirected to the Azure identity provider we set up.
    • Log in to Azure, and then we are redirected to back to debug-app.
    • Let’s take a look at the token and see the data that came over from Azure.

    As a bonus, let’s map some information about the user’s organization memberships into the token in case we need to do something with that information in our application.

    • Go back to the Admin UI and navigate to the Client we created.
    • Select the frontend-dedicated client scope.
    • Add a mapper by configuration.
    • Select the Organization Role mapper and configure it as shown.
    • Save the configuration.

    Now let’s go back to the debug-app and reload.

    • Take a look at the token. It now contains information about the organization we created.
    • The user was automatically created and added as a member to the organization when we logged in through the Azure identity provider.

    You now have a fully working authentication and enterprise SSO setup for your application. It took about 5 minutes!

    · One min read

    Working with one of our customers, we discovered that even the most technically literate developer or ops professional could look at the configuration for an SSO connection like it was a foreign language. While our configuration interface attempts to cover all possible options, and document clearly what each option means, it can still be entirely unclear what is required during a setup.

    Furthermore, the identity provider that is being integrated can present a similarly extensive interface that may not use the same terms and language. However, after investigation into the most common identity providers, we found that most of the configuration options can simply be set by convention if the vendor is known.

    Based on that observation, we've built what we call a "wizard" UI on top of our identity provider configuration to make it easy to integration the top commercial identity provider vendors. Take a look at a quick video of a setup using our most recent prototype.

    If you're interested in early access to our "wizards", please contact us today.

    - + \ No newline at end of file diff --git a/blog/tags/themes/index.html b/blog/tags/themes/index.html index 10a334702..947ac0771 100644 --- a/blog/tags/themes/index.html +++ b/blog/tags/themes/index.html @@ -12,8 +12,8 @@ - - + + @@ -24,7 +24,7 @@

    Images and other assets

    You'll notice that images, icons and fonts not found in the standard Keycloak themes are used. These are referenced by URL in the CSS. For example, if you are used to loading a custom font in the <head> of your HTML, it is possible to do it in CSS using @import:

    @import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Lexend+Deca:wght@300;400;500;600;700&family=Lexend:wght@300;400;500;600;700&family=Work+Sans:wght@300;400;500;600;700&display=swap");

    And background and other images can be referenced by their full URL using url(...):

    .login-pf body {
    background-image: url("https://raw.githubusercontent.com/p2-inc/keycloak-themes/main/examples/saas/assets/SaaS%20BG.webp");
    }

    Success!

    As always, our success is based on the success of our customers. We hope this extension and guide has helped you update the default Keycloak login branding to match that of your needs. If you have suggestions for further improvement of this feature, please reach out on GitHub!

    - + \ No newline at end of file diff --git a/blog/tags/tutorial/index.html b/blog/tags/tutorial/index.html index 7a7fa34cf..43ca56158 100644 --- a/blog/tags/tutorial/index.html +++ b/blog/tags/tutorial/index.html @@ -12,8 +12,8 @@ - - + + @@ -26,7 +26,7 @@

    Go back to the admin console in the other browser window, and navigate to the Users section. You will be able to find the user that was just created.

    Magic links are a great way to streamline your user onboarding and experience to help you easily drive engagement across your application. Phase Two makes it quick and easy to integrate magic links (and social login, and enterprise SSO, and much more). Stay tuned for more guides that will help you build the authentication experience that is right for your app.

    · 3 min read

    One of the first things you will need to do when getting a Keycloak Realm ready for use is to set up your email server configuration. There are many system emails that are sent to users in the course of verifying and updating user accounts: Email address verification, magic links, password reset, account update, login failure notifications, identity provider linking, etc.

    In order to provide your users with a positive experience, these messages need a way to get to them. Keycloak supports any internet reachable SMTP server. If you are currently testing, and don't have an email server or service that you currently use, SendGrid provides free accounts that allow you to send up to 100 emails per day forever. For debugging, you can also use a service like MailTrap to give you a catch-all for emails coming from Keycloak.

    If you are using a Phase Two Deployment, log in to the self-service dashboard, and click on the Open Console link for the Deployment you wish to use. Once in the Keycloak admin console, click Realm settings in the left menu, and then click the Email tab.

    In the first section, labeled Template, you will set options that will be used in the templates for the emails that are sent to your users. The only required field is the From field, which must contain the email address the user will see the email originating from. This should be an email address that your email server is expecting, and it will not block for authorization reasons.

    The other fields in the Template section are not required, but will enhance how your emails look:

    • From address used to send emails (required)
    • From display name a user-friendly name displayed along From
    • Reply to an email address that will be used by email clients when your user replies to an email
    • Reply to display name a user-friendly name displayed along Reply to
    • Envelope from Bounce Address used for the mails that are rejected

    In the Connection & Authentication section, you will provide details of your SMTP server:

    • Host indicates the SMTP server hostname used for sending emails
    • Port indicates the SMTP server port (usually 25, 465, 587, or 2525)
    • Encryption support encryption for communication with your SMTP server
    • Authentication if your SMTP server requires authentication, and supply the Username and Password

    Finally, before you click Save, click the Test connection button to send a test email to the email address of the currently logged in user. If you don't have that set, you might have click Save and edit your user before you come back. You'll receive a success message, or information that will help you resolve problems.

    Once you do that, you'll have accomplished a significant task which enables lots of other functionality!

    Also, stay tuned for another post on how to customize your email templates to match your branding and messaging.

    - + \ No newline at end of file diff --git a/blog/tags/vue/index.html b/blog/tags/vue/index.html index 2e8e33f42..1a729e1be 100644 --- a/blog/tags/vue/index.html +++ b/blog/tags/vue/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    2 posts tagged with "vue"

    View All Tags

    · One min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Vue example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Vue.js Project

    info

    We will use the Phase Two Vue example code here, but the logic could easily be applied to any existing application.

    This example uses Vue.js. We're going to leverage oidc-client-ts to integrate OIDC authentication with the Vue app. The oidc-client-ts package is a well-maintained and used library. It provides a lot of utilities for building out a fully production app.

    1. Clone the Phase Two example repo.

    2. Open the Vue folder within /frameworks/vue and open the /nuxt/oidc-client-ts folder.

    3. Run npm install and then npm run dev.

    4. We'll review where we configure out Keycloak instance. First open /auth.ts. In this file you will want to update it with the values for the Keycloak instance we set-up earlier in the tutorial. Update the clientSecret with the value. Use and environment variable here if you wish.

      export const keycloakConfig = {
      authorityUrl: "https://euc1.auth.ac",
      applicationUrl: "http://localhost:3000",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      clientSecret: "CLIENT_SECRET",
      };

      After the config, you can see how the OIDC instance is started.

      const settings = {
      authority: `${keycloakConfig.authorityUrl}/auth/realms/${keycloakConfig.realm}`,
      client_id: keycloakConfig.clientId,
      client_secret: keycloakConfig.clientSecret,
      redirect_uri: `${window.location.origin}/auth`,
      silent_redirect_uri: `${window.location.origin}/silent-refresh`,
      post_logout_redirect_uri: `${window.location.origin}`,
      response_type: "code",
      userStore: new WebStorageStateStore(),
      loadUserInfo: true,
      };
      this.userManager = new UserManager(settings);
    5. With the Keycloak instance defined, we attach this to the app instance for Vue. Switch to /main.ts

      import Auth from "@/auth";
      // ...
      app.config.globalProperties.$auth = Auth;

      We pull in the Auth instance then expose it through the $auth variable.

    6. There are a few main pages in play here that we define to create paths the library can leverage. The /view/auth and /view/silent-refresh create paths at the same name. These are used to do the redirection during authentication. From within these we use the Auth instance to direct the user around within the app. For instance in /views/AuthView:

      export default {
      name: "AuthAuthenticated",
      async mounted() {
      try {
      await this.$auth.signinCallback();
      this.$router.push("/");
      } catch (e) {
      console.error(e);
      }
      },
      };

      The router.push naively sends someone to the home page. This could be updated to go to any number of places, including the page one started the login flow from if you were to store that information to be retrieved.

    7. Now that we have all the things setup, we can define the user component /components/User to easily pull information about the user's state and display the appropriate UI.

      export default {
      name: "UserComponent",
      data() {
      return {
      user: null,
      signIn: () => this.$auth.signinRedirect(),
      logout: () => this.$auth.signoutRedirect(),
      };
      },
      async created() {
      const user = await this.$auth.getUser();
      if (user) {
      this.user = user;
      }
      },
      };

      With this, the user object is now easily available. A simple v-if="user" allows the app to determine what UI to show.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    · 7 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Nuxt example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Nuxt Project

    info

    We will use the Phase Two Nuxt example code here, but the logic could easily be applied to any existing application.

    This example uses Nuxt3. There are a couple methods by which you can integrate Keycloak to your Nuxt application. We're going to explore two methods here, one uses keycloak-js and the other leverages oidc-client-ts. The keycloak-js library provides a simple, client-only method, but lacks some of the sophistication provided by the oidc-client library that is heavily supported and more widely used.

    Using keycloak-js

    info

    For this example, we need to disable "Client Authentication" in the OIDC client that was setup earlier. This is available under Client > Settings > Capability config > Client authentication to OFF.

    1. Clone the Phase Two example repo.

    2. Open the Nuxt folder within /frameworks/nuxt and open the keycloak-js folder within /frameworks/nuxt/keycloak-js.

    3. Run npm install and then npm run dev. keycloak-js is a Javascript library that provides a fast way to secure an application.

    4. The project makes use of the following Nuxt items: components, composables, layouts, and plugins. We'll review each in kind.

    5. The main component that shows the User's authenticated state is in /components/User. In this component we call the useKeycloak composable, which let's us key into the keycloak-js functions that we've wrapped to make easily availble.

      const { keycloak, authState } = useKeycloak();

      function login() {
      keycloak.login();
      }

      function logout() {
      keycloak.logout();
      }

      Lower in the file the component leverages v-if checks to determine if the authState is authenticated or not. Depending on the state, a Log in or Log out button is available.

    6. Let's take a look at the setup for the composable next. Our composable is in /composables/keycloak-c. A composable is a function defined that can be called anywhere in the Nuxt application. It's a good way to abstract logic to be reused. In our case we use it to wrap a keycloak-js plugin (more on that in the next step) and help provided a state value for the authenticated state.

      export const useKeycloak = () => {
      const nuxtApp = useNuxtApp();
      const keycloak = nuxtApp.$keycloak as Keycloak;
      const authState = useState("authState", () => "unAuthenticated");

      keycloak.onAuthSuccess = () => (authState.value = "authenticated");
      keycloak.onAuthError = () => (authState.value = "error");

      return {
      keycloak,
      authState,
      };
      };
    7. In the plugin, /plugins/keycloak.client.ts we instantiate the keycloak-js library. We can then attach that instance to the NuxtApp instance. Substitute the correct values for your Keycloak instance that we created earlier in the tutorial.

      export default defineNuxtPlugin((nuxtApp) => {
      const initOptions: KeycloakConfig = {
      url: "https://euc1.auth.ac/auth/",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      };

      const keycloak = new Keycloak(initOptions);

      nuxtApp.$keycloak = keycloak;

      keycloak.init({
      onLoad: "check-sso",
      });
      });
    8. The logic for checking the authenticated state can be used to expand in ways to secure your site in a number of ways.

    Using oidc-client

    The oidc-client-ts package is a well-maintained and used library. It provides a lot of utilities for building out a fully production app.

    1. Clone the Phase Two example repo.

    2. Open the Nuxt folder within /frameworks/nuxt and open the /nuxt/oidc-client-ts folder.

    3. Run npm install and then npm run dev.

    4. The structure of the project is similar to the keycloak-js version but with a the use of services, stores, and middleware.

    5. We'll review where we configure out Keycloak instance. First open /services/keycloak-config.ts. In this file you will want to update it with the values for the Keycloak instance we set-up earlier in the tutorial. Make sure you are using the one with Client Authentication enabled. Update the clientSecret with the value. Use and environment variable here if you wish.

      export const keycloakConfig = {
      authorityUrl: "https://euc1.auth.ac",
      applicationUrl: "http://localhost:3000",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      clientSecret: "CLIENT_SECRET",
      };
    6. Switch over to the /services/auth-service now to see how the Oidc instance is started. The class pulls in values from the keycloakConfig to use in the constructor. The other functions are wrappers around methods provided by the oidc-client library. This allows us to key into things like signInRedirect and signoutRedirect.

      How the settings are integrated:

      const settings = {
      authority: `${keycloakConfig.authorityUrl}/auth/realms/${keycloakConfig.realm}`,
      client_id: keycloakConfig.clientId,
      client_secret: keycloakConfig.clientSecret,
      redirect_uri: `${window.location.origin}/auth`,
      silent_redirect_uri: `${window.location.origin}/silent-refresh`,
      post_logout_redirect_uri: `${window.location.origin}`,
      response_type: "code",
      userStore: new WebStorageStateStore(),
      loadUserInfo: true,
      };
      this.userManager = new UserManager(settings);

      Example function wrapper:

      public signInRedirect() {
      return this.userManager.signinRedirect();
      }
    7. With the AuthService defined, we can now expose that through a composable. Switch to the /composables/useServices file. The file is simple but provides a way for any component to hook into the service instance.

      import AuthService from "@/services/auth-service";
      import ApplicationService from "@/services/application-service";
      import { useAuth } from "@/stores/auth";

      export const useServices = () => {
      const authStore = useAuth();

      return {
      $auth: new AuthService(),
      $application: new ApplicationService(authStore.access_token),
      };
      };

      We pull in the AuthService then expose it through the $auth variable. The $application variable exposes the ApplicationService which is provided as an example of how you could secure API calls.

    8. We leverage the pinia library to make store User information to make it easily accessible. Open /stores/auth/index. From within this file, we can wrap the User object exposed by the oidc-client package. This can then be leveraged in the middleware function we want to define or to pull information quickly about the user.

    9. There are a few main pages in play here that we define to create paths the library can leverage. The /pages/auth, /pages/logout, /pages/silent-refresh create paths at the same name. These are used to do the redirection during authentication or log out. From within these we use the AuthService to direct the user around within the app. For instance in /auth:

      const authenticateOidc = async () => {
      try {
      await services.$auth.signInCallback();
      router.push("/");
      } catch (error) {
      console.error(error);
      }
      };

      await authenticateOidc();

      The router.push naively sends someone to the home page. This could be updated to go to any number of places, including the page one started the login flow from if you were to store that information to be retrieved.

    10. We have also created a middleware file in /middleware/auth.global to be used in a couple of ways. It checks if the user is authenticated and based on that knowledge, stores the user information in the store (if not there) or could be used to send someone to login. For our example, we created buttons to initiate that but there is a comment which shows how you could force a set of paths to require login.

      const authFlowRoutes = ["/auth", "/silent-refresh", "/logout"];

      export default defineNuxtRouteMiddleware(async (to, from) => {
      const authStore = useAuth();
      const services = useServices();
      const user = (await services.$auth.getUser()) as User;

      if (!user && !authFlowRoutes.includes(to.path)) {
      // use this to automatically force a sign in and redirect
      // services.$auth.signInRedirect();
      } else {
      authStore.setUpUserCredentials(user);
      }
      });
    11. Now that we have all the things setup, we can define the user component /components/User to easily pull information about the user's state and display the appropriate UI.

      const authStore = useAuth();
      const user = authStore.user;

      const signIn = () => services.$auth.signInRedirect();
      const signOut = () => services.$auth.logout();

      With this, the user object is now easily available. A simple v-if="user" allows the app to determine what UI to show.

    12. A bit more complicated of a setup, but more elegant in the handling of the logged in flow. The oidc-client allows for much better fine-tuning of the experience.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/tags/vuejs/index.html b/blog/tags/vuejs/index.html index c272f2b77..e5da4a8d3 100644 --- a/blog/tags/vuejs/index.html +++ b/blog/tags/vuejs/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    2 posts tagged with "vuejs"

    View All Tags

    · One min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Vue example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Vue.js Project

    info

    We will use the Phase Two Vue example code here, but the logic could easily be applied to any existing application.

    This example uses Vue.js. We're going to leverage oidc-client-ts to integrate OIDC authentication with the Vue app. The oidc-client-ts package is a well-maintained and used library. It provides a lot of utilities for building out a fully production app.

    1. Clone the Phase Two example repo.

    2. Open the Vue folder within /frameworks/vue and open the /nuxt/oidc-client-ts folder.

    3. Run npm install and then npm run dev.

    4. We'll review where we configure out Keycloak instance. First open /auth.ts. In this file you will want to update it with the values for the Keycloak instance we set-up earlier in the tutorial. Update the clientSecret with the value. Use and environment variable here if you wish.

      export const keycloakConfig = {
      authorityUrl: "https://euc1.auth.ac",
      applicationUrl: "http://localhost:3000",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      clientSecret: "CLIENT_SECRET",
      };

      After the config, you can see how the OIDC instance is started.

      const settings = {
      authority: `${keycloakConfig.authorityUrl}/auth/realms/${keycloakConfig.realm}`,
      client_id: keycloakConfig.clientId,
      client_secret: keycloakConfig.clientSecret,
      redirect_uri: `${window.location.origin}/auth`,
      silent_redirect_uri: `${window.location.origin}/silent-refresh`,
      post_logout_redirect_uri: `${window.location.origin}`,
      response_type: "code",
      userStore: new WebStorageStateStore(),
      loadUserInfo: true,
      };
      this.userManager = new UserManager(settings);
    5. With the Keycloak instance defined, we attach this to the app instance for Vue. Switch to /main.ts

      import Auth from "@/auth";
      // ...
      app.config.globalProperties.$auth = Auth;

      We pull in the Auth instance then expose it through the $auth variable.

    6. There are a few main pages in play here that we define to create paths the library can leverage. The /view/auth and /view/silent-refresh create paths at the same name. These are used to do the redirection during authentication. From within these we use the Auth instance to direct the user around within the app. For instance in /views/AuthView:

      export default {
      name: "AuthAuthenticated",
      async mounted() {
      try {
      await this.$auth.signinCallback();
      this.$router.push("/");
      } catch (e) {
      console.error(e);
      }
      },
      };

      The router.push naively sends someone to the home page. This could be updated to go to any number of places, including the page one started the login flow from if you were to store that information to be retrieved.

    7. Now that we have all the things setup, we can define the user component /components/User to easily pull information about the user's state and display the appropriate UI.

      export default {
      name: "UserComponent",
      data() {
      return {
      user: null,
      signIn: () => this.$auth.signinRedirect(),
      logout: () => this.$auth.signoutRedirect(),
      };
      },
      async created() {
      const user = await this.$auth.getUser();
      if (user) {
      this.user = user;
      }
      },
      };

      With this, the user object is now easily available. A simple v-if="user" allows the app to determine what UI to show.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    · 7 min read

    In this article we'll be using Keycloak to quickly augment an application with user management and SSO. We will demonstrate the integration by securing a page for logged-in users. This quickly provides a jump-off point to more complex integrations.

    info

    If you just want to skip to the code, visit the Phase Two Nuxt example.

    Setting up a Keycloak Instance

    Instructions
    tip

    If you already have a functioning Keycloak instance, you can skip to the next section.

    Rather than trying to set up a "from scratch" instance of Keycloak, we're going to short-circuit that process by leveraging a free Phase Two Starter instance. The Starter provides a free hosted instance of Phase Two's enhanced Keycloak ready for light production use cases.

    • Visit the sign-up page.

    • Enter an email, use a Github account, or use an existing Google account to register.

    • Follow the register steps. This will include a sign-in link being sent to your email. Use that for password-less login.

    • After creating an account, a realm is automatically created for you with all of the Phase Two enhancements. You need to create a Deployment in the Shared Phase Two infrastructure in order to gain access to the realm. Without a deployment created, the Create Shared Deployment modal will automatically pop up.

    • Create a Shared Deployment by providing a region (pick something close to your existing infrastructure), a name for the deployment, and selecting the default organization that was created for you upon account creation. Hit "Confirm" when ready. Standby while our robots get to work generating your deployment. This can take a few seconds.

    • After the deployment is created and active, you can access the Keycloak Admin console by clicking "Open Console" for that deployment. Open it now to see the console.

    At this point, move on to the next step in the tutorial. We'll be coming back to the Admin Console when its time to start connecting our App to the Keycloak instance.

    Setting up an OIDC Client

    Instructions

    We need to create a OpenID Connect Client in Keycloak for the app to communicate with. Keycloak's docs provide steps for how to create an OIDC client and all the various configurations that can be introduced. Follow the steps below to create a client and get the right information necessary for app configuration.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.

    2. Click Clients in the menu.

    3. Click Create client.

    4. Leave Client type set to OpenID Connect.

    5. Enter a Client ID. This ID is an alphanumeric string that is used in OIDC requests and in the Keycloak database to identify the client.

    6. Supply a Name for the client.

    7. Click Next.

    8. Under the Capability Config section, leave the defaults as selected. This can be configured further later.

      • Client authentication to On.
      • Authorization to Off.
      • Standard flow checked. Direct access grants checked. All other items unchecked.
    9. Click Next.

    10. Under Login settings we need to add a redirect URI and Web origin in order. Assuming you are using the example applicaiton:

      Valid redirect URI (allows redirect back to application)

      http://localhost:3000/*

      Web origins (allows for Token auth call)

      http://localhost:3000
      URI and Origin Details

      The choice of localhost is arbitrary. If you are using an example application running locally, this will apply. If you are using an app that you actually have deployed somewhere, then you will need to substitute the appropriate URI for that.

    11. Click Save

    OIDC Config

    We will need values to configure our application. To get these values follow the instructions below.

    1. Click Clients in the menu.

    2. Find the Client you just created and click on it. In the top right click the Action dropdown and select Download adapter config.

    3. Select Keycloak OIDC JSON in the format option. The details section will populate with the details we will need.

      • Note the realm, auth-server-url, and resource values.

    4. You also need to copy the Client secret in the Credential tab for the client to use. Once on the Credential tab, click the copy button to copy the key to your clipboard. Save the key somewhere for use later in this tutorial

    Adding a Non-Admin User

    Instructions
    info

    It is bad practice to use your Admin user to sign in to an Application.

    Since we do not want to use our Admin user for signing into the app we will build, we need to add a another non-admin user.

    1. Open the Admin UI by clicking Open Console in the Phase Two Dashboard.
    2. Click Users in the menu.
    3. Click Add user.
    4. Fill out the information for Email, First name, and Last name. Click Create.
    5. We will now set the password for this user manually. Click Credentials (tab) and click Set Password. Provide a password for this user. For our use case, as a tutorial, you can leave "Temporary" set to "Off".
    6. Click Save and confirm the password by clicking Save password

    Setting up a Nuxt Project

    info

    We will use the Phase Two Nuxt example code here, but the logic could easily be applied to any existing application.

    This example uses Nuxt3. There are a couple methods by which you can integrate Keycloak to your Nuxt application. We're going to explore two methods here, one uses keycloak-js and the other leverages oidc-client-ts. The keycloak-js library provides a simple, client-only method, but lacks some of the sophistication provided by the oidc-client library that is heavily supported and more widely used.

    Using keycloak-js

    info

    For this example, we need to disable "Client Authentication" in the OIDC client that was setup earlier. This is available under Client > Settings > Capability config > Client authentication to OFF.

    1. Clone the Phase Two example repo.

    2. Open the Nuxt folder within /frameworks/nuxt and open the keycloak-js folder within /frameworks/nuxt/keycloak-js.

    3. Run npm install and then npm run dev. keycloak-js is a Javascript library that provides a fast way to secure an application.

    4. The project makes use of the following Nuxt items: components, composables, layouts, and plugins. We'll review each in kind.

    5. The main component that shows the User's authenticated state is in /components/User. In this component we call the useKeycloak composable, which let's us key into the keycloak-js functions that we've wrapped to make easily availble.

      const { keycloak, authState } = useKeycloak();

      function login() {
      keycloak.login();
      }

      function logout() {
      keycloak.logout();
      }

      Lower in the file the component leverages v-if checks to determine if the authState is authenticated or not. Depending on the state, a Log in or Log out button is available.

    6. Let's take a look at the setup for the composable next. Our composable is in /composables/keycloak-c. A composable is a function defined that can be called anywhere in the Nuxt application. It's a good way to abstract logic to be reused. In our case we use it to wrap a keycloak-js plugin (more on that in the next step) and help provided a state value for the authenticated state.

      export const useKeycloak = () => {
      const nuxtApp = useNuxtApp();
      const keycloak = nuxtApp.$keycloak as Keycloak;
      const authState = useState("authState", () => "unAuthenticated");

      keycloak.onAuthSuccess = () => (authState.value = "authenticated");
      keycloak.onAuthError = () => (authState.value = "error");

      return {
      keycloak,
      authState,
      };
      };
    7. In the plugin, /plugins/keycloak.client.ts we instantiate the keycloak-js library. We can then attach that instance to the NuxtApp instance. Substitute the correct values for your Keycloak instance that we created earlier in the tutorial.

      export default defineNuxtPlugin((nuxtApp) => {
      const initOptions: KeycloakConfig = {
      url: "https://euc1.auth.ac/auth/",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      };

      const keycloak = new Keycloak(initOptions);

      nuxtApp.$keycloak = keycloak;

      keycloak.init({
      onLoad: "check-sso",
      });
      });
    8. The logic for checking the authenticated state can be used to expand in ways to secure your site in a number of ways.

    Using oidc-client

    The oidc-client-ts package is a well-maintained and used library. It provides a lot of utilities for building out a fully production app.

    1. Clone the Phase Two example repo.

    2. Open the Nuxt folder within /frameworks/nuxt and open the /nuxt/oidc-client-ts folder.

    3. Run npm install and then npm run dev.

    4. The structure of the project is similar to the keycloak-js version but with a the use of services, stores, and middleware.

    5. We'll review where we configure out Keycloak instance. First open /services/keycloak-config.ts. In this file you will want to update it with the values for the Keycloak instance we set-up earlier in the tutorial. Make sure you are using the one with Client Authentication enabled. Update the clientSecret with the value. Use and environment variable here if you wish.

      export const keycloakConfig = {
      authorityUrl: "https://euc1.auth.ac",
      applicationUrl: "http://localhost:3000",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      clientSecret: "CLIENT_SECRET",
      };
    6. Switch over to the /services/auth-service now to see how the Oidc instance is started. The class pulls in values from the keycloakConfig to use in the constructor. The other functions are wrappers around methods provided by the oidc-client library. This allows us to key into things like signInRedirect and signoutRedirect.

      How the settings are integrated:

      const settings = {
      authority: `${keycloakConfig.authorityUrl}/auth/realms/${keycloakConfig.realm}`,
      client_id: keycloakConfig.clientId,
      client_secret: keycloakConfig.clientSecret,
      redirect_uri: `${window.location.origin}/auth`,
      silent_redirect_uri: `${window.location.origin}/silent-refresh`,
      post_logout_redirect_uri: `${window.location.origin}`,
      response_type: "code",
      userStore: new WebStorageStateStore(),
      loadUserInfo: true,
      };
      this.userManager = new UserManager(settings);

      Example function wrapper:

      public signInRedirect() {
      return this.userManager.signinRedirect();
      }
    7. With the AuthService defined, we can now expose that through a composable. Switch to the /composables/useServices file. The file is simple but provides a way for any component to hook into the service instance.

      import AuthService from "@/services/auth-service";
      import ApplicationService from "@/services/application-service";
      import { useAuth } from "@/stores/auth";

      export const useServices = () => {
      const authStore = useAuth();

      return {
      $auth: new AuthService(),
      $application: new ApplicationService(authStore.access_token),
      };
      };

      We pull in the AuthService then expose it through the $auth variable. The $application variable exposes the ApplicationService which is provided as an example of how you could secure API calls.

    8. We leverage the pinia library to make store User information to make it easily accessible. Open /stores/auth/index. From within this file, we can wrap the User object exposed by the oidc-client package. This can then be leveraged in the middleware function we want to define or to pull information quickly about the user.

    9. There are a few main pages in play here that we define to create paths the library can leverage. The /pages/auth, /pages/logout, /pages/silent-refresh create paths at the same name. These are used to do the redirection during authentication or log out. From within these we use the AuthService to direct the user around within the app. For instance in /auth:

      const authenticateOidc = async () => {
      try {
      await services.$auth.signInCallback();
      router.push("/");
      } catch (error) {
      console.error(error);
      }
      };

      await authenticateOidc();

      The router.push naively sends someone to the home page. This could be updated to go to any number of places, including the page one started the login flow from if you were to store that information to be retrieved.

    10. We have also created a middleware file in /middleware/auth.global to be used in a couple of ways. It checks if the user is authenticated and based on that knowledge, stores the user information in the store (if not there) or could be used to send someone to login. For our example, we created buttons to initiate that but there is a comment which shows how you could force a set of paths to require login.

      const authFlowRoutes = ["/auth", "/silent-refresh", "/logout"];

      export default defineNuxtRouteMiddleware(async (to, from) => {
      const authStore = useAuth();
      const services = useServices();
      const user = (await services.$auth.getUser()) as User;

      if (!user && !authFlowRoutes.includes(to.path)) {
      // use this to automatically force a sign in and redirect
      // services.$auth.signInRedirect();
      } else {
      authStore.setUpUserCredentials(user);
      }
      });
    11. Now that we have all the things setup, we can define the user component /components/User to easily pull information about the user's state and display the appropriate UI.

      const authStore = useAuth();
      const user = authStore.user;

      const signIn = () => services.$auth.signInRedirect();
      const signOut = () => services.$auth.logout();

      With this, the user object is now easily available. A simple v-if="user" allows the app to determine what UI to show.

    12. A bit more complicated of a setup, but more elegant in the handling of the logged in flow. The oidc-client allows for much better fine-tuning of the experience.

    Learning more

    Phase Two's enhanced Keycloak provides many ways to quickly control and tweak the log in and user management experience. Our blog has many use cases from customizing login pages, setting up magic links (password-less sign in), and Organization workflows.

    - + \ No newline at end of file diff --git a/blog/tags/wizard/index.html b/blog/tags/wizard/index.html index 32696ecc7..8d12d37f6 100644 --- a/blog/tags/wizard/index.html +++ b/blog/tags/wizard/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    One post tagged with "wizard"

    View All Tags

    · One min read

    Working with one of our customers, we discovered that even the most technically literate developer or ops professional could look at the configuration for an SSO connection like it was a foreign language. While our configuration interface attempts to cover all possible options, and document clearly what each option means, it can still be entirely unclear what is required during a setup.

    Furthermore, the identity provider that is being integrated can present a similarly extensive interface that may not use the same terms and language. However, after investigation into the most common identity providers, we found that most of the configuration options can simply be set by convention if the vendor is known.

    Based on that observation, we've built what we call a "wizard" UI on top of our identity provider configuration to make it easy to integration the top commercial identity provider vendors. Take a look at a quick video of a setup using our most recent prototype.

    If you're interested in early access to our "wizards", please contact us today.

    - + \ No newline at end of file diff --git a/blog/welcome/index.html b/blog/welcome/index.html index dca65d1ba..3d6ad9ba8 100644 --- a/blog/welcome/index.html +++ b/blog/welcome/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Welcome to Phase Two

    · 3 min read

    After building and working for startups and technology companies for almost 25 years, I found myself having a sense of déjà-vu.

    Had I really built the same features and functionality over and over?

    Everyone of us who has been in the industry for any length of time probably has the same feeling. Whether or not we are fully conscious of it, we probably built a (bad) version of login, registration, user management, authorization, organizations and invitations, audit logging, etc. at least one time for every company we've worked for.

    In early 2018, I joined an enterprise SaaS startup, where I built the initial team and product over 18 months. Analyzing tickets and epics in the project tracking system we used, I found that over 60% of our first 18 months was spent building features and functionality like this -- essential building blocks, but not the core competency we sought to test in the marketplace. And the result of that effort was only adequate versions of those common features, which resulted in less time spent on what we were trying to prove. I began to refer to this heavy tax a "SaaS CRUD".

    Was this really what everyone else was doing? I was lucky to have a large network of engineering leaders at companies that ranged from the earliest stages to the largest public companies, so I asked them. The responses were remarkably consisitent. Early stage companies wished there was something comprehensive they could adopt, and later stage and large companies lamented not adopting external tools earlier that gave them guarantees around uptime and compliance. All were aware of or had tried to knit together a mish-mash of "feature company" products, and all expressed dissatisfaction with "model mismatch" of most of the tools in the marketplace, which demanded more integration overhead than the perceived benefit allowed.

    I was lucky to find others that had observed the same thing. We joined forces and spent the next 6 months interviewing companies of a range of sizes, developing a playbook for companies building new products. Based on that playbook, today we are releasing our first version of tooling designed to help application developers avoid rebuilding "SaaS CRUD".

    Phase Two is designed to help SaaS companies accelerate time-to-market. Authentication and SSO are our first targets, but we plan to expand to many other areas of pain for the growing company seeking enterprise adoption. We'd love for you to join us on this journey. For product demos and to become a beta customer, please contact us!

    - + \ No newline at end of file diff --git a/blog/wizard/index.html b/blog/wizard/index.html index 677b9749f..1ee52c65e 100644 --- a/blog/wizard/index.html +++ b/blog/wizard/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    SSO? Wave Your Magic Wand

    · One min read

    Working with one of our customers, we discovered that even the most technically literate developer or ops professional could look at the configuration for an SSO connection like it was a foreign language. While our configuration interface attempts to cover all possible options, and document clearly what each option means, it can still be entirely unclear what is required during a setup.

    Furthermore, the identity provider that is being integrated can present a similarly extensive interface that may not use the same terms and language. However, after investigation into the most common identity providers, we found that most of the configuration options can simply be set by convention if the vendor is known.

    Based on that observation, we've built what we call a "wizard" UI on top of our identity provider configuration to make it easy to integration the top commercial identity provider vendors. Take a look at a quick video of a setup using our most recent prototype.

    If you're interested in early access to our "wizards", please contact us today.

    - + \ No newline at end of file diff --git a/carousel/index.html b/carousel/index.html index 4a5de82ed..ebba575d6 100644 --- a/carousel/index.html +++ b/carousel/index.html @@ -12,14 +12,14 @@ - - + +
    - + \ No newline at end of file diff --git a/docs/about/index.html b/docs/about/index.html index dad7edce8..2a8c25f02 100644 --- a/docs/about/index.html +++ b/docs/about/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    About Us

    Mission

    Phase Two is dedicated to help SaaS companies accelerate time-to-market and enterprise adoption. We build products that allow you to rapidly integrating the features you need to access new customers and new markets.

    Story

    Phase Two was founded in 2019 by software veterans with over 60 combined years of experience building consumer and enterprise software products. The founders had just completed building the first version of a product for an early-stage SaaS startup. We realized that over 60% of the first 18 months was spent building features and functionality that were not the core competency the company sought to test in the marketplace.

    Following several months of market testing and investigation, we released a prototype based on the Keycloak open source identity and access management tool, and Phase Two was born. Since then, we've expanded our toolchain to serve software engineers at companies building products for both cloud and on-prem distribution.

    Team

    While we were founded in Seattle, Washington, the Phase Two team is now a remote, globally distributed group of individuals that are passionate about helping software engineers succeed. We are a company built by engineers, for engineers, and love to apply engineering principles to everything we do.

    Contact

    Feel free to reach out with bugs and feature requests, or email us with questions and comments. Prefer snail mail? You can reach us at 140 Lakeside Ave, Suite A49, Seattle, WA 98122.

    - + \ No newline at end of file diff --git a/docs/admin-portal/foo/index.html b/docs/admin-portal/foo/index.html index 7f30c2560..d8ea84ed8 100644 --- a/docs/admin-portal/foo/index.html +++ b/docs/admin-portal/foo/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Foo

    caution

    This section is currently under construction. Check back soon for updates.

    - + \ No newline at end of file diff --git a/docs/admin-portal/index.html b/docs/admin-portal/index.html index 151b66ed6..23465addb 100644 --- a/docs/admin-portal/index.html +++ b/docs/admin-portal/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Admin Portal

    The Phase Two Admin Portal allows you to offer self-management of User Profile and Organization features to your customers from within your application with almost no code. A portal link can be generated in your application that will take a logged in user to the Admin Portal. The appropriate configuration to activate the Admin Portal are installed by default. You can customize style and visibilty of the portal using the information in the following sections

    Configuration

    In the Styles->Portal section of the admin UI, it is possible to configure user access to portions of the portal. This has the effect of limiting the self-management functionality that is available to your users. The sections that can be toggled are:

    • Profile View and edit profile information such as first name, last name and email. View and edit credentials, linked account, and manage authenticated sessions.
      • Password update Update password.
      • 2FA create/update Add and remove 2FA mechanisms like OTP and WebAuthn.
      • Device activity View and terminate active authentication sessions.
      • Linked accounts View, create and remove links with social and other identity providers.
    • Organizations View and (conditionally) edit details of organizations for which a user is a member.
      • Details View and edit organization profile information.
      • Members View and manage organization members and their roles. Invite new members.
      • Invitations Invite new members.
      • Domains Add and verify email domains for SSO login.
      • SSO Create and update SSO connections to organization identity provider.
      • Events View events related to organization member activity.

    Styles

    Currently, the logo and favicon set in the general styles section will be used when rendering the portal in order to preserve your branding.

    Additionally, you can override three colors used in the portal, and optionally override the entire CSS. See the Admin Portal source code for details for overriding the stylesheet.

    Access control

    Access to components in the admin portal is dictated by the User's roles, both globally and within their organization.

    Profile

    Profile access requires the User to have the following account Client roles. These are granted to all Users by default, so you don't need to change anything unless you wish to revoke these roles. These roles can be managed in the Users section of the Admin UI by selecting the User you wish to edit and navigating to their Role mapping tab.

    • Details - requires view-profile to view, and manage-account to change profile data
    • Security - requires view-profile to view, manage-account to change credentials, and manage-account-links to add or change any social or brokered logins

    Organization

    Access to each of the Organization components is controlled by the User's member roles within the organization. There are no organization default roles, so you must grant these to Users after they are created and added to the organization. Member roles can be managed in the Organizations section of the Admin UI by selecting the Organization you wish to manage, finding the User in the Members tab, and managing their roles using the context menu on the right.

    • Details: requires view-organization to view, and manage-organization to change
    • Members & Invitations: requires view-organization
      • requires view-members to see members, and manage-members to remove or edit them
      • requires view-roles to see member roles
      • requires view-invitations to see pending invitations, and manage-invitations and view-roles to invite new users
      • requires view-organization, manage-organization, view-identity-providers and manage-identity-providers to use the SSO setup wizards and view/remove Identity Providers

    API

    If you choose to build functionality like the Admin Portal into your application to create a more unified experience, or to build it into native or mobile applications, you can use the APIs for User and Organization management.

    Listening for changes

    Once the user has made changes to their details, they seamlessly return to your application. You can be informed of changes by using audit webhooks.

    Component guides

    - + \ No newline at end of file diff --git a/docs/admin-portal/portal-link/index.html b/docs/admin-portal/portal-link/index.html index eb15f8c9b..e32adcae6 100644 --- a/docs/admin-portal/portal-link/index.html +++ b/docs/admin-portal/portal-link/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Portal Link

    The hosted account management experience can be easily linked to from your application. Branding is automatic using the same variables for customizing the login UI.

    The link is a simple template, similar to the Keycloak Account Management Console.

      https://{host}/auth/realms/{realm}/portal/

    For example:

      https://usw2.auth.ac/auth/realms/foobar/portal/
    - + \ No newline at end of file diff --git a/docs/affiliate/index.html b/docs/affiliate/index.html index 0785df251..d80865294 100644 --- a/docs/affiliate/index.html +++ b/docs/affiliate/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    - + \ No newline at end of file diff --git a/docs/api-description/index.html b/docs/api-description/index.html index 1805280d9..f42614319 100644 --- a/docs/api-description/index.html +++ b/docs/api-description/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    api-description

    This is a REST API reference for the Phase Two Keycloak custom resources. These are extensions to the standard Keycloak Admin REST API.

    Base URI format

    Paths specified in the documentation are relative to the the base URI.

    • Format: https://<host>:<port>/auth/realms
    • Example: https://app.phasetwo.io/auth/realms

    Authentication

    Authentication is achieved by using the Authentication: Bearer <token> header in all requests. This is either the access token received from a normal authentication, or by a request directly to the OpenID Connect token endpoint.

    It is recommended that you use a Keycloak Admin Client, such as this one for Javascript, as they take care of authentication, getting an access token, and refreshing it when it expires.

    Client credentials grant example

    POST /auth/realms/test-realm/protocol/openid-connect/token
    Host: app.phasetwo.io
    Accept: application/json
    Content-type: application/x-www-form-urlencoded

    grant_type=client_credentials&client_id=admin-cli&client_secret=fd649804-3a74-4d69-acaa-8f065c6b7da1

    Password grant example

    POST /auth/realms/test-realm/protocol/openid-connect/token
    Host: app.phasetwo.io
    Accept: application/json
    Content-type: application/x-www-form-urlencoded

    grant_type=password&username=uname@foo.com&password=pwd123AZY&client_id=admin-cli
    - + \ No newline at end of file diff --git a/docs/api/authentication/index.html b/docs/api/authentication/index.html index 4e090ddab..052ab58e3 100644 --- a/docs/api/authentication/index.html +++ b/docs/api/authentication/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Authentication

    Authentication is achieved by using the Authentication: Bearer <token> header in all requests. This is either the access token received from a normal authentication, or by a request directly to the OpenID Connect token endpoint.

    It is recommended that you use a Keycloak Admin Client, such as this one for Javascript, as they take care of authentication, getting an access token, and refreshing it when it expires.

    Example requests

    Client credentials grant example

    The client_credentials grant type is used if you are following the recommended convention of making a service account to call the APIs. See the previous section on service accounts if you need to set one up.

    POST /auth/realms/test-realm/protocol/openid-connect/token
    Host: app.phasetwo.io
    Accept: application/json
    Content-type: application/x-www-form-urlencoded

    grant_type=client_credentials&client_id=admin-cli&client_secret=fd649804-3a74-4d69-acaa-8f065c6b7da1

    Password grant example

    The password grant type can be used if normal users are using their credentials to obtain a token manually for the purpose of calling the APIs.

    POST /auth/realms/test-realm/protocol/openid-connect/token
    Host: app.phasetwo.io
    Accept: application/json
    Content-type: application/x-www-form-urlencoded

    grant_type=password&username=uname@foo.com&password=pwd123AZY&client_id=admin-cli

    Example response

    The response to each type will be a JSON document containing the access token and some information about type and expiration.

    {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
    "token_type": "bearer",
    "expires_in": 60
    }

    The access_token value will be used in the Authorization: Bearer {access_token} header for all authenticated API requests.

    - + \ No newline at end of file diff --git a/docs/api/index.html b/docs/api/index.html index 7d288df80..154de16f5 100644 --- a/docs/api/index.html +++ b/docs/api/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@ - + \ No newline at end of file diff --git a/docs/api/sdks/index.html b/docs/api/sdks/index.html index 264fa87c8..7e51a0fa8 100644 --- a/docs/api/sdks/index.html +++ b/docs/api/sdks/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Software Development Kits

    Much of the functionality described here is documented in our API section. Our API is built as extensions to the standard Keycloak Admin REST API, and can be used in a similar way.

    Modern API libraries are available for several common languages. They complement some of the popular Keycloak Admin libraries. These are available as open source at the links below, or you can choose to generate your own using our OpenAPI spec file.

    SDK libraries

    LanguageLibrary
    Java (and other JVM languages)https://github.com/p2-inc/phasetwo-java
    JavaScript/TypeScripthttps://github.com/p2-inc/phasetwo-js
    Pythonhttps://github.com/p2-inc/phasetwo-python

    Notes

    If you are using a language or tech stack that is not covered by our API libraries, we want to hear from you! Please contact us to tell us about your needs.

    - + \ No newline at end of file diff --git a/docs/api/service-accounts/index.html b/docs/api/service-accounts/index.html index 09399394e..d458b0a7e 100644 --- a/docs/api/service-accounts/index.html +++ b/docs/api/service-accounts/index.html @@ -12,8 +12,8 @@ - - + + @@ -22,7 +22,7 @@

    Once you have set up the client, find your Client secret in the Credentials tab of the client you just created. Copy and save it for later use.

    Mapping roles

    There will be a Service accounts roles tab in the client. Select this in order to grant roles to the service account user. The Assign role button can be used to find roles to grant. The roles you select will likely be found by selecting Filter by clients and looking for roles in the realm-management client.

    The roles you grant to the service account user, will be dictated by how you intend to use the API. If you will just use it to look up users, you will only need to grant the view-users and query-users roles. If you are using it to manage organizations you will need to grant the view-organizations, manage-organizations, view-users and query-users roles. A full matrix of roles is beyond the scope of this document, but more information can be found in the Phase Two API and the Keycloak Admin REST API documentation.

    - + \ No newline at end of file diff --git a/docs/audit-logs/access/index.html b/docs/audit-logs/access/index.html index 99603f2d5..711d13d99 100644 --- a/docs/audit-logs/access/index.html +++ b/docs/audit-logs/access/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Access

    Phase Two records access event types that pertain to end users use of the Phase Two and customer systems. These are primarily registration, login and account management actions.

    Example

    The following is an example of the detail contained in an access.LOGIN event:

    {
    "uid": "8d76de08-1c12-4d9a-bb86-fd1c602b2486",
    "time": 1588363619462,
    "realmId": "customer.app",
    "type": "access.LOGIN",
    "authDetails": {
    "realmId": "customer.app",
    "clientId": "web-app",
    "userId": "f7cd3491-7c6c-480c-bc5c-b2b3b0fb048c",
    "ipAddress": "78.195.109.209",
    "username": "johndoe",
    "sessionId": "70c446c9-ed9a-4c9e-9a72-d26363bb4000"
    },
    "details": {
    "auth_method": "openid-connect",
    "redirect_uri": "https://customer.com/dashboard",
    "consent": "no_consent_required",
    "code_id": "70c446c9-ed9a-4c9e-9a72-d26363bb4000",
    "username": "johndoe"
    }
    }

    Field definitions can be viewed in the API documentation.

    Event types

    All access event types are indicated in the type field of the json event with the access. prefix.

    Login events

    • LOGIN - A user has logged in.
    • REGISTER - A user has registered.
    • LOGOUT - A user has logged out.
    • CODE_TO_TOKEN - An application/client has exchanged a code for a token.
    • REFRESH_TOKEN - An application/client has refreshed a token.

    Account events

    • SOCIAL_LINK - An account has been linked to a social provider.
    • REMOVE_SOCIAL_LINK - A social provider has been removed from an account.
    • UPDATE_EMAIL - The email address for an account has changed.
    • UPDATE_PROFILE - The profile for an account has changed.
    • SEND_RESET_PASSWORD - A password reset email has been sent.
    • UPDATE_PASSWORD - The password for an account has changed.
    • UPDATE_TOTP - The TOTP settings for an account have changed.
    • REMOVE_TOTP - TOTP has been removed from an account.
    • SEND_VERIFY_EMAIL - An email verification email has been sent.
    • VERIFY_EMAIL - The email address for an account has been verified.

    For all events, there is a corresponding error event, suffixed with _ERROR, which indicates a problem when performing the indicated action.

    - + \ No newline at end of file diff --git a/docs/audit-logs/admin/index.html b/docs/audit-logs/admin/index.html index 3dc55da17..3dea7e16d 100644 --- a/docs/audit-logs/admin/index.html +++ b/docs/audit-logs/admin/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Admin

    Actions performed from the Phase Two dashboard or using the administrative API are recorded as events.

    Example

    The following is an example of the detail contained in an admin event:

    {
    "uid":"31db295f-4bc7-4ba0-b689-65197efb5c6e",
    "time":1588696343501,
    "realmId":"master",
    "type":"admin.CLIENT-CREATE"
    "resourceType":"CLIENT",
    "operationType":"CREATE",
    "resourcePath":"clients/396664b6-40c5-4d45-bedf-2d8dd0f2af0e",
    "authDetails":{
    "realmId":"master",
    "clientId":"54aee1f9-2fe5-4049-99e8-3b72277a8e43",
    "userId":"f7cd3491-7c6c-480c-bc5c-b2b3b0fb048c",
    "ipAddress": "78.195.109.209",
    "username":"admin"
    }
    }

    Event types

    All admin event types are indicated in the type field of the json event with the admin. prefix. This field is a concatenation of the resourceType and operationType.

    Operation types

    This fields indicates what operation was executed on a resource. This is limited to CREATE, DELETE, UPDATE, ACTION. ACTION is a placeholder for actions that do not fit the classic CRUD model for a resource. For example...

    Resource types

    NameDescription
    AUTH_EXECUTION-
    AUTH_EXECUTION_FLOW-
    AUTH_FLOW-
    AUTHENTICATOR_CONFIG-
    AUTHORIZATION_POLICY-
    AUTHORIZATION_RESOURCE-
    AUTHORIZATION_RESOURCE_SERVER-
    AUTHORIZATION_SCOPE-
    CLIENT-
    CLIENT_INITIAL_ACCESS_MODEL-
    CLIENT_ROLE-
    CLIENT_ROLE_MAPPING-
    CLIENT_SCOPE-
    CLIENT_SCOPE_MAPPING-
    CLUSTER_NODE-
    COMPONENT-
    CUSTOM-
    GROUP-
    GROUP_MEMBERSHIP-
    IDENTITY_PROVIDER-
    IDENTITY_PROVIDER_MAPPER-
    PROTOCOL_MAPPER-
    REALM-
    REALM_ROLE-
    REALM_ROLE_MAPPING-
    REALM_SCOPE_MAPPING-
    REQUIRED_ACTION-
    USER-
    USER_FEDERATION_MAPPER-
    USER_FEDERATION_PROVIDER-
    USER_LOGIN_FAILURE-
    USER_SESSION-

    Resource path

    - + \ No newline at end of file diff --git a/docs/audit-logs/api/index.html b/docs/audit-logs/api/index.html index 96c712c04..8b0c2991b 100644 --- a/docs/audit-logs/api/index.html +++ b/docs/audit-logs/api/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Audit Log API

    Customers can use the existing audit logging mechanism to include their own application's events. There is a single API method that will consume events in the representation below and make them available for searching, filtering and exporting.

    Endpoint

    Events can be posted to the following endpoint. Note that host, port and realm must be replaced with your values. This endpoint accepts and returns JSON content.

    POST https://<host>:<port>/auth/realms/<realm>/events

    Authentication

    Only authenticated users may send events at this time. The current user login access token should be used for browser requests in the Authorization HTTP header as below. Backend requests can be made using a service account or other admin access token.

    Authorization: Bearer <accessToken>

    Field definitions

    Event

    The Event object describes the full action that took place in the system.

    NameTypeRequiredDescription
    uidstringprovidedAn ID unique to this event
    timenumberoptional 1UNIX timestamp of the event
    realmIdstringprovidedThe realm ID where the action took place
    authDetailsAuthDetailsprovided-
    typestringrequiredA description of the action that took place
    operationTypestringoptionalWhat operation was executed on a resource (CREATE, DELETE, UPDATE, ACTION)
    resourcePathstringoptionalIf the action changed a resource, this is meant to be the / delimited resource path
    resourceTypestringoptionalIf the action changed a resource, this is the resource's type
    detailsobjectoptionalAn optional hash of values pertaining to the action that took place
    errorstringoptionalAn optional error string for tracing purposes if the action resulted in a failure

    AuthDetails

    The AuthDetails object provides information on the logged in user executing the action. When posting an event, AuthDetails should be omitted, as it will automatically be provided by the server.

    NameTypeRequiredDescription
    realmIdstringprovidedThe realm ID of the logged in user
    clientIdstringprovidedThe client ID the user was logged into
    userIdstringprovidedThe user ID of the logged in user
    ipAddressstringprovidedThe IP address of the logged in user
    usernamestringprovidedThe username of the logged in user
    sessionIdstringprovidedA unique ID of the current login session

    Responses

    HTTP status codes are used to indicate types of successful or failure states. A short JSON object will also be returned, with content indicating more informatin about an error state.

    Status codeDescription
    202Event received
    400Malformed event
    403API rate limit exceeded
    409Reserved event type
    5xxServer error

    Example

    curl https://app.phasetwo.io/auth/realms/company.app/events \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --header "Authorization: Bearer <accessToken>" \
    --data "{ \"type\": \"foo.bar\" }"

    1. If time is not passed by the caller, it will be provided by the server
    - + \ No newline at end of file diff --git a/docs/audit-logs/index.html b/docs/audit-logs/index.html index d5bab887f..7bfe5e51a 100644 --- a/docs/audit-logs/index.html +++ b/docs/audit-logs/index.html @@ -12,8 +12,8 @@ - - + + @@ -22,7 +22,7 @@ Both Phase Two and customer events are available in the administrative application to be searched, filtered and exported for external consumption.

    - + \ No newline at end of file diff --git a/docs/audit-logs/system/index.html b/docs/audit-logs/system/index.html index fd75e2326..ab094b178 100644 --- a/docs/audit-logs/system/index.html +++ b/docs/audit-logs/system/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    System

    System event types are reported by Phase Two to give the customer information on operational issues with the Phase Two system. Things like system maintenance, scheduled downtime, version updates, outage notifications and more will be published for this event type.

    Event types

    All system event types are indicated in the type field of the json event with the system. prefix. No authDetails object will be included as with access, admin and custom event types.

    - + \ No newline at end of file diff --git a/docs/audit-logs/webhooks/index.html b/docs/audit-logs/webhooks/index.html index f838140ec..e08808b49 100644 --- a/docs/audit-logs/webhooks/index.html +++ b/docs/audit-logs/webhooks/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Webhooks

    Webhooks give application developers the ability to listen for all audit events using an HTTP endpoint. Events are sent to your endpoint using the same format as they are submitted in the audit API.

    Managing webhook subscriptions

    Webhooks are managed with a custom REST resource with the following methods. Use of these methods requires the authenticated user to have the view-events and manage-events permissions. If you are using one of the supported languages, we recommend using our SDKs rather than building the requests yourself.

    PathMethodPayloadReturnsDescription
    /auth/realms/:realm/webhooksGETList of webhook objectsGet webhooks
    /auth/realms/:realm/webhooksPOSTWebhook object201Create webhook
    /auth/realms/:realm/webhooks/:idGETWebhook objectGet webhook
    /auth/realms/:realm/webhooks/:idPUTWebhook object204Update webhook
    /auth/realms/:realm/webhooks/:idDELETE204Delete webhook

    The webhook object has this format:

    {
    "id": "475cd2fd-3ca8-4c22-b5c8-c8b8927dcc10",
    "enabled": "true",
    "url": "https://example.com/some/webhook",
    "secret": "ofj09saP4",
    "eventTypes": [
    "*"
    ],
    "createdBy": "ff730b72-a421-4f6e-9e4e-7fc7f53bac88",
    "createdAt": "2021-04-21T18:25:43-05:00"
    }

    For creating and updating of webhooks, id, createdBy and createdAt are ignored. secret is not sent when fetching webhooks.

    Event types

    The eventTypes variable is an array of expressions that match the type of event. It can contain a wildcard, such as * (send all events), access.* (send all access events), or admin.CLIENT-* (send all admin events related to the CLIENT resource type). It can also be a specific event type such as admin-USER-CREATE (only send user creation events).

    Retries

    Webhooks are sent using an automatic exponential backoff if there is not a 2xx response. The sending tasks are scheduled after the transaction which produced the event has been committed, so there is no question if the activity has occured.

    Client performance

    It is expected that the client will immediately send a 2xx response when receiving an event. If it does not, requests can become backed up and you may miss events because the server will not retry forever.

    Example

    To create a webhook for all events on the test realm:

    POST /auth/realms/test/webhooks

    {
    "enabled": "true",
    "url": "https://exxxxxxxxxxxxxx.m.pipedream.net",
    "secret": "A3jt6D8lz",
    "eventTypes": [
    "*"
    ]
    }

    Testing

    Pipedream is a great way to test your webhooks, and use the data to integrate with your other applications.

    - + \ No newline at end of file diff --git a/docs/authentication/complex-flows/index.html b/docs/authentication/complex-flows/index.html index dbdc3f6df..e5fb3b74d 100644 --- a/docs/authentication/complex-flows/index.html +++ b/docs/authentication/complex-flows/index.html @@ -12,8 +12,8 @@ - - + + @@ -22,7 +22,7 @@

    - + \ No newline at end of file diff --git a/docs/authentication/index.html b/docs/authentication/index.html index 166945e6d..9b53bbd29 100644 --- a/docs/authentication/index.html +++ b/docs/authentication/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Authentication

    Authentication refers to how your users will securely identify themselves to your application. It is also a complex topic, as each application will have its own requirements. There will be several important topics to understand so that you can configure your authentication flows to fit your needs.

    Testing

    If you have not already, it is important to set up a test application such as the debug-app described in the Getting started section. This will provide a mechanism to view and debug your authentication flows, validate that the desired user experience is correct, and the information you want passed to the application is present.

    Authentication topics

    - + \ No newline at end of file diff --git a/docs/authentication/magic-links/index.html b/docs/authentication/magic-links/index.html index b71005745..446063934 100644 --- a/docs/authentication/magic-links/index.html +++ b/docs/authentication/magic-links/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Magic Links

    Magic links are a type of password-less authentication that allow your users to log in to your application following a link that is emailed to them, rather than typing a username and password. We wrote a blog post with more details and advantages/disadvantages in the Magic Links Guide.

    Note that email must be configured before setting up magic links. See the email setup guide if you haven't already done that.

    In the Flows tab of the Authentication section, you will find a flow named magic link. Depending on your setup, it is possible this flow was not installed by default. If it's missing, proceed first to the Create magic link flow section first.

    Open the configuration for the Magic Link Authenticator by clicking the gear icon on the last line with the Magic Link execution. You will see three options:

    • Alias is a useful name to remember this configuration.
    • Force create user creates a new user when an email is provided that does not match an existing user. This allows the use of magic links to register new users that have not been previously seen.
    • Update profile on create adds an UPDATE_PROFILE required action if the user was created. This means that the user will need to fill out other required fields such as first/last name, etc.

    Save the configuration, and go back to the flow page.

    In the Action menu in the upper right of the flow page, select Bind, and select Browser flow.

    Login UI

    After binding the magic link flow to the Browser flow, go back and reload your login screen. You will see that there is only a field for the email address, and the password field is now gone.

    After entering a valid email address, a magic link is sent to that email, and the login screen prompts the user to click on the link to complete the login process

    caution

    This section is currently under construction. Check back soon for updates.

    - + \ No newline at end of file diff --git a/docs/authentication/otps/index.html b/docs/authentication/otps/index.html index 3d391f3e2..7e766fd81 100644 --- a/docs/authentication/otps/index.html +++ b/docs/authentication/otps/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    One-time Passwords

    A one-time password (OTP) is an automatically generated numeric or alphanumeric string of characters that authenticates a user for a single transaction or login session. An OTP is more secure than a static password, especially a user-created password, which can be weak and/or reused across multiple accounts.

    We currently support authenticator apps such as Google Authenticator and FreeOTP, but many others are likely compatible.

    Configuring OTP

    As part of the default browser flow, you may have noticed in the forms section that there are conditional steps to determine if the user has an OTP authentication method configured. This means that OTP checking is already built into the default flow. We're done, right? But what if we want to force the user to configure an OTP application?

    Required action

    In order to force users to configure OTP, you must go to the Required actions tab of the Authentication section and set Enabled and Set as default action both to ON for Configure OTP. This will force the user to set up an OTP method as a barrier to registration and login.

    However, this setting will only apply to new users. For existing users, you must go to the Users section, select the user you wish to edit, and add Configure OTP to the Required user actions field.

    Login UI

    After updating the required action default or giving a test user the action, nothing will change until the user has successfully passed the username-password challenge. After that, the user is presented with a QR code and instructions for setting up their authenticator application.

    On subsequent logins, users will be prompted to enter the OTP from their app during each authentication attempt.

    Create OTP flow

    caution

    This section is currently under construction. Check back soon for updates.

    - + \ No newline at end of file diff --git a/docs/authentication/social-login/index.html b/docs/authentication/social-login/index.html index 2c04d5887..e4099344a 100644 --- a/docs/authentication/social-login/index.html +++ b/docs/authentication/social-login/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Social Login

    Given the use of the default browser flow, configured social identity providers will be added to your authentication flow by default.

    Configuring social providers

    In the Identity provders section, a new social provider can be added with the Add provider button. There are several built-in options you may select from. Many social providers that don't have built-in options can also be configured using standard OpenID Connect v1.0.

    Each provider requires you to do some initial setup on the administrative side of your social login provider. These options are usually referred to as "Applications" or "Login with X". When setting up each social provider, the wizards will take you through the information that is required from the social provider, usually some kind of Client ID and Client Secret.

    Complete examples of all social providers is currently outside of the scope of this section, but the following example should be instructive.

    Example: GitHub

    Before you begin the GitHub portion of the setup, select GitHub from the Add provider dropdown button, and copy the Redirect URI from the next page.

    GitHub setup

    The Settings section of either a personal or organization GitHub account contains a Developer settings section. In the OAuth Apps part of that section select New OAuth App.

    Name your application, give it the URL of your choosing, and paset the Redirect URI into the field labeled Authorization callback URL.

    After clicking Register application, select the Generate new client secret button.

    Copy the new client secret before navigating away, as GitHub will not display it again.

    Finally, go back to the admin UI and paste the Client ID and Client Secret values from GitHub into the fields, and select Add to complete the setup.

    Login UI

    After setting up one or more social identity providers as above, go back and reload your login screen. You will see that the social logins are automatically displayed as alternative authentication mechanism.

    If you wish to temporarily disable a social provider, you can go into the identity provider configuration and either set Enabled to OFF or set Hide on login page to ON.

    Using tokens from social providers

    In some cases, it is desirable to store the tokens granted to your user so that they can be used for other purposes, such as calling social APIs. Extending the GitHub example above, you may wish to offer your users the ability to manage their GitHub repositories from your application.

    There are two fields in the identity provider configuration that may be set to facilitate this use case.

    • Scopes A space-separated list of scopes that are sent when asking for authorization. It usually defaults to only openid, which causes the minimum data to be shared when These will be dictated by the social provider. In the GitHub example, you may also want to include repo, as a granted token with that scope will be authorized to manage the user's repositories.
    • Store tokens Enables token storage following authentication.

    Once you have set these values, then the token will be available for retrieval. More documentation on using external identity provider tokens can be found in the Keycloak documentation on retrieving external IDP tokens.

    - + \ No newline at end of file diff --git a/docs/authentication/sso/index.html b/docs/authentication/sso/index.html index e027479ee..6b9ceb44b 100644 --- a/docs/authentication/sso/index.html +++ b/docs/authentication/sso/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    SSO

    caution

    This section is currently under construction. Check back soon for updates.

    The goal of enterprise SSO is to allow corporate or other organization users to securely access all of the applications and resources available to them with a single authentication. Support for SSO in this context is allowing organizations to use their own identity providers to authenticate with your application.

    Configuring SSO

    This section isn't a full treatment of SSO, but is intended to provide an example setup for using organizations' SSO providers as part of your authentication flow.

    Identity provider setup

    There is a section dedicated to identity provider setup that will be helpful to review here. We will assume you can use one of the methods documented there to set up a connection. This will be either working with an admin at your customer's organization, or allowing them to create the connection using our SSO wizards.

    Once the identity provider setup is complete, note the alias in the Identity providers section.

    Organization setup

    In the Organizations section, create a new organization or navigate to an existing one. In the Identity Provider tab of that organization, select the alias of the identity provider you create that you wish to associate with the organization.

    In order to restrict the use of the identity provider to specific email domains associated with this organization, go to the Settings tab for the organization and add email domains owned by the organization.

    Flow

    Login UI

    After binding the SSO flow to the Browser flow, go back and reload your login screen. You will see that there is only a field for the email address.

    - + \ No newline at end of file diff --git a/docs/authentication/understanding-flows/index.html b/docs/authentication/understanding-flows/index.html index 8b6f4e853..6d36cb309 100644 --- a/docs/authentication/understanding-flows/index.html +++ b/docs/authentication/understanding-flows/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Understanding Flows

    An authentication flow defines the experience your user will go through in securely identifying themselves to your application. It is a container of challenges, screens, and actions, during log in, registration, and other workflows. When we refer to a named flow in the documentation, we are simply referring to such a container, some of which are built-in, and some can be created and configured by you.

    The topic of flows is covered extensively in the Keycloak documentation on authentication flows. In order to facilitate getting setup quickly, we have defined a set of example flows that you can use or extend to build several common flows.

    In each of the sections, we will show you how to use the example flows to setup your own unique authentication flow.

    Authentication admin

    Configuration of flows and how they map to different activities is done in the Admin UI under the Authentication section. The guides and examples in the next section will largely be executed in the Flows tab of that section.

    Flow types

    You will notice many different flow types in this section. Other than for advanced topics, your use will largely be constrained to the browser flow, by duplicating and customizing that flow.

    Binding flows

    The new flows you build can be saved in this UI, and will be selected by assignment called Binding the new flow to the Browser flow. The documentation will refer to this process, which can be achieved using the Action menu on the upper right of each flow.

    Required actions

    In addition to authentication flows, required actions are common tasks that can be assigned to individual users or set as default for all users. These are assigned manually or based on rules. Required actions are actions a user must perform during the authentication process. A user will not be able to complete the authentication process until these actions are complete. For example, an admin may schedule users to reset their passwords every month. An update password required action would be set for all these users. The configuration of these actions is done in the Required actions tab.

    - + \ No newline at end of file diff --git a/docs/authentication/username-password/index.html b/docs/authentication/username-password/index.html index e820a165d..6ec19655c 100644 --- a/docs/authentication/username-password/index.html +++ b/docs/authentication/username-password/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Username-Password

    Username-password authentication is part of the default browser flow. If you select this flow from the Flows tab, you will see the steps that make up the flow.

    Steps

    The first step, called Cookie redirects already authenticated and active users back to the application, not requiring a login challenge.

    Kerberos, disabled by default, can be ignored for now.

    The next step, Identity Provider Redirector is used in the case where your application code sends the user to login with a kc_idp_hint parameter set. This would be used in the case where you are storing the association between a user and their identity provider outside of Keycloak, and you wish to automatically redirect the user to that IdP.

    The forms section of the steps contains a form for input of the username and password, and conditional logic to determine if the user has an OTP method configured.

    Login UI

    When testing the default authentication flow, you will be presented with a simple form. Note that the appearance of Remember me and Forgot password elements are conditional on how you have set up your Realm. Additionaly, you may have already customized your login style, which may lead to different styling.

    - + \ No newline at end of file diff --git a/docs/authentication/webauthn/index.html b/docs/authentication/webauthn/index.html index f963cf94a..df5fc5452 100644 --- a/docs/authentication/webauthn/index.html +++ b/docs/authentication/webauthn/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    WebAuthn

    WebAuthn is a web standard for password-free login. At it's core, it is a browser-based API that allows for web applications to simplify and secure user authentication by using registered devices (phones, laptops, etc) as factors. It uses public key cryptography to protect users from advanced phishing attacks.

    Most modern browsers support WebAuthn, and there are currently many technologies that can be used. External devices such as Yubikeys, integrated software and hardware such Apple and Google Passkeys, and even all-software implementations are all examples.

    Configuring WebAuthn

    It is possible to use WebAuthn both as a second factor (2FA), in addition to username-password, or as a replacement for the password (so-called "passwordless"). We'll describe configuration for both here.

    2FA

    Required action

    Prior to authentication setup, go to the Required actions tab and enable the Webauthn Register action. If you wish to make it mandatory that your users set up a WebAuthn device, also turn on Set as default action.

    Flow

    Use of WebAuthn for is similar to how we used one-time passcodes in the previous section. We'll start by duplicating the browser authentication flow we used there. In the Flows tab, duplicate the browser flow. Name it something like webauthn 2fa.

    Delete the OTP Forms step, and replace it with WebAuthn Authenticator. Make sure it is set to Required

    Login UI

    After binding the new flow to the Browser flow, go back and reload your login screen. It will be a normal username and password entry screen. Following username-password authentication, if you have set Webauthn Register as a required action, the user will be prompted to set up their WebAuthn device.

    The user can potentiall have multiple device choices, based on their device and previous configurations. Depending on the type of device the user selects, they will be prompted by the browser to validate that they wish to use this device with the application. This example is for a hardware security key.

    The browser will also prompt the user to create a label for the device, which allows easy recognition of which device they used in future sessions (e.g. "My Blue Yubikey").

    That label will be used when the user is challenged on previous authentication attempts to prompt them to provide that device as a second factor.

    Passwordless

    Because of the superior security inherent in many devices, it is becoming popular to use WebAuthn as a replacement for passwords. This section will describe a configuration for using WebAuthn to implement a passwordless login flow.

    Required action

    Again, prior to authentication setup, go to the Required actions tab and enable the Webauthn Register Passwordless action. If you wish to make it mandatory that your users set up a WebAuthn device instead of using a password, also turn on Set as default action.

    Flow

    In the Flows tab, duplicate the browser flow. Name it something like webauthn passwordless.

    • Delete the Username Password Form and replace it with Username Form. Make it Required. This will allow collection of only the username or email before the WebAuthn challenge.
    • Delete the Conditional OTP sub-flow.
    • In the forms section, below the Username Form, add the WebAuthn Passwordless Authenticator step. Make it Required.

    Note that this flow makes an assumption that the user has registered a WebAuthn device on registration, or by previous requirement. If the user has not done so, this flow will not allow them to authenticate.

    Login UI

    After binding the new flow to the Browser flow, go back and reload your login screen. You will see that there is only a field for the username or email address, and the password field is gone.

    Once a user enters the email address or username of an existing user with a WebAuthn Passwordless device setup, the next step is a prompt to use that device to complete authentication.

    - + \ No newline at end of file diff --git a/docs/careers/index.html b/docs/careers/index.html index 89b542eb8..cb5fcf69c 100644 --- a/docs/careers/index.html +++ b/docs/careers/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Careers at Phase Two

    Open positions

    Current positions available are listed below. Please apply by sending resume and cover letter to jobs@phasetwo.io.

    Equal Opportunity Employer

    Phase Two is an equal opportunity employer, committed to diversity and inclusiveness. We will consider all qualified applicants without regard to race, color, nationality, gender, gender identity or expression, sexual orientation, religion, disability or age.

    - + \ No newline at end of file diff --git a/docs/getting-started/configuration/index.html b/docs/getting-started/configuration/index.html index 1f11a7864..3519b4832 100644 --- a/docs/getting-started/configuration/index.html +++ b/docs/getting-started/configuration/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Configuration

    There is a sensible set of configuration defaults that have been chosen for common use cases. However, this section will contain a few of the basic changes you may wish to make to tailor the functionality to your specific use case.

    Basic settings

    There are a couple of default configuration choices you should be aware of before opening up your application to the world.

    Login and registration

    In the Realm settings section, in the Login tab, you will find several important settings.

    • User registration controls if users can register for your application, and enables the default registration screens.
    • Email as username controls if the users email address will be used as their username.
    • Login with email allows users to log in with their email address, regardless if their email is their username.
    • Verify email requires the user to verify their email address. An email server must be set up in your account in order to send a verification email to your users. See the email server configuration guide for more information.

    User profile

    By default, only username, email, first name and last name are collected. It is possible to add more requirements for your users.

    In the Realm settings section, in the General tab, you must turn on User Profile Enabled in order to allow additional fields. Once you save the configuration, another tab called User profile will appear. You can add additional fields, control who can view and manage those fields, set requirements, and add validation.

    Realm setup

    Depending on how you are using Phase Two (i.e. self-service, dedicated deployments, Docker image, etc.), your initial configuration might differ slightly. These options may already have been set by default, but they are documented here in case not. It is also useful to understand the purpose for each selection.

    Themes

    In the Realm settings section, select the Themes tab.

    Login

    The Attributes login theme is the Phase Two default that allows customizing the login UI without deploying a custom theme. This must be selected in order to use the options detailed in the customizing UI section.

    Admin console

    If you are not seeing an Extensions section at the bottom of the left nav, you need to set the Admin console theme to phasetwo.v2. This theme contains all of the admin UI for managing the Phase Two extensions. You must log out and log back into the Admin UI in order for this change to take effect.

    Email

    The Attributes email theme is the Phase Two default that allows the use of the email template UI detailed in the emails section.

    - + \ No newline at end of file diff --git a/docs/getting-started/customizing-ui/index.html b/docs/getting-started/customizing-ui/index.html index cb7202c7e..3f64b3d16 100644 --- a/docs/getting-started/customizing-ui/index.html +++ b/docs/getting-started/customizing-ui/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Customizing UI

    It is possible to customize styles for login screens to match your branding. This can be achieved by simple colors and logo override of the default them, or by full CSS replacement.

    Simple

    The simple override of colors and logo can be access in the admin UI in the Styles section. The available override values are

    • General tab
      • Logo: URL of your logo image. This will be constrained to 150x150. You should use an SVG or PNG with alpha channel to make sure this renders properly on your background color and in the Admin Portal, if you are using it.
      • Favicon: URL of your favicon.

    • Login tab
      • Background color: Hex color for background.
      • Primary color: Hex color for primary.
      • Secondary color: Hex color for secondary.

    Full CSS

    Full CSS can be added on the same page in the Admin UI. This stylesheet will be loaded last. Note that the use of a full CSS file will override the values from above. For more information on the styles used in the login pages, see Patternfly v4, and the Keycloak base and keycloak themes.

    Maually by Realm attributes

    The above methods for updating the style store the values as Realm attributes. If you prefer to programmatically set these, use the following Realm attribute keys:

    • _providerConfig.assets.login.css
    • _providerConfig.assets.login.primaryColor
    • _providerConfig.assets.login.secondaryColor
    • _providerConfig.assets.login.backgroundColor
    • _providerConfig.assets.logo.url
    • _providerConfig.assets.favicon.url
    - + \ No newline at end of file diff --git a/docs/getting-started/email/index.html b/docs/getting-started/email/index.html index 27bc4ea65..cc0109fd4 100644 --- a/docs/getting-started/email/index.html +++ b/docs/getting-started/email/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Emails

    This section details setting up email delivery with your email service and customizing the content templates to match your brand and messaging.

    Server configuration

    One of the first things you will need to do when getting a Keycloak Realm ready for use is to set up your email server configuration. There are many system emails that are sent to users in the course of verifying and updating user accounts: Email address verification, magic links, password reset, account update, login failure notifications, identity provider linking, etc.

    In order to provide your users with a positive experience, these messages need a way to get to them. Keycloak supports any internet reachable SMTP server. If you are currently testing, and don't have an email server or service that you currently use, SendGrid provides free accounts that allow you to send up to 100 emails per day forever. For debugging, you can also use a service like MailTrap to give you a catch-all for emails coming from Keycloak.

    In the Admin UI, select Realm settings in the left menu, and then click the Email tab.

    In the first section, labeled Template, you will set options that will be used in the templates for the emails that are sent to your users. The only required field is the From field, which must contain the email address the user will see the email originating from. This should be an email address that your email server is expecting, and it will not block for authorization reasons.

    The other fields in the Template section are not required, but will enhance how your emails look:

    • From address used to send emails (required)
    • From display name a user-friendly name displayed along From
    • Reply to an email address that will be used by email clients when your user replies to an email
    • Reply to display name a user-friendly name displayed along Reply to
    • Envelope from Bounce Address used for the mails that are rejected

    In the Connection & Authentication section, you will provide details of your SMTP server:

    • Host indicates the SMTP server hostname used for sending emails
    • Port indicates the SMTP server port (usually 25, 465, 587, or 2525)
    • Encryption support encryption for communication with your SMTP server
    • Authentication if your SMTP server requires authentication, and supply the Username and Password

    Finally, before you click Save, click the Test connection button to send a test email to the email address of the currently logged in user. If you don't have that set, you might have click Save and edit your user before you come back. You'll receive a success message, or information that will help you resolve problems.

    Content templates

    Email content can be modified in the Styles part of the Extensions section, in the Emails tab. There are several default email types that you can modify.

    • Execute Required Actions
    • Link to Identity Provider
    • Login error
    • Magic Link
    • Organization Invitation
    • Password Reset
    • Remove OTP
    • Update OTP
    • Update Password
    • Update confirmation
    • Verification
    • Verification with code

    The templates are made in both text and HTML, as emails are assembled as multi-part messages that can display either type depending on the User's email client and accessibility settings. You must edit content for both types if you are making changes.

    Templating syntax

    The syntax of the templates roughly follows that of mustache.js which allows the replacement of values using tags enclosed by double braces, like this {{name}}. Each email template type exposes a set of named values and objects that can be used in your templates. At minimum, they are:

    • user.username
    • user.firstName
    • user.lastName
    • user.email
    • user.attributes.<name>
    • realmName
    • url.loginUrl
    • url.registrationUrl

    Use the default templates to see the available variables for each template type.

    - + \ No newline at end of file diff --git a/docs/getting-started/index.html b/docs/getting-started/index.html index 484ddb522..747e01936 100644 --- a/docs/getting-started/index.html +++ b/docs/getting-started/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Getting Started

    This section is indended as a quickstart for the impatient, the experienced, or both. There are a world of options between Keycloak itself and the Phase Two extensions, and this section does not attempt to do anything but scratch the surface. Busy developers want quick validation for proofs-of-concept, and this should get you there quickly.

    - + \ No newline at end of file diff --git a/docs/getting-started/launch-checklist/index.html b/docs/getting-started/launch-checklist/index.html index 10a02313a..de8424c46 100644 --- a/docs/getting-started/launch-checklist/index.html +++ b/docs/getting-started/launch-checklist/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Launch Checklist

    This section contains a set of bare minimum requirements to make sure you're ready to move your application into production. Much of the options and configuration are beyond the scope of this section, but this is intended to give you a place to check that you've thought about the major areas before going live.

    As with any integrations, we suggest you create a separate Realm for development and testing, so that there are no accidental changes that will affect your users in production.

    Configuration

    • Basic setup. Make decisions about registration, usernames, emails, user profile elements, password policies, and more. See Configuration.
    • Email server. Configure your email server details so that your users can receive messages related to authentication (e.g., forgot password, email address verification, etc.). See Email server configuration.

    Customization

    • Branding. At a minimum, update your colors and logo to make the login screens reflect your brand. See Customizing UI.
    • Emails. Customize email templates with your messaging and branding. See Email templates.

    Authentication

    • Client setup. Add your app's details for secure redirect after authentication. See Client setup.
    • Flows. Set up any social login providers and other non-standard elements in your login flows, such as magic links, 2FA, and SSO. See Authentication.

    Integration

    • Secure your app. Set up your application to trigger login and protect elements requiring authentication. Receive user identity and profile information after successful authentication. See Securing applications.
    • User management. Add user-managed elements to your application using the admin portal functionality. See Admin portal.
    - + \ No newline at end of file diff --git a/docs/getting-started/sample/index.html b/docs/getting-started/sample/index.html index 0278a35da..f909e2279 100644 --- a/docs/getting-started/sample/index.html +++ b/docs/getting-started/sample/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Sample Application

    Most users will want to get started quickly with a sample of how to secure an application. Once you have registered for a Phase Two account, you can clone our demo app repository to see a demonstration of authentication in action.

    https://github.com/p2-inc/debug-app

    Client setup

    Before you configure and start the sample application, you must build a Client configuration. In Keycloak, a "Client" is synonymous with an application you are securing.

    Log into your Keycloak realm, and click on Clients in the left navigation, and click Create client.

    1. Enter frontend as the Client ID and click Next (you can name this whatever you want)
    2. In the Capability config screen, keep the defaults and click Save
    3. In the Access settings screen, enter the following values:
      1. http://localhost:3001/* for Valid redirect URIs
      2. + in Valid post logout redirect URIs
      3. + in Web origins
    4. Click Save
    5. In the upper right corner, open the Action menu and select Download adapter config. Click Download and save the file for the next step.

    Sample configuration

    Clone that repo to your local machine. You'll find the applications in the frontend and backend directory, and a set of supporting files for configuration and deployment.

    git clone https://github.com/p2-inc/debug-app.git
    cd debug-app/frontend

    Once you have cloned the repo, edit the keycloak.json file and switch it with the configuration you downloaded at the end of the Client setup. It should look something like this (you'll need to replace the values in curly braces).

    {
    "realm": "{realm}",
    "auth-server-url": "https://{host}/auth/",
    "ssl-required": "external",
    "resource": "frontend",
    "public-client": true,
    "confidential-port": 0
    }

    Then run the server using the included script:

    ./start.sh

    And open http://localhost:3001/ in your browser. Once you have logged in, you'll see some information about the user that is contained the "access token" (more about that later).

    Curious how it works? Most of the functionality for quickly securing an app is contained in app.js. More detailed information is contained in the next section, dedicated to securing applications.

    - + \ No newline at end of file diff --git a/docs/hosting/connect/index.html b/docs/hosting/connect/index.html index c0d5c7228..a91dcf6dc 100644 --- a/docs/hosting/connect/index.html +++ b/docs/hosting/connect/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Connect

    info

    Phase Two Connect is currently available by invitation only. Contact us for more information.

    caution

    This section is currently under construction. Check back soon for updates.

    Phase Two Connect solves one of the top issues with on-prem software installation and onboarding: connecting the customer identity provider. It allows the vendor to implement a simple, standard interface, giving them access to an extensive catalog of identity provider support, with guided onboarding wizards for the customer.

    If your software is currently secured using Keycloak, Phase Two or another OIDC method, updating it to use Phase Two Connect is a quick integration that will give you significant automation to one of the most time-consuming aspects of on-prem software customer onboarding.

    See a short video demonstrating how the Phase Two Connect "wizards" streamline IdP setup:

    The standard Phase Two Docker image contains the base version of Phase Two Connect by default. This restricts the IdP onboarding wizards to the generic SAML, OIDC and LDAP protocol wizards. If you are a licensee with a support agreement, talk to your customer success representative about accessing your private image registry.

    Realm import

    You are encouraged not to use the master realm to secure your deployed application. A default realm with your initial setup parameters can be imported on install. Please see the guide on importing a realm a startup. Your specific method will depend on if you are deploying with Kubernetes or some other mechanism.

    Onboarding Guide

    It is recommended to provide your on-prem customers with an onboarding guide that includes a way of accessing their Keycloak credentials following initial installtion in their cluster. You can create a link to the Phase Two Connect onboarding portal using the following format:

        https://<host>:<port>/auth/realms/<realm>/wizard/

    Your customer will use their admin credentials to login and setup their IdP integration.

    - + \ No newline at end of file diff --git a/docs/hosting/index.html b/docs/hosting/index.html index 2bcfab5fe..2bc72337e 100644 --- a/docs/hosting/index.html +++ b/docs/hosting/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Hosting

    Phase Two is currently available for two primary hosted use cases:

    1. Self-hosted for public cloud applications. Organizations that have compliance, data residency, or other requirements not met by the hosted versions of Phase Two can install on their own cloud. Both for community supported and paid versions, we distribute Docker images and Kubernetes Helm charts to expedite successful setup.

    2. Bundled and distributed with your software to your customer's site. Many traditional software and SaaS companies are being asked, by customers in government and regulated industry, to deploy on-prem, and have requirements to connect to the customer identity provider. The complexity of potential identity providers and the customer service burden required to onboard has caused us to create a specific offering for this use case. Phase Two Connect allows you to implement a simple, standard interface, giving your customers access to an extensive catalog of identity provider support, with guided onboarding wizards.

    - + \ No newline at end of file diff --git a/docs/hosting/kubernetes/index.html b/docs/hosting/kubernetes/index.html index cbf49191e..0f704b41b 100644 --- a/docs/hosting/kubernetes/index.html +++ b/docs/hosting/kubernetes/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    - + \ No newline at end of file diff --git a/docs/introduction/documentation/index.html b/docs/introduction/documentation/index.html index c4461104b..b27f3c50b 100644 --- a/docs/introduction/documentation/index.html +++ b/docs/introduction/documentation/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    - + \ No newline at end of file diff --git a/docs/introduction/index.html b/docs/introduction/index.html index d6fa18425..a5a0f27be 100644 --- a/docs/introduction/index.html +++ b/docs/introduction/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Introduction

    Welcome to Phase Two. We have built a toolkit for some of the most common features and pain points you will encounter when building your SaaS application. Initially, we have focused on authentication and authorization use cases. Our goal is to build secure and easy-to-use tools that will accelerate your time to market and adoption by enterprise customers.

    Phase Two is unique in that it allows you to build against a common set of tools, libraries and APIs whether you are using our hosted version for a public cloud product, hosting it yourself, or deploying to a customer site.

    We are constantly looking for ways to improve our documentation, APIs and developer experience. If you have suggestions or requests, please don't hesitate to contact us. If you prefer a more hands-on approach, you can submit a pull request in our Github documentation repository.

    - + \ No newline at end of file diff --git a/docs/introduction/keycloak/index.html b/docs/introduction/keycloak/index.html index 033fc01f5..cd6621903 100644 --- a/docs/introduction/keycloak/index.html +++ b/docs/introduction/keycloak/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Keycloak

    Phase Two is based on the Keycloak Open Source Identity and Access Management system, built and maintained by Red Hat.

    Keycloak has been battle-tested and hardened for many years. It's security and reliability is depended on by organizations from small startups to Fortune 500 companies and governments.

    Phase Two is built as a collection of essential Keycloak extensions. While we endeavor to make Keycloak simple to use, operate and scale, we will maintain compatibility so that customers can always choose to migrate to their own Keycloak deployment. Our core extensions will always be open source.

    - + \ No newline at end of file diff --git a/docs/introduction/open-source/index.html b/docs/introduction/open-source/index.html index 84f200a19..057dd246e 100644 --- a/docs/introduction/open-source/index.html +++ b/docs/introduction/open-source/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Open Source

    The core extensions to Keycloak that Phase Two is built on will always be open source so that you can migrate to your own Keycloak deployment. Below is a list of the relevant extensions and their current status.

    Components and repos

    ComponentStatusRepositoryDescription
    Eventshttps://github.com/p2-inc/keycloak-eventsAll event listener implementations.
    Magic Linkhttps://github.com/p2-inc/keycloak-magic-linkMagic Link Authentication. Created with an Authenticator or Resource.
    Organizationshttps://github.com/p2-inc/keycloak-orgsOrganizations multi-tenant entities, resources and APIs.
    Themeshttps://github.com/p2-inc/keycloak-themesLogin and email theme customizations via Realm attributes without deploying an extension.
    Admin UIhttps://github.com/p2-inc/keycloak-uiAdmin UI customizations.
    Admin Portalhttps://github.com/p2-inc/phasetwo-admin-portalUser self-management for their account and organizations.
    User Migrationhttps://github.com/p2-inc/keycloak-user-migrationUser migration storage provider and API client.

    Docker

    We distribute a Docker image that combines the above extensions with the Keycloak image. While the online Self-service tool is the easiest way to try Phase Two, and includes additional features and extensions, if you want to try it on your own, the Docker image is the fastest way to do it. Documentation and examples for using it are in the phasetwo-containers repository. The most recent version of these extension are included.

    What isn't there

    It is important to note that not all of Phase Two's extensions will be available as open source. The extensions that are considered "non-core" relate to functionality that is not essential in order to migrate to your own Keycloak deployment. This includes all changes to the underlying storage architecture that allows Phase Two to achieve larger scale than a standard Keycloak deployment. This also includes the user experience features targeted at IdP onboarding, such as the Phase Two Connect on-prem wizards and dashboards.

    Also, if you are currently a paying customer, either for the hosted version or Phase Two Connect, and there is an extension that is not yet open sourced, you can request early access. Please contact us for more information.

    Dual licensing

    The open source Phase Two extensions are licensed under the Elastic License v2. Paid customers for the self-service or hosted offerings are using the license specified in our SaaS Service Agreement. On-prem and other licensees can refer to their specific service agreements.

    Open source license

    With the Elastic License, you can use and host the Phase Two Keycloak extensions as part of your own product. You may not sell the extensions as a hosted or managed service, which includes bundling and distribution by companies who sell their products for on-prem and private cloud use. You are free to create derivative works, but they must maintain the same license, copyright and other notices. You may not represent the Phase Two Keycloak extensions as your own work of authorship.

    Paying customers that have accepted our service agreements can use, host and distribute the Phase Two Keycloak extensions in accordance with the restrictions defined in those agreements. You will have access to extensions that are not available as open source, such as our identity provider vendor wizards. Modifications and additional extensions are permitted. You may not use, host or distribute the extensions in a way that is competitive to Phase Two.

    - + \ No newline at end of file diff --git a/docs/organizations/attributes/index.html b/docs/organizations/attributes/index.html index 3cb7194fa..059666a24 100644 --- a/docs/organizations/attributes/index.html +++ b/docs/organizations/attributes/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Attributes

    Attributes functionality provides key-value storage of Organization attributes that can be used in your application to customize experience. The ability to manage attributes is available in the Organizations tab of the Admin UI.

    Adding attributes to the token

    It is possible to map all attributes of an organization into the access token, ID token or userinfo endpoint response using the Organization Attribute token claim mapper for OIDC. If you have users that will have a large number of organization memberships or attributes per organization, it is recommended that you only add the claim to the userinfo endpoint response, as it may cause large token sizes.

    API access

    It is possible to get and update organization attributes using the API. The attributes object is a child of the organization object. It is a hash between attribute name and an array of values. They are read and updated using the organization get, create and update methods.

    - + \ No newline at end of file diff --git a/docs/organizations/identity-providers/index.html b/docs/organizations/identity-providers/index.html index 6cbada9dc..ef1ca595d 100644 --- a/docs/organizations/identity-providers/index.html +++ b/docs/organizations/identity-providers/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Identity providers

    Identity providers (IdPs) can be associated with an organization for the purpose of directing users to authenticate with the IdP via a verified email domain, and for automatically granting membership to users who authenticate with that IdP.

    Associating an identity provider

    There are two ways that an IdP can be associated with an organization.

    Admin portal

    If you have activated the organizations and SSO portions of the admin portal, members of the organization with the appropriate roles can set up their IdPs on their own. When they create and manage IdPs this way, they will be automatcially associated with the user's organization.

    Admin UI

    The admin UI contains an Identity providers section where full details of all IdPs can be created and managed. Also, the IdP wizards that are available to your organization administrators through the admin portal are also available to site administrators by navigating to https://{host}/auth/realms/{realm}/wizard/.

    Because IdPs created using the admin UI or wizards are done by a site administrator, there is no organization associated by default. To do so, you can navigate to the Organizations section, select the organization for assignment and select the Identity providers tab. Making the association here will create the necessary relationships with the organization.

    Verified domains

    The purpose of associating domains with an organization is to allow identity providers to be conditionally shown based on email domain of a user who is logging in. In the Settings tab of each organizations, you can add multiple domains. When an authentication flow is configured properly to allow for redirection based on email domain, these are the domains that will be used to look up the associating identity provider.

    Adding IdP and organization information to the token

    Following a successful login using an IdP associated with an organization, a few values will be set in what are called "User Session Notes".

    • org_id - The ID of the organization associated with the IdP.
    • identity_provider - The IdP alias of the broker used to perform the login.
    • identity_provider_identity - The IdP username of the currently authenticated user.

    These are values that survive the duration of a user login session, and can be used in token mappers. In order to add such a mapper in the admin UI, navigate to the client you are using, select the Client scopes tab, select the {client}-dedicated scope link, select the Mappers tab and configure a new mapper of type User Session Notes. You can choose the Token Claim Name that you require, and configure which tokens it is included.

    API access

    It is possible to manage all aspects of the identity provider and its relationship to the organization using the API. You will notice that the method in the Phase Two API are similar to those in the Keycloak Admin API. If you are building software that is targeted at organization administrators, you should use the Phase Two API, as it uses the permission model for organizations, and is not compatible with the Keycloak Admin API.

    - + \ No newline at end of file diff --git a/docs/organizations/index.html b/docs/organizations/index.html index d29cb77ec..55cc4f4fa 100644 --- a/docs/organizations/index.html +++ b/docs/organizations/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Organizations

    The Organizations feature represents an significant enhancement to standard Keycloak that allows business-to-business (B2B) customers to better manage their partners and customers, and to customize the ways that end-users access their applications. Phase Two customers can use Organizations to:

    • Represent their business customers and partners in Phase Two and manage their membership.
    • Represent attributes and roles, unique to business customers and partners.
    • Provide streamlined invitations to Organizations.
    • Allow the self-management of business customers' Identity Providers and Users using our hosted portal and setup wizards.
    • Build administration capabilities into their products, using Organizations APIs, so that those businesses can manage their own organizations.

    Creating and managing organizations

    Organizations can be managed in the Admin UI in the Organizations section. It is possible here to create Organizations, and manage their attributes, membership, invitations, roles, and associated identity providers.

    Feature guides

    - + \ No newline at end of file diff --git a/docs/organizations/invitations/index.html b/docs/organizations/invitations/index.html index 2a3151d11..9350066b7 100644 --- a/docs/organizations/invitations/index.html +++ b/docs/organizations/invitations/index.html @@ -12,8 +12,8 @@ - - + + @@ -22,7 +22,7 @@ Invitations can be managed by the Keycloak admin in the Organizations tab of the Admin UI. You can enable organization administrators to manage invitations using the organization portal or building it into your application using the API.

    Setup

    Invitees may or may not be existing users. If you choose to allow invitations to emails that are not represented by existing users, you must allow registration to your application, or use an authentication flow that automatically creates user accounts (such as magic link authentication).

    It is possible to pre-select any roles for a user by selecting in the Admin UI, or passing the roles array in the API. This will automatically add the user to the given roles upon user creation and acceptance of the invitation.

    Invitees will receive an email indicating the realm, organization and inviter. It is possible to add a specific application redirect URI so that they will be redirected to a specific location in the application.

    Following user creation and authentication, invitees will be prompted to accept or decline any outstanding invitations. In addition to any roles that were selected in the invitation, the user will automatically be added as a member to the inviting organization.

    API access

    It is possible to create, remove pending and fetch all outstanding organization invitations using the API.

    - + \ No newline at end of file diff --git a/docs/organizations/membership/index.html b/docs/organizations/membership/index.html index bc0af751a..200dfbbc1 100644 --- a/docs/organizations/membership/index.html +++ b/docs/organizations/membership/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Membership

    Users who are associated with an organization are considered members. The relationship of users to organizations can be managed in the Organizations tab of the Admin UI. Invitations also provide a way to allow organization administrators to invite new members to the organization. If you are associating an identity provider with an organization, all users who authenticate through an associated identity provider will automatically be added as members to the organization.

    Anonymous organization administrator

    When an organization is created, you will notice that a user is created with the username org-admin-{org_id}. This is a machine user that can be used for the purpose of sending admin portal links over email to known, trusted administrators who don't otherwise have access through the application. It is recommended that you do not edit this user or make any changes to its setup, or it could compromise the security of your application.

    Adding organizations to the token

    It is possible to map organization membership into the access token, ID token or userinfo endpoint response using the Organization Role token claim mapper for OIDC. It is possible to configure the mapper to exclude the roles. If you have users that will have a large number of organization memberships or roles per organization, it is recommended that you only add the claim to the userinfo endpoint response, as it may cause large token sizes.

    API access

    It is possible to create, verify, delete and fetch all organization memberships using the API.

    - + \ No newline at end of file diff --git a/docs/organizations/portal/index.html b/docs/organizations/portal/index.html index f98f79b16..ac903e394 100644 --- a/docs/organizations/portal/index.html +++ b/docs/organizations/portal/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Portal

    The Organization Portal allows members of an organization with appropriate permissions to self-manage their organization. This includes details, attributes, memberships, verified domains, identity providers, as well as view information about events that have occured relevant to access.

    There are two mechanisms of creating a portal link. Choosing one depends on how you wish to expose this to your users.

    Admin UI

    In the Admin UI, in the Organizations tab, when you select an organization, the upper right context menu allows you to select Create Portal Link. This will create a portal link for the default organization user. This is a user that is created by default when the organization is created for the purpose of executing administrative tasks for the organization. It is not associated with a real member of the organization. It has full privileges within the organization, so be careful who and how this link is distributed. It will be active for 1 day following creation.

    API

    You can programmatically create portal links for your users with the API. This allows you to create a link for a specific user. The portal itself will take care of restricting access based on that user's organization permissions. Please refer to the API documentation to create a link for the organization's admin portal. Because this is an expiring link, it is recommended that you do not create the link until it has been selected in your application.

    - + \ No newline at end of file diff --git a/docs/organizations/roles/index.html b/docs/organizations/roles/index.html index 5d11dbe90..e7023ec60 100644 --- a/docs/organizations/roles/index.html +++ b/docs/organizations/roles/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Roles

    Members of an organization can have role assignments that are specific to that organization. These are separate from Keycloak realm and client roles, and do not inherit from them. There are a set of default roles that control access to functionality within Phase Two, and additional roles can be added for your application purposes. Role creation, management and assigment can be done in the Organizations tab of the Admin UI.

    Default roles

    The default roles can be assigned to users to give them access to view and manage organization data.

    NameAccess
    view-organizationView organization details.
    manage-organizationUpdate organization details. Delete the organization.
    view-membersView memberships.
    manage-membersAdd and remove memberships.
    view-rolesView roles and assignements.
    manage-rolesCreate and remove roles and assignments.
    view-invitationsView outstanding invitations.
    manage-invitationsCreate and remove pending invitations.
    view-identity-providersView configured identity providers for SSO.
    manage-identity-providersCreate, manage and remove identity providers for SSO.

    Custom roles

    Custom roles can be created in the Roles section of each organization in the Organizations tab of the Admin UI. It is important to note that a role is created for each organization individually. If you wish to have the same role name for multiple organization, it is recommended that you create them programmatically on organization creation.

    Adding roles to the token

    It is possible to map organization roles into the access token, ID token or userinfo endpoint response using the Organization Role token claim mapper for OIDC. If you have users that will have a large number of organization memberships or roles per organization, it is recommended that you only add the claim to the userinfo endpoint response, as it may cause large token sizes.

    API access

    It is possible to create, update, delete and fetch all organization roles, as well as grant, revoke, verify and fetch all user assignments using the API.

    - + \ No newline at end of file diff --git a/docs/privacy/index.html b/docs/privacy/index.html index 4a9a5f13e..d58d81507 100644 --- a/docs/privacy/index.html +++ b/docs/privacy/index.html @@ -12,8 +12,8 @@ - - + + @@ -301,7 +301,7 @@ page.

    Contact Us

    If you have any questions about this Privacy Policy, You can contact us:

    - + \ No newline at end of file diff --git a/docs/securing-applications/django/index.html b/docs/securing-applications/django/index.html index 6def3c5fb..28c264844 100644 --- a/docs/securing-applications/django/index.html +++ b/docs/securing-applications/django/index.html @@ -12,8 +12,8 @@ - - + + @@ -25,7 +25,7 @@ Update your Django app's urls.py to include the authentication URLs provided by mozilla-django-oidc:

    urlpatterns += [
    path('oidc/', include('mozilla_django_oidc.urls')),
    ]

    Using it in your app

    Protect your views

    Use Decorators for Access Control. You can now use the @oidc_protected decorator to protect views that require authentication and potentially specific roles:

    from mozilla_django_oidc.decorators import oidc_protected

    @oidc_protected
    def protected_view(request):
    # Your view logic

    Accessing user information

    You can access user information after authentication using the request.oidc_user attribute. For example:

    def profile_view(request):
    user_info = request.oidc_user.userinfo
    # Access user_info['sub'], user_info['email'], etc.
    # Your view logic

    By default, mozilla-django-oidc looks up a Django user matching the email field to the email address returned in the user info data from Keycloak.

    If a user logs into your site and doesn’t already have an account, by default, mozilla-django-oidc will create a new Django user account. It will create the User instance filling in the username (hash of the email address) and email fields.

    Use Username rather than Email

    mozilla-django-oidc defaults to setting up Django users using the email address as the user name from keycloak was required. Fortunately, preferred_username is set up by default in Keycloak as a claim. The claim can used by overriding the OIDCAuthenticationBackend class in mozilla_django_oidc.auth and referring to this in AUTHENTICATION_BACKENDS as below:


    # Classes to override default OIDCAuthenticationBackend (Keycloak authentication)
    from mozilla_django_oidc.auth import OIDCAuthenticationBackend

    class KeycloakOIDCAuthenticationBackend(OIDCAuthenticationBackend):

    def create_user(self, claims):
    """ Overrides Authentication Backend so that Django users are
    created with the keycloak preferred_username.
    If nothing found matching the email, then try the username.
    """
    user = super(KeycloakOIDCAuthenticationBackend, self).create_user(claims)
    user.first_name = claims.get('given_name', '')
    user.last_name = claims.get('family_name', '')
    user.email = claims.get('email')
    user.username = claims.get('preferred_username')
    user.save()
    return user

    def filter_users_by_claims(self, claims):
    """ Return all users matching the specified email.
    If nothing found matching the email, then try the username
    """
    email = claims.get('email')
    preferred_username = claims.get('preferred_username')

    if not email:
    return self.UserModel.objects.none()
    users = self.UserModel.objects.filter(email__iexact=email)

    if len(users) < 1:
    if not preferred_username:
    return self.UserModel.objects.none()
    users = self.UserModel.objects.filter(username__iexact=preferred_username)
    return users

    def update_user(self, user, claims):
    user.first_name = claims.get('given_name', '')
    user.last_name = claims.get('family_name', '')
    user.email = claims.get('email')
    user.username = claims.get('preferred_username')
    user.save()
    return user

    In settings.py, overide the new library you have just added in AUTHENTICATION_BACKENDS :

     # mozilla_django_oidc - Keycloak authentication
    "fragalysis.auth.KeycloakOIDCAuthenticationBackend",

    Logging out

    You can use the @oidc_logout decorator to log the user out of both your app and Keycloak:

    from mozilla_django_oidc.decorators import oidc_logout

    @oidc_logout
    def logout_view(request):
    # Your logout view logic

    Add support for Django Rest Framework

    Django Rest Framework (DRF) is a flexible toolkit built on top of Django, specifically designed for building RESTful APIs.

    If you want DRF to authenticate users based on an OAuth access token provided in the Authorization header, you can use the DRF-specific authentication class which ships with the package.

    Add this to your settings:

    REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
    'mozilla_django_oidc.contrib.drf.OIDCAuthentication',
    'rest_framework.authentication.SessionAuthentication',
    # other authentication classes, if needed
    ],
    }

    Note that this only takes care of authenticating against an access token, and provides no options to create or renew tokens.

    If you’ve created a custom Django OIDCAuthenticationBackend and added that to your AUTHENTICATION_BACKENDS, the DRF class should be smart enough to figure that out. Alternatively, you can manually set the OIDC backend to use:

    OIDC_DRF_AUTH_BACKEND = 'mozilla_django_oidc.auth.OIDCAuthenticationBackend'
    - + \ No newline at end of file diff --git a/docs/securing-applications/index.html b/docs/securing-applications/index.html index b761b5510..1bc5874ee 100644 --- a/docs/securing-applications/index.html +++ b/docs/securing-applications/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Securing Applications

    Phase Two is an implementation of the OpenID Connect specification. That means, no custom libraries or code are required Your applications and services can be secured using any compliant OpenID Connect Relying Party library. There are lists maintained by the OpenID Foundation of client libraries.

    Confused? We will also use this category as a place to provide language and framework specific guides to make securing your applications easier.

    Guides

    Libraries

    Also, here is an unofficial list of OpenID Connect libraries we've heard good things about. Please email us if you're a library author, and you'd like to see your library linked here, or if you've had success with a library not listed here.

    - + \ No newline at end of file diff --git a/docs/securing-applications/javascript/index.html b/docs/securing-applications/javascript/index.html index 7ec566c64..6bd655546 100644 --- a/docs/securing-applications/javascript/index.html +++ b/docs/securing-applications/javascript/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    JavaScript

    Most modern applications are being built as single-page apps. The easiest way to secure these is with the JavaScript keycloak-js library. If you are using a package manager like NPM, you can use it from there. If you are importing it direclty, the library is served by the server at `https://{host}/auth/js/keycloak.js

    Example

    You must replace the {host}, {realm} and {clientId} values with those from your account.

    <html>
    <head>
    <script src="https://{host}/auth/js/keycloak.js"></script>
    <script>
    var auth = new Keycloak({
    url: "https://{host}/auth",
    realm: "{realm}",
    clientId: "{clientId}",
    });
    auth
    .init({
    onLoad: "login-required",
    })
    .then(function (authenticated) {
    alert(authenticated ? "authenticated" : "not authenticated");
    })
    .catch(function () {
    alert("failed to initialize");
    });
    </script>
    </head>
    <body>
    <div id="message"></div>
    </body>
    </html>

    A complete specification of what is available in the keycloak-js library is available in their official documentation https://www.keycloak.org/docs/latest/securing_apps/#_javascript_adapter

    - + \ No newline at end of file diff --git a/docs/securing-applications/next/index.html b/docs/securing-applications/next/index.html index 5ff11f1d6..6b9154699 100644 --- a/docs/securing-applications/next/index.html +++ b/docs/securing-applications/next/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Next.js

    This example uses Next.js 13 and splits server and client components accordingly.

    1. Clone the Phase Two example repo.

    2. Open the Next.js folder within /frameworks/nextjs.

    3. Run npm install and then npm run dev. This example leverages NextAuth.js to provide hook and HOC support.

    4. NextAuth.js configures an API route that is uses for the Authentication of the Client. It generates the routes automatically for you. These are added to Next.js in the api/auth/[...nextauth]/route.ts file.

    5. Open the src/lib/auth.ts file. This is a server only file. We will be updating a few values from the prior section where we set up our OIDC client. Taking the values from the OIDC Client Config section, set those values in the code. While it is recommended to use Environment variables for the secret, for the purpose of this tutorial, paste in the Client secret from the OIDC client creation section for the value of clientSecret

      const authServerUrl = "https://euc1.auth.ac/auth/";
      const realm = "shared-deployment-001";
      const clientId = "reg-example-1";
      const clientSecret = "CLIENT_SECRET"; // Paste "Client secret" here. Use Environment variables in prod

      Those are used to popluate the AuthOptions config for the KeycloakProvider:

      export const AuthOptions: NextAuthOptions = {
      providers: [
      KeycloakProvider({
      clientId,
      clientSecret,
      issuer: `${authServerUrl}realms/${realm}`,
      }),
      ],
      };

      The config is then provided to the AuthProvider in the /src/app/layout.tsx file. Next.js uses this file to generate an HTML view for this page.

      import { NextAuthProvider as AuthProvider } from "./providers";
      ...
      <AuthProvider {...oidcConfig}>
      <App />
      </AuthProvider>

      At this point our entire application will be able to access all information and methods needed to perform authentication. View the providers.tsx file for additional information about how the SessionProvider is used. The SessionProvider enables use of Hooks to derive the authenticated state. View user.component.tsx for exactly how the code is authenticating your user. The sections rendering the "Log in" and "Log out" buttons are conditional areas based on the authenticated context. The buttons invoke functions provided by NextAuth.

      The logic using the hook to conditionally determine the Authenticated state, can be used to secure routes, components, and more.

    6. Open localhost:3000. You will see the Phase Two example landing page. You current state should be "Not authenticated". Click Log In. This will redirect you to your login page.

      info

      Use the non-admin user created in the previous section to sign in.

    7. Enter the credentials of the non-admin user you created. Click Submit. You will then be redirected to the application. The Phase Two example landing page now loads your "Authenticated" state, displaying your user's email and their Token.

    8. After your first log in, click Log out. Then click Log in again. Notice how this time you will not be redirected to sign in as your state is already in the browser. Neat! If you clear the browser state for that tab, then you will have to be redirected away to sign-in again.

    - + \ No newline at end of file diff --git a/docs/securing-applications/nuxt/index.html b/docs/securing-applications/nuxt/index.html index a4fa6517b..546f85ef4 100644 --- a/docs/securing-applications/nuxt/index.html +++ b/docs/securing-applications/nuxt/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Nuxt

    This example uses Nuxt3. There are a couple methods by which you can integrate Keycloak to your Nuxt application. We're going to explore two methods here, one uses keycloak-js and the other leverages oidc-client-ts. The keycloak-js library provides a simple, client-only method, but lacks some of the sophistication provided by the oidc-client library that is heavily supported and more widely used.

    Nuxt with keycloak-js

    info

    For this example, we need to disable "Client Authentication" in the OIDC client. This is available under Client > Settings > Capability config > Client authentication to OFF.

    1. Clone the Phase Two example repo.

    2. Open the Nuxt folder within /frameworks/nuxt and open the keycloak-js folder within /frameworks/nuxt/keycloak-js.

    3. Run npm install and then npm run dev. keycloak-js is a Javascript library that provides a fast way to secure an application.

    4. The project makes use of the following Nuxt items: components, composables, layouts, and plugins. We'll review each in kind.

    5. The main component that shows the User's authenticated state is in /components/User. In this component we call the useKeycloak composable, which let's us key into the keycloak-js functions that we've wrapped to make easily availble.

      const { keycloak, authState } = useKeycloak();

      function login() {
      keycloak.login();
      }

      function logout() {
      keycloak.logout();
      }

      Lower in the file the component leverages v-if checks to determine if the authState is authenticated or not. Depending on the state, a Log in or Log out button is available.

    6. Let's take a look at the setup for the composable next. Our composable is in /composables/keycloak-c. A composable is a function defined that can be called anywhere in the Nuxt application. It's a good way to abstract logic to be reused. In our case we use it to wrap a keycloak-js plugin (more on that in the next step) and help provided a state value for the authenticated state.

      export const useKeycloak = () => {
      const nuxtApp = useNuxtApp();
      const keycloak = nuxtApp.$keycloak as Keycloak;
      const authState = useState("authState", () => "unAuthenticated");

      keycloak.onAuthSuccess = () => (authState.value = "authenticated");
      keycloak.onAuthError = () => (authState.value = "error");

      return {
      keycloak,
      authState,
      };
      };
    7. In the plugin, /plugins/keycloak.client.ts we instantiate the keycloak-js library. We can then attach that instance to the NuxtApp instance. Substitute the correct values for your Keycloak instance that we created earlier in the tutorial.

      export default defineNuxtPlugin((nuxtApp) => {
      const initOptions: KeycloakConfig = {
      url: "https://euc1.auth.ac/auth/",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      };

      const keycloak = new Keycloak(initOptions);

      nuxtApp.$keycloak = keycloak;

      keycloak.init({
      onLoad: "check-sso",
      });
      });
    8. The logic for checking the authenticated state can be used to expand in ways to secure your site in a number of ways.

    Nuxt with oidc-client

    The oidc-client-ts package is a well-maintained and used library. It provides a lot of utilities for building out a fully production app.

    1. Clone the Phase Two example repo.

    2. Open the Nuxt folder within /frameworks/nuxt and open the /nuxt/oidc-client-ts folder.

    3. Run npm install and then npm run dev.

    4. The structure of the project is similar to the keycloak-js version but with a the use of services, stores, and middleware.

    5. We'll review where we configure out Keycloak instance. First open /services/keycloak-config.ts. In this file you will want to update it with the values for the Keycloak instance we set-up earlier in the tutorial. Make sure you are using the one with Client Authentication enabled. Update the clientSecret with the value. Use and environment variable here if you wish.

      export const keycloakConfig = {
      authorityUrl: "https://euc1.auth.ac",
      applicationUrl: "http://localhost:3000",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      clientSecret: "CLIENT_SECRET",
      };
    6. Switch over to the /services/auth-service now to see how the Oidc instance is started. The class pulls in values from the keycloakConfig to use in the constructor. The other functions are wrappers around methods provided by the oidc-client library. This allows us to key into things like signInRedirect and signoutRedirect.

      How the settings are integrated:

      const settings = {
      authority: `${keycloakConfig.authorityUrl}/auth/realms/${keycloakConfig.realm}`,
      client_id: keycloakConfig.clientId,
      client_secret: keycloakConfig.clientSecret,
      redirect_uri: `${window.location.origin}/auth`,
      silent_redirect_uri: `${window.location.origin}/silent-refresh`,
      post_logout_redirect_uri: `${window.location.origin}`,
      response_type: "code",
      userStore: new WebStorageStateStore(),
      loadUserInfo: true,
      };
      this.userManager = new UserManager(settings);

      Example function wrapper:

      public signInRedirect() {
      return this.userManager.signinRedirect();
      }
    7. With the AuthService defined, we can now expose that through a composable. Switch to the /composables/useServices file. The file is simple but provides a way for any component to hook into the service instance.

      import AuthService from "@/services/auth-service";
      import ApplicationService from "@/services/application-service";
      import { useAuth } from "@/stores/auth";

      export const useServices = () => {
      const authStore = useAuth();

      return {
      $auth: new AuthService(),
      $application: new ApplicationService(authStore.access_token),
      };
      };

      We pull in the AuthService then expose it through the $auth variable. The $application variable exposes the ApplicationService which is provided as an example of how you could secure API calls.

    8. We leverage the pinia library to make store User information to make it easily accessible. Open /stores/auth/index. From within this file, we can wrap the User object exposed by the oidc-client package. This can then be leveraged in the middleware function we want to define or to pull information quickly about the user.

    9. There are a few main pages in play here that we define to create paths the library can leverage. The /pages/auth, /pages/logout, /pages/silent-refresh create paths at the same name. These are used to do the redirection during authentication or log out. From within these we use the AuthService to direct the user around within the app. For instance in /auth:

      const authenticateOidc = async () => {
      try {
      await services.$auth.signInCallback();
      router.push("/");
      } catch (error) {
      console.error(error);
      }
      };

      await authenticateOidc();

      The router.push naively sends someone to the home page. This could be updated to go to any number of places, including the page one started the login flow from if you were to store that information to be retrieved.

    10. We have also created a middleware file in /middleware/auth.global to be used in a couple of ways. It checks if the user is authenticated and based on that knowledge, stores the user information in the store (if not there) or could be used to send someone to login. For our example, we created buttons to initiate that but there is a comment which shows how you could force a set of paths to require login.

      const authFlowRoutes = ["/auth", "/silent-refresh", "/logout"];

      export default defineNuxtRouteMiddleware(async (to, from) => {
      const authStore = useAuth();
      const services = useServices();
      const user = (await services.$auth.getUser()) as User;

      if (!user && !authFlowRoutes.includes(to.path)) {
      // use this to automatically force a sign in and redirect
      // services.$auth.signInRedirect();
      } else {
      authStore.setUpUserCredentials(user);
      }
      });
    11. Now that we have all the things setup, we can define the user component /components/User to easily pull information about the user's state and display the appropriate UI.

      const authStore = useAuth();
      const user = authStore.user;

      const signIn = () => services.$auth.signInRedirect();
      const signOut = () => services.$auth.logout();

      With this, the user object is now easily available. A simple v-if="user" allows the app to determine what UI to show.

    - + \ No newline at end of file diff --git a/docs/securing-applications/react/index.html b/docs/securing-applications/react/index.html index 2aa496f75..6b758dafd 100644 --- a/docs/securing-applications/react/index.html +++ b/docs/securing-applications/react/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    React

    Many SPAs use a framework such as React to simplify the creation of interactive experiences. We suggest the use of the open source react-keycloak library to make securing React applications easier.

    Example

    Follow the setup instructions in the library documentation https://www.npmjs.com/package/@react-keycloak/web

    Setup Keycloak instance

    Create a keycloak.js file in the src folder of your project (e.g. where App.js is located) with the following content:

    import Keycloak from "keycloak-js";

    // Pass initialization options as required or leave blank to load from 'keycloak.json'
    const keycloak = new Keycloak();

    export default keycloak;

    Further documentation on setup of the Keycloak instance is in the official documentation https://www.keycloak.org/docs/latest/securing_apps/#_javascript_adapter

    Setup ReactKeycloakProvider

    Once you have set up the Keycloak instance there, you can easily wrap components you wish to protect using the ReactKeycloakProvider.

    import { ReactKeycloakProvider } from "@react-keycloak/web";

    import keycloak from "./keycloak";

    // Wrap everything inside KeycloakProvider
    const App = () => {
    return (
    <ReactKeycloakProvider authClient={keycloak}>...</ReactKeycloakProvider>
    );
    };

    Using hooks

    When a component requires access to Keycloak, you can use the useKeycloak Hook.

    import { useKeycloak } from "@react-keycloak/web";

    export default () => {
    // Using Object destructuring
    const { keycloak, initialized } = useKeycloak();

    // Here you can access all of keycloak methods and variables.
    // See https://www.keycloak.org/docs/latest/securing_apps/index.html#javascript-adapter-reference

    return (
    <div>
    <div>{`User is ${
    !keycloak.authenticated ? "NOT " : ""
    }authenticated`}</div>

    {!!keycloak.authenticated && (
    <button type="button" onClick={() => keycloak.logout()}>
    Logout
    </button>
    )}
    </div>
    );
    };
    - + \ No newline at end of file diff --git a/docs/securing-applications/vue/index.html b/docs/securing-applications/vue/index.html index 432d1e854..2c51ab464 100644 --- a/docs/securing-applications/vue/index.html +++ b/docs/securing-applications/vue/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Vue.js

    info

    We will use the Phase Two Vue example code here, but the logic could easily be applied to any existing application.

    This example uses Vue.js. We're going to leverage oidc-client-ts to integrate OIDC authentication with the Vue app. The oidc-client-ts package is a well-maintained and used library. It provides a lot of utilities for building out a fully production app.

    1. Clone the Phase Two example repo.

    2. Open the Vue folder within /frameworks/vue and open the /nuxt/oidc-client-ts folder.

    3. Run npm install and then npm run dev.

    4. We'll review where we configure out Keycloak instance. First open /auth.ts. In this file you will want to update it with the values for the Keycloak instance we set-up earlier in the tutorial. Update the clientSecret with the value. Use and environment variable here if you wish.

      export const keycloakConfig = {
      authorityUrl: "https://euc1.auth.ac",
      applicationUrl: "http://localhost:3000",
      realm: "shared-deployment-001",
      clientId: "reg-example-1",
      clientSecret: "CLIENT_SECRET",
      };

      After the config, you can see how the OIDC instance is started.

      const settings = {
      authority: `${keycloakConfig.authorityUrl}/auth/realms/${keycloakConfig.realm}`,
      client_id: keycloakConfig.clientId,
      client_secret: keycloakConfig.clientSecret,
      redirect_uri: `${window.location.origin}/auth`,
      silent_redirect_uri: `${window.location.origin}/silent-refresh`,
      post_logout_redirect_uri: `${window.location.origin}`,
      response_type: "code",
      userStore: new WebStorageStateStore(),
      loadUserInfo: true,
      };
      this.userManager = new UserManager(settings);
    5. With the Keycloak instance defined, we attach this to the app instance for Vue. Switch to /main.ts

      import Auth from "@/auth";
      // ...
      app.config.globalProperties.$auth = Auth;

      We pull in the Auth instance then expose it through the $auth variable.

    6. There are a few main pages in play here that we define to create paths the library can leverage. The /view/auth and /view/silent-refresh create paths at the same name. These are used to do the redirection during authentication. From within these we use the Auth instance to direct the user around within the app. For instance in /views/AuthView:

      export default {
      name: "AuthAuthenticated",
      async mounted() {
      try {
      await this.$auth.signinCallback();
      this.$router.push("/");
      } catch (e) {
      console.error(e);
      }
      },
      };

      The router.push naively sends someone to the home page. This could be updated to go to any number of places, including the page one started the login flow from if you were to store that information to be retrieved.

    7. Now that we have all the things setup, we can define the user component /components/User to easily pull information about the user's state and display the appropriate UI.

      export default {
      name: "UserComponent",
      data() {
      return {
      user: null,
      signIn: () => this.$auth.signinRedirect(),
      logout: () => this.$auth.signoutRedirect(),
      };
      },
      async created() {
      const user = await this.$auth.getUser();
      if (user) {
      this.user = user;
      }
      },
      };

      With this, the user object is now easily available. A simple v-if="user" allows the app to determine what UI to show.

    - + \ No newline at end of file diff --git a/docs/self-service/dedicated-clusters/index.html b/docs/self-service/dedicated-clusters/index.html index cef8552e9..b57d22dac 100644 --- a/docs/self-service/dedicated-clusters/index.html +++ b/docs/self-service/dedicated-clusters/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Dedicated Clusters

    Dedicated clusters are available with paid plans. These Clusters use isolated compute, network and storage resources. Customers using dedicated clusters can create up to 20 Realms per cluster. If you need more, please contact your account representative or email support@phasetwo.io.

    Creating a Cluster

    Clusters can be created on the home page of the Dashboard by selecting the Create Cluster button. You must select a region, select an owning organization, and input a name to identify the cluster, and optionally provide a domain name you wish to use.

    Once you have input the required information, you will be sent to Stripe, our payment partner, to set up your billing account and payment method.

    Following successful billing setup, you will be returned to the Dashboard while your Cluster is provisioned. This is usually fast, but can take up to 24 hours in some cases. You will be notified by email and in the Dashboard when the Cluster is live.

    Regions

    Dedicated clusters are currently available in the following regions using the self-service dashboard:

    • AWS US West (Oregon) us-west-2
    • AWS Europe (Frankfurt) eu-central-1

    If you wish to launch a dedicated cluster in one of the following regions, we can provision it for you with an additional monthly fee. Please contact sales@phasetwo.io for more information.

    • AWS Asia-Pacific (Mumbai) ap-south-1
    • AWS Asia-Pacific (Singapore) ap-southeast-1
    • AWS Europe (Ireland) eu-west-1
    • AWS US East (N. Virginia) us-east-1
    • GCP Asia-Pacific (Jurong West) asia-southeast1
    • GCP Europe (St. Ghislain) europe-west1
    • GCP South America (São Paulo) southamerica-east1
    • GCP US Central (Iowa) us-central1
    • GCP US East (South Carolina) us-east1
    • GCP US West (California) us-west2

    Global clusters

    For use cases that require global proximity to users and region failover behavior, we are currently in beta for our global clusters. Global server load balancing provides geographic region affinity and failover to connect your users with the closest, available instances.

    These clusters are backed by CockroachDB multi-region clusters, which are hosted and operated by Cockroach Labs. There are two price tiers for global clusters, depending on your use of our shared CockroachDB clusters, or your own dedicated clusters.

    Please contact sales@phasetwo.io to talk to us about your global cluster use case.

    Custom domains

    Support for custom domains is avao;ab;e for all cloud providers. In order to set up a custom domain that you already own, specify the desired domain when creating the cluster. Following setup of your cluster, you will need to create 2 DNS records.

    For all cloud providers:

    • CNAME yourdomain.com TO {cluster_name}.{region}.{cloud_provider}.auth.ac

    For AWS:

    • CNAME (the record name and value will be provided to you in an email from Support).

    Your cluster will be available from your custom domain within 24 hours of correct configuration of the DNS records.

    Migrating from a shared deployment

    If you currently have a free, shared deployment, and you would like to migrate that deployment into your dedicated cluster, this can be done in a one-time operation. This is a batch operation, and will require 24 hours to process, so immediate migrations are not available. Please contact your customer success representative at support@phasetwo.io and indicate the deployment name you want to migrate, the target cluster, and the time you want to initiate the migration.

    Using a Cluster

    Clusters are used much as our shared deployments. You can create up to 20 Realms per cluster.

    Creating Deployments in your Cluster

    Once your Cluster has been provisioned, you can create Deployments as before, but you will open the create Deployment modal from the Cluster. Access to the admin console for those Realms is the same, using the Open Console link next to each Deployment.

    Billing

    Access to invoices and ability to change payment information can be accessed in the action menu next to the Cluster. This will take you to Stripe, our payment partner, to access your billing history and update your payment information. This is restricted to users with the appropriate organization roles.

    Deleting a Cluster

    Clusters that have reached the provisioing or active state cannot be immediately deleted. If you wish to delete your Cluster and end your subscription, you can schedule the deletion. Your cluster will be available until the end of your montly billing period. At that point, there will begin a 7-day grace period where the cluster will be available, and a 14-day grace period where the data will be preserved. Following that, the cluster will be de-provisioned and all data will be purged from our systems for security and compliance reasons.

    Refunds

    There are no refunds available for subscriptions paid on a monthly basis.

    If you have paid annually, and you have more than one month left in your subscription period, you will be refunded a pro-rated amount following the end of the 14-day grace period. This refund will come through your payment method registered with Stripe.

    SLA

    Please refer to our Service Level Agreement for more information.

    - + \ No newline at end of file diff --git a/docs/self-service/deployments/index.html b/docs/self-service/deployments/index.html index cf02e8ded..c5e8430b5 100644 --- a/docs/self-service/deployments/index.html +++ b/docs/self-service/deployments/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Deployments

    A Deployment is a Phase Two hosted instance of a Realm in a Phase Two enhanced Keycloak cluster. These clusters can be shared with other customers, or dedicated for customers in paid plans. Free, shared plans are allowed one Deployment per customer, and dedicated plans can create up to 20 Realms per cluster.

    Regions

    Shared clusters are currently operating in the following regions:

    • AWS US West (Oregon) us-west-2
    • AWS Europe (Frankfurt) eu-central-1

    Dedicated clusters can be launched in most AWS or GCP regions by request.

    Creating a Deployment

    Once you have logged in the self-service tool, click on the Deployments menu item and select the Create Deployment button. You must input a name, choose a region, and (optionally) select a cluster if you are creating a Deployment in a dedicated cluster.

    Opening the console

    Next to active Deployments in the list, you can select the Open Console link to create a login link and open it in another browser window.

    - + \ No newline at end of file diff --git a/docs/self-service/index.html b/docs/self-service/index.html index 4deade922..acc3b38ae 100644 --- a/docs/self-service/index.html +++ b/docs/self-service/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    - + \ No newline at end of file diff --git a/docs/self-service/your-organization/index.html b/docs/self-service/your-organization/index.html index 9f3f3ff0c..5b54fd123 100644 --- a/docs/self-service/your-organization/index.html +++ b/docs/self-service/your-organization/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Organization

    Organizations allow you to create a team to manage your Phase Two Deployments and Clusters. You can use the Organizations link in the Dashboard to view and manage your organization for teams you are a member.

    Members

    Select the Members link in the row next to your organization in order to manage and invite members. Members may be removed and edited in the list of members.

    Invitations

    New members may be invited by their email address by selecting the Invite new member button on the members list page.

    Roles

    Roles can be assigned at a high-level:

    • Developer. Can see all details but cannot edit. Can use Deployments owned by this organization.
    • Admin. Can see and edit all details. Can use Deployments owned by this organization.

    Or fine-grained, where the names of the roles explain their functionality.

    SSO

    If your organization wants to use its own identity provider to log into Phase Two self-service, you can set it up using our SSO wizards. Select the SSO Setup link in the row next to your organization and follow the steps for your Identity Provider vendor.

    Domains

    Phase Two self-service requires validated email domains in order to redirect authentication requests for your users. Create a domain using the Domains link in the row next to your organization. Once you have added a domain, you will be prompted to set up a DNS TXT record for the domain you have entered. The details for setting up that record can be found by selecting the Setup link in the row of the domain you added. Once you have added that record, return and select Verify. Because DNS records often take some time to propogate, please be patient, as verifiction may take up to 24 hours.

    Automatic membership

    Once you have configured SSO and verified an email domain, all users that authenticate using your identity provider will automatically be added to your organization.

    - + \ No newline at end of file diff --git a/docs/service-agreement/index.html b/docs/service-agreement/index.html index 775e084f4..a3264b95c 100644 --- a/docs/service-agreement/index.html +++ b/docs/service-agreement/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Phase Two SaaS Service Agreement

    Last updated: October 24, 2023

    This SaaS Service Agreement governs your use of the software-as-a-service solution identified below (the "Service"). "We" or "us" or "our" means Phase Two, Inc., a Washington corporation, with offices located at 140 Lakeside Avenue, Suite A49, Seattle, Washington 98122.

    WE ARE WILLING TO GRANT ACCESS TO THE SERVICE TO YOU AS THE PERSON OR THE LEGAL ENTITY THAT WILL BE UTILIZING THE SERVICE ON THE CONDITION THAT YOU ACCEPT ALL OF THIS AGREEMENT OF THIS AGREEMENT. BY ENTERING INTO THIS AGREEMENT ON BEHALF OF AN ENTITY OR ORGANIZATION, YOU REPRESENT THAT YOU HAVE THE LEGAL AUTHORITY TO BIND THAT ENTITY OR ORGANIZATION TO THIS AGREEMENT.

    PLEASE READ THIS AGREEMENT CAREFULLY BEFORE USING THE SERVICE. THIS AGREEMENT CONSTITUTES A LEGAL AND ENFORCEABLE CONTRACT BETWEEN US AND YOU. BY INDICATING CONSENT ELECTRONICALLY, OR ACCESSING OR OTHERWISE USING THE SERVICE, YOU AGREE TO ALL THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE TO THIS AGREEMENT, DO NOT INDICATE CONSENT ELECTRONICALLY AND MAKE NO FURTHER USE OF THE SERVICE.


    Description of service

    Platform Description: Phase Two Keycloak user authentication and SSO service

    Available Tiers of Service:

    • Starter: all features; up to 1,000 users and <10 identity provider connections: community support
    • Premium: all features; unlimited users and identity provider connections; custom domain; email and chat support
    • Enterprise: all premium features; multi-region deployment; custom themes and extensions; dedicated support; 99.99% uptime SLA

    You will select your Tier of Service upon deployment creation.

    Order form term

    One year from the date you first access the Service.

    Fees

    • Starter: FREE
    • Premium: $749 billed monthly OR $5,988 billed annually ($499 monthtly effective)
    • Enterprise: $2,499 billed monthly OR $23,988 billed annually ($1,999 monthtly effective)

    Payment terms

    We will bill you according to the Tier of Service you select. You shall pay upfront at the beginning of each period.

    This Agreement is subject to the additional terms and conditions specified on Exhibit A hereto, which is an essential part of this Agreement.


    Exhibit A: Terms and Conditions

    1. USE OF THE SERVICE

    1.1 Use of the Service. Subject to the terms and conditions of this Agreement, we grant you a limited, non-exclusive, non-transferable right during the term of this Agreement to use the Service solely in connection with your internal business operations. Your rights to use the Service are subject to any limitations on use of the Service based on the plan or version of the Service you register for (e.g., features, applicable usage limits) or that may be specified on the first page containing the order details (collectively, the “Scope Limitations”) and your rights to use the Service are contingent upon your compliance with the Scope Limitations and this Agreement. You are solely responsible for your conduct, for any data uploaded into the Service or otherwise provided for processing by the Service (collectively, “Your Data”), the content of Your Data and legality and means by which you acquired it, and all communications with others while using the Service. You acknowledge that we have no obligation to monitor any information on the Service, but we may remove or disable any information that you make publicly available on the Service at any time for any reason or for no reason at all. You, and not us, are responsible for the availability, accuracy, appropriateness, or legality of Your Data or any other information you may provide when using the Service.

    1.2 Acceptable Use. Except as otherwise explicitly provided in this Agreement or as may be expressly permitted by applicable law, you will not, and will not permit or authorize others to: (a) rent, lease, or, except as explicitly set forth in this Agreement, otherwise permit third parties to use the Service; (b) use the Service to provide services to third parties as a service bureau or in any way that violates applicable law; (c) circumvent or disable any security or other technological features or measures of the Service, or attempt to probe, scan or test the vulnerability of a network or system, or to breach security or authentication measures; (d) upload or provide for processing any information or material that is false, misleading, illegal, defamatory, offensive, abusive, obscene, or that violates privacy, intellectual property, or any other legal rights of any third party; (e) use the Service to harm, threaten, or harass another person or organization; (f) send, store, or distribute any viruses, worms, Trojan horses, or other disabling code or malware component harmful to a network or system; (g) use any robot, spider, site search/retrieval application, or other manual or automatic device or process to download, access, retrieve, index, "data mine", or in any way reproduce or circumvent, avoid, bypass, remove, or deactivate the navigational structure or technical measures or presentation of the Service or its contents; (h) attempt to probe, scan or test the vulnerability of the Service or any of our systems or network or breach any security or authentication measures; or (i) use, display, "frame" or "mirror" any part of the Service, our names, any of our trademarks, logos or other proprietary information, or the layout and design of any page or form contained on a page, without prior written authorization from us. You will not copy, reproduce, modify, translate, enhance, decompile, disassemble, reverse engineer, or create derivative works of the Service or its underlying software. You will neither alter nor remove any trademark, copyright notice, or other proprietary rights notice that may appear in any part of the Service and will include all such notices on any copies.

    1.3 Accounts. We may provision one or more accounts and access credentials to you. You may not share your account or access credentials with anyone else. You are responsible for maintaining the confidentiality of your login, password, and account and for all activities that occur under your login and account.

    1.4 Evaluation Licenses; Beta Versions. If you are licensing the Service for evaluation purposes, your use of the Service is only for the period limited by the evaluation period we specify to you. Notwithstanding any other provision in this Agreement, an evaluation license of the Service is provided “AS-IS” without indemnification, support or warranty of any kind, expressed or implied. From time to time, we may make available for you to try, at your sole discretion, certain functionality related to the Service, which is clearly designated as beta, pilot, limited release, non-production, or by a similar description (each, a “Beta Version”). Beta Versions are intended for evaluation purposes and not for production use, are not supported, and may be subject to additional terms. We may discontinue Beta Versions at any time in our sole discretion and may never make them generally available. We have no liability for any harm or damage arising out of or in connection with a Beta Version.

    1.5 Reservation of Rights. We retain all rights, title, and interest, including all intellectual property rights, in and to the Service, its underlying technologies, and all its related or included components and elements, including without limitation any modifications, updates, customizations, routines, apps, or other add-ons. Your rights to use the Service on are limited to those expressly set forth in this Agreement. We reserve all other rights in and to the Service and its underlying technologies and components.

    1.6 Technical Requirements. You will need certain equipment, software, and Internet access to be able to access the Service. Acquiring, installing, maintaining and operating equipment and Internet access is solely your responsibility. Phase Two neither represents nor warrants that the Service will be accessible through all web browser releases or operating systems.

    1.7 Service Availability. You are responsible for making available Your Data as necessary for us to provide the Service. We will attempt to provide the Service at all times, except for periods for maintenance and repair or in the case of emergencies or outages. The Service may be subject to unavailability for a variety of factors beyond our control including, without limitation, emergencies, third-party service failures, transmission, equipment or network problems or limitations, interference, signal strength, and may be interrupted, limited or curtailed. Delays or omissions may occur. We are not responsible for data, messages or pages lost, not delivered, delayed or misdirected because of interruptions or performance issues with the Service or communications services or networks. We may impose usage or Service limits, suspend the Service, or block certain kinds of usage in our sole discretion to protect users, other third parties, data, our systems, or the Service. The accuracy and timeliness of data received is not guaranteed.

    2. TERM; RIGHT TO RESTRICT OR TERMINATE ACCESS

    2.1 Term. The term applicable to your use of the Service will be as specified on the first page of this Agreement (the “Term”). The Term will automatically renew for consecutive periods of equal duration unless you or we notify the other of its desire to non-renew the Term at least 30 days prior to the end of the current Term.

    2.2 Termination. If you are using the Service free of charge, we may deny, suspend, terminate or restrict your access to all or part of the Service without notice in our reasonable discretion. If you have purchased a subscription to the Service, either you or we may terminate the Service (a) if the other party materially breaches this Agreement and fails to cure such breach within 30 days of written notice of such breach or (b) as may otherwise be permitted by this Agreement.

    2.3 Post-Termination Obligations. Following any expiration or termination, you shall immediately cease use of the Service and any license granted to you under any agreement related to your use of the Service shall immediately terminate. Termination of this Agreement does not affect your right to pay any amounts previously owed to us. Upon termination, we (a) will within 30 days refund pro-rata any monthly fees paid in advance and not used through the effective date of termination, and (b) may delete all of Your Data, and other information stored on our servers. Sections 1.5, 2.3, 5.2, 6, 8, 9, 10, and 11 will survive termination.

    3. CHANGE TO THE TERMS. We may add to, change or remove any part of this Agreement, at any time without prior notice to you other than listing of a later effective date than the one set forth at the top of this Agreement. Such modification shall be effective immediately upon posting a notification within the Service or by contacting you via email at the address you provided. As your next use of the Service may be governed by different Terms, we encourage you to look for a new effective date on this Agreement when you use the Service. It is your responsibility to check this Agreement periodically for changes. If we make any material changes to this Agreement, we will endeavor to provide all registered users with additional notice of any changes, such as at your e-mail address of record or when you log-in to your account. Your use or continued use of the Service following the posting or notice of any changes to this Agreement shall constitute your acceptance of the changed Terms.

    4. FEES. You will pay the Fees in the amount and at the time specified on the first page of this Agreement. If no payment date is otherwise specified, you will pay all invoices issued within 30 days from the invoice date. A late charge of the lesser of 1.5% per month or the maximum amount permitted by law will be added to past due accounts until paid in full. All reasonable costs and expenses, including but not limited to attorneys’ fees, court costs and service charges incurred by us in collecting payment will be paid by you. Credit terms are at our discretion and are subject to change. You are responsible for all taxes associated with your purchase except taxes on our income. You will pay only in United States currency and are not entitled to set off any Fees against any other amounts for any reason.

    5. DATA

    5.1 Data Transmission. We use commercially reasonable efforts designed to protect Your Data. You acknowledge that use of the Service involves transmission of Your Data and other communications over the Internet and other networks, and that such transmissions could potentially be accessed by unauthorized parties due to the inability to protect against all threats at all times arising from the use of the internet. You must protect your login name and password from access or use by unauthorized parties, and are solely responsible for any failure to do so. You must promptly notify us of any suspected security breach at security@phasetwo.io.

    5.2 Your Data. Your Data is your property. We use data in accordance with our privacy policy found at https://phasetwo.io/docs/privacy. You grant us a non-exclusive, worldwide, perpetual, royalty-free license to use, copy, transmit, sub-license, index, store, aggregate, and display Your Data as required to provide or perform the Service, account management, account support, and technical services, and to publish, display, modify, and distribute de-identified information derived from Your Data and from your use of the Service for any lawful purposes, including, without limitation, improving our products and services, developing new products and services, and developing, displaying, and distributing benchmarks, analysis and similar reports, provided that we do so in accordance with all applicable laws.

    5.3 Removal of Data. If Phase Two is required by any third-party rights holder to remove any data, content or information, or receives information that any data, content or information provided by or to you may violate applicable law or third-party rights, we may discontinue your access to such data, content or information through the Service, and/or may notify you that you must discontinue all use of such data, content or information, and to the extent not prohibited by law, you will do so and promptly remove such data, content or information from your systems. Phase Two may disable access to the applicable data, content or information or Service until the potential violation is resolved.

    6. CONFIDENTIALITY.

    6.1 Definitions. “Confidential Information” means any nonpublic information that a reasonable person would know should be kept confidential in light of the nature of its contents or circumstances of its disclosure. Our Confidential Information includes any usernames or passwords we issue to you or that you create, as well as the non-public aspects of the Service and any of our business, technical or financial information. “Disclosing Party” means the party disclosing Confidential Information hereunder, whether such disclosure is directly from Disclosing Party or through Disclosing Party’s employees or agents. “Recipient” means the party receiving any Confidential Information hereunder, whether such disclosure is received directly or through Recipient’s employees or agents. Confidential Information does not include information that: (a) is already known to the Recipient without restriction on use or disclosure prior to receipt of such information from the Disclosing Party; (b) is or becomes generally known by the public other than by breach of this Agreement by, or other wrongful act of, the Recipient; (c) is developed by the Recipient independently of, and without reference to, any Confidential Information of the Disclosing Party; or (d) is received by the Recipient from a third party who is not under any obligation to the Disclosing Party to maintain the confidentiality of such information.

    6.2 Requirement of Confidentiality. The Recipient agrees that it will use the same degree of care it uses to protect the confidentiality of its own confidential information of like kind (but not less than reasonable care) to: (a) not disclose or otherwise make available Confidential Information of the Disclosing Party to any third party without the prior written consent of the Disclosing Party, provided that the Recipient may disclose the Confidential Information of the Disclosing Party to its, and its affiliates’, officers, employees, consultants and legal advisors who have a “need to know,” who have been apprised of this restriction and who are themselves bound by nondisclosure obligations at least as restrictive as those set forth in this Section 6; and (b) use the Confidential Information of the Disclosing Party only for the purposes of performing its obligations or as otherwise authorized under this Agreement. The Recipient will promptly notify the Disclosing Party in the event it becomes aware of any loss or disclosure of any of the Confidential Information of Disclosing Party. The Recipient may disclose Confidential Information of the Disclosing Party to the extent compelled by law to do so, provided the Recipient gives the Disclosing Party prior notice of the compelled disclosure (to the extent legally permitted). The obligations in this Section 6 will survive termination and continue for a three year period thereafter, or if the information constitutes a trade secret, indefinitely.

    6.3 Feedback. The Service may permit you to or you may otherwise submit feedback, suggestions, enhancement requests, recommendations, and messages relating to the use and operation of the Service. You agree we may freely use and exploit the Feedback without any requirement of confidentiality, other restriction or any duty of accounting.

    7. WARRANTY; DISCLAIMERS

    7.1 Limited Service Warranty. We warrant that Service will substantially conform to the written functional and technical service specifications we make available from time to time under normal use and circumstances. Your exclusive remedy and our sole obligations for any breach of the foregoing warranty is to the repair or re-perform the Service so that it complies with the foregoing warranty or for you to terminate your use of the Service. We are not responsible for any misuse of the Service or for any issues that arise from any third party product or service.

    7.2 Warranty Disclaimer. EXCEPT AS SPECIFIED IN SECTION 7.1, YOUR USE OF THE SERVICE IS AT YOUR SOLE RISK. WE DO NOT MAKE ANY ADDITIONAL REPRESENTATION OR WARRANTY OF ANY KIND WHETHER EXPRESS, IMPLIED (EITHER IN FACT OR BY OPERATION OF LAW), OR STATUTORY, AS TO ANY MATTER WHATSOEVER. WE EXPRESSLY DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUALITY, ACCURACY, TITLE, AND NON-INFRINGEMENT. WE DO NOT WARRANT AGAINST INTERFERENCE WITH THE ENJOYMENT OF THE SERVICE. WE DO NOT WARRANT THAT THE SERVICE IS ERROR-FREE OR THAT OPERATION OR USE OF THE SERVICE WILL BE SECURE OR UNINTERRUPTED. WE EXERCISE NO CONTROL OVER AND EXPRESSLY DISCLAIM ANY LIABILITY ARISING OUT OF OR BASED UPON THE RESULTS OF USE OF THE SERVICE OR DOCUMENTATION.

    8. INDEMNIFICATION. You agree to defend, indemnify and hold us, our affiliate companies, and each of our respective directors, officers, employees, contractors, agents, successors and assigns harmless from any claim or demand, including reasonable attorneys’ fees, arising out of or relating to (i) any violation of this Agreement by you; (ii) Your Data or any other content or material you submit or otherwise transmit through our Service; (iii) your violation of any applicable laws or rights of another; (iv) your negligent or more culpable conduct; or (v) your use of the Service. We may, at our own expense, elect to assume the exclusive defense and control of any third party claim otherwise subject to defense by you. You may not settle or compromise any claim subject to this section without our prior written consent in our sole discretion.

    9. LIMITATIONS OF LIABILITY

    9.1 Disclaimer of Indirect Damages. UNDER NO CIRCUMSTANCES WILL WE, OUR AFFILIATES, EMPLOYEES, OFFICERS, AGENTS, REPRESENTATIVES, LICENSORS OR OTHER THIRD PARTY PARTNERS (“PHASE TWO PARTIES”) BE LIABLE TO YOU OR ANY OTHER PERSON FOR ANY INDIRECT, INCIDENTAL, PUNITIVE, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE, INABILITY TO USE, OR THE RESULTS OF USE OF OUR SERVICE, WHETHER BASED ON WARRANTY, CONTRACT, TORT (INCLUDING NEGLIGENCE), OR ANY OTHER LEGAL THEORY; INCLUDING WITHOUT LIMITATION DAMAGES RESULTING FROM PERSONAL INJURY, DEATH, LOST PROFITS, LOST DATA, LOSS OF BUSINESS OR BUSINESS INTERRUPTION, WHETHER DIRECT OR INDIRECT, ARISING OUT OF THE USE, INABILITY TO USE, OR THE RESULTS OF USE OF OUR SERVICE, WHETHER BASED ON WARRANTY, CONTRACT, TORT (INCLUDING NEGLIGENCE), OR ANY OTHER LEGAL THEORY. YOUR SOLE AND EXCLUSIVE REMEDY UNDER THIS AGREEMENT SHALL BE FOR YOU TO DISCONTINUE YOUR USE OF THE SERVICE.

    9.2 Cap on Liability. TO THE EXTENT PERMITTED BY LAW, UNDER NO CIRCUMSTANCES WILL ANY PHASE TWO PARTIES’ TOTAL LIABILITY OF ALL KINDS ARISING OUT OF OR RELATED TO THIS AGREEMENT (INCLUDING BUT NOT LIMITED TO WARRANTY CLAIMS), REGARDLESS OF THE FORUM AND REGARDLESS OF WHETHER ANY ACTION OR CLAIM IS BASED ON CONTRACT, TORT, OR OTHERWISE, EXCEED THE TOTAL AMOUNTS PAID BY YOU UNDER THIS AGREEMENT DURING THE SIX (6) MONTHS IMMEDIATELY PRECEDING THE DATE OF THE EVENT GIVING RISE TO THE CLAIM.

    9.3 Exception. Some states or jurisdictions may not allow the exclusion or the limitation of liability. In such states or jurisdictions, the MIME Parties’ liability to you shall be limited to the full extent permitted by law.

    9.4 Independent Allocations of Risk. Each provision of this Agreement that provides for a limitation of liability, disclaimer of warranties, or exclusion of damages is to allocate the risks of this Agreement between the parties. This allocation is reflected in the pricing offered by us to you and is an essential element of the basis of the bargain between the parties. Each of these provisions is severable and independent of all other provisions of this Agreement. The limitations in this section will apply notwithstanding the failure of essential purpose of any limited remedy in this Agreement.

    10. ARBITRATION AGREEMENT

    10.1 Mandatory Arbitration; Exceptions and Opt-Out. You agree that any dispute, claim or controversy arising out of or relating to this Agreement or the Service (collectively, “Disputes”) will be settled by binding arbitration, except that each party retains the right: (i) to bring an individual action in small claims court and (ii) to seek injunctive or other equitable relief in a court of competent jurisdiction to prevent the actual or threatened infringement, misappropriation or violation of a party’s copyrights, trademarks, trade secrets, patents or other intellectual property rights (the action described in the foregoing clause (ii), an “IP Protection Action”). You will also have the right to litigate any other Dispute if you provide us with written notice to opt out of arbitration (“Arbitration Opt-out Notice”) by email at support@getmime.com or by regular mail to 915 Broadway Street, Suite 112, Vancouver, Washington 98660 United States of America within thirty (30) days following the date you first accept this Agreement, or if you have not registered for an account, then within thirty (30) days following the date you first use our Service. If you don’t provide us with an Arbitration Opt-out Notice within the thirty (30) day period, you will be deemed to have knowingly and intentionally waived your right to litigate any Dispute except as expressly set forth in clauses (i) and (ii) above. The exclusive jurisdiction and venue of any IP Protection Action or, if you timely provide us with an Arbitration Opt-out Notice, will be the state and federal courts located in the Northern District of Georgia and each of the parties hereto waives any objection to jurisdiction and venue in such courts. Unless you timely provide us with an Arbitration Opt-out Notice, you acknowledge and agree that you are each waiving the right to a trial by jury or to participate as a plaintiff or class member in any purported class action or representative proceeding.

    10.2 No Class Actions. Further, unless we otherwise agree in a writing signed by an authorized representative, the arbitrator may not consolidate more than one person’s claims, and may not otherwise preside over any form of any class or representative proceeding. If a decision is issued stating that applicable law precludes enforcement of any limitations set forth in this agreement to arbitrate on the right to arbitrate claims on a class or representative basis, or as part of a consolidated proceeding, as to a given claim for relief, then that claim (and only that claim) must be severed from the arbitration and brought in the state or federal courts located in the Northern District of Georgia. All other claims will be arbitrated.

    10.3 Rules. The arbitration will be administered by the American Arbitration Association (“AAA”) in accordance with the Commercial Arbitration Rules and the Supplementary Procedures for Consumer Related Disputes (the “AAA Rules”) then in effect, except as modified by this “Arbitration Agreement” section. (The AAA Rules are available at https://www.adr.org/Rules or by calling the AAA at 1-800-778-7879.) The Federal Arbitration Act will govern the interpretation and enforcement of this Section.

    10.4 Arbitration Process. A party who desires to initiate arbitration must provide the other party with a written Demand for Arbitration as specified in the AAA Rules. AAA provides a general form for a Demand for Arbitration and may provide a separate form for Demand for Arbitration for residents of a particular state, such as California. The arbitrator will be either a retired judge or an attorney licensed to practice law with at least 15 years of experience and will be selected by the parties from the AAA’s roster of arbitrators. If the parties are unable to agree upon an arbitrator within seven (7) days of delivery of the Demand for Arbitration, then the AAA will appoint the arbitrator in accordance with the AAA Rules.

    10.5 Arbitration Location and Procedure. If you are a business, the arbitration will be conducted in Fulton County, Georgia. If you are a consumer, the arbitration will be conducted in the county where you reside. If your claim does not exceed $10,000, then the arbitration will be conducted solely on the basis of the documents that are submitted to the arbitrator, unless you request a hearing or the arbitrator determines that a hearing is necessary. If your claim exceeds $10,000, your right to a hearing will be determined by the AAA Rules. Subject to the AAA Rules, the arbitrator will have the discretion to direct a reasonable exchange of information by the parties, consistent with the expedited nature of the arbitration.

    10.6 Arbitrator’s Decision. The arbitrator will render an award within the time frame specified in the AAA Rules. The arbitrator’s decision will include the essential findings and conclusions upon which the arbitrator based the award. Judgment on the arbitration award may be entered in any court having jurisdiction thereof. The arbitrator’s award of damages must be consistent with the terms of the “Limitation of Liability” section above as to the types and amounts of damages for which a party may be held liable. The arbitrator may award declaratory or injunctive relief only in favor of the claimant and only to the extent necessary to provide relief warranted by the claimant’s individual claim. If you prevail in arbitration you will be entitled to an award of attorneys’ fees and expenses to the extent provided under applicable law. We will not seek, and hereby waive all rights we may have under applicable law to recover, attorneys’ fees and expenses if we prevail in arbitration.

    10.7 Fees. Your responsibility to pay any AAA filing, administrative and arbitrator fees will be solely as set forth in the AAA Rules. However, if your claim for damages does not exceed $75,000, we will pay all such fees unless the arbitrator finds that either the substance of your claim or the relief sought in your Demand for Arbitration was frivolous or was brought for an improper purpose (as measured by the standards set forth in Federal Rule of Civil Procedure 11(b)).

    10.8 Changes. Notwithstanding anything to the contrary in this Agreement, if we change this “Arbitration Agreement” section after the date you accepted this Agreement or access our Service, you may reject any such change by sending us written notice (including by email to support@phasetwo.io) within 30 days of the date such change became effective, as indicated in the “Effective Date” listed at the beginning of this Agreement or in the date of our email to you notifying you of such change. By rejecting any change, you are agreeing that you will arbitrate any Dispute between you and us in accordance with the provisions of this “Arbitration Agreement” section as of the date you accepted this Agreement, or accessed our Service.

    10.9 Survival. This “Arbitration Agreement” section will survive any expiration or termination of this Agreement.

    11. CONSENT TO ELECTRONIC COMMUNICATIONS

    11.1 Consent. You agree that we may send the following to you by email or by posting them on our website: legal disclosures; this Agreement; Privacy Policy; future changes to any of the foregoing; and other notices, policies, communications or disclosures and information related to the Service. You agree that we may contact you via email, phone, text, or mail regarding your subscription or the Service. You consent to receive such communications electronically. You agree to update your contact information to ensure accuracy. Your consent to conduct actions electronically covers all interactions between you and us.

    11.2 Updating your Consent. If you later decide that you do not want to receive certain future communications electronically, please send an email to support@phasetwo.io or a letter to 140 Lakeside Avenue, Suite A49, Seattle, Washington 98122 United States of America. You may also opt out of certain electronic communications through your account or by following the unsubscribe instructions in any communication you receive from us. Your withdrawal of consent will be effective within a reasonable time after we receive your withdrawal notice described above. We will need to send you certain communications electronically regarding the Service. You will not be able to opt out of those communications – e.g., communications regarding updates to the Terms or information about billing. Your withdrawal of consent will not affect the legal validity or enforceability of the Terms provided to and accepted by, you. If you withdraw your consent to receive communications electronically, certain portions of the Service may become unavailable to you.

    12. GENERAL PROVISIONS

    12.1 Access by Competitors. You may not access the Service if you are our direct competitor, except with our prior written consent. In addition, you may not access the Service for purposes of monitoring its availability, performance, or functionality, or for any other benchmarking or competitive purpose.

    12.2 U.S. Government Use. If the Service is licensed under a United States government contract, you acknowledge that the Service is a “commercial item” as defined in 48 CFR 2.101, consisting of “commercial computer software” and “commercial computer software documentation,” as such terms are defined in FAR Section 2.101 and Section 252.227-7014 of the Defense Federal Acquisition Regulation Supplement (48 CFR 252.227-7014) and used in 48 CFR 12.212 or 48 CFR 227.7202-1, as applicable. You also acknowledge that the Service is “commercial computer software” as defined in 48 CFR 252.227-7014(a)(1). United States government agencies and entities and others acquiring under a United States government contract will have only those rights, and will be subject to all restrictions, set forth in this Agreement.

    12.3 Relationship. We will be and act as an independent contractor (and not as the agent or representative of you) in the performance of this Agreement.

    12.4 Assignment and Delegation. You may not assign any of your rights or delegate any of your obligations under this Agreement (in whole or in part) without our prior written consent, except in connection with a change of control, merger, or by operation of law. Your assignment or delegation will not relieve you of your obligations under this Agreement nor release you of your liability under this Agreement. We may voluntarily, involuntarily, or by operation of law assign any of our rights or delegate any of our obligations under this Agreement without your consent. Any purported assignment or delegation in violation of this Subsection will be null and void. Subject to this Subsection, this Agreement will bind and inure to the benefit of each party’s respective permitted successors and permitted assigns.

    12.5 Notices. Any notice required or permitted to be given in accordance with this Agreement will be effective if it is in writing and sent by certified or registered mail, or overnight courier, return receipt requested, to the appropriate party at the address at the address provided by the other party and with the appropriate postage affixed. Either party may change its address for receipt of notice by notice to the other party in accordance with this Subsection. Notices are deemed given two business days following the date of mailing or one business day following delivery to a courier.

    12.6 Force Majeure. We will not be liable for, or be considered to be in breach of or default under this Agreement on account of, any delay or failure to perform as required by this Agreement as a result of any cause or condition beyond our reasonable control.

    12.7 Governing Law. This Agreement will be interpreted, construed, and enforced in all respects in accordance with the local laws of the State of Washington, without reference to its choice of law rules and not including the provisions of the 1980 U.N. Convention on Contracts for the International Sale of Goods. Subject to the venue requirements of the arbitration section, you consent to the exclusive jurisdiction of the state and federal courts in King County, Washington, USA.

    12.8 No Third-Party Beneficiaries. There are no third-party beneficiaries to this Agreement.

    12.9 Waiver and Modifications. Failure, neglect, or delay by a party to enforce the provisions of this Agreement or its rights or remedies at any time, will not be construed as a waiver of the party’s rights under this Agreement and will not in any way affect the validity of the whole or any part of this Agreement or prejudice the party’s right to take subsequent action. Exercise or enforcement by either party of any right or remedy under this Agreement will not preclude the enforcement by the party of any other right or remedy under this Agreement or that the party is entitled by law to enforce.

    12.10 Severability. If any part of this Agreement is found to be illegal, unenforceable, or invalid, the remaining portions of this Agreement will remain in full force and effect. If any material limitation or restriction on the use of the Service under this Agreement is found to be illegal, unenforceable, or invalid, your right to use the Service will immediately terminate.

    12.11 Headings. Headings are used in this Agreement for reference only and will not be considered when interpreting this Agreement.

    12.12 Entire Agreement. This Agreement contain the entire agreement of the parties with respect to the subject matter of this Agreement and supersede all previous communications, representations, understandings, and agreements, either oral or written, between the parties with respect to said subject matter. No usage of trade or other regular practice or method of dealing between the parties will be used to modify, interpret, supplement, or alter the terms of this Agreement.

    - + \ No newline at end of file diff --git a/docs/sla/index.html b/docs/sla/index.html index c7f0027fe..a1da3f58c 100644 --- a/docs/sla/index.html +++ b/docs/sla/index.html @@ -12,8 +12,8 @@ - - + + @@ -22,7 +22,7 @@ Phase Two does all that we can to keep your service up and running. However, there are a few things that we can’t be held accountable for. Thus, they are excluded from our SLA. The following events will be exempt from Phase Two's availability guarantee:

    • Scheduled interruptions or network/hardware maintenance.
    • Customer triggered redeployment (Keycloak version upgrade).
    • Load caused on the systems by client queries or usage (including, but not limited to, inaccurate installations and software configurations, abuse of the network, abuse of hardware or overuse of resources).
    • Failures or termination of the instance by the cloud providers used by Phase Two to implement the service.
    • Failures due to hostile or abusive actions by third parties such as denial-of-service attacks that violate the Agreement.
    • The system is not available due to technology failures on customer's side (network, hardware and software, third-party equipment).
    • Routing or other faults caused by intermediary or external networks.
    • Errors caused by factors outside of Phase Two’s reasonable control.
    • Features or Services designated Alpha or Beta or deprecated.
    - + \ No newline at end of file diff --git a/docs/sso/index.html b/docs/sso/index.html index e66c23c01..7b628a682 100644 --- a/docs/sso/index.html +++ b/docs/sso/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    SSO

    SSO enables authentication via an organization’s Identity Provider (IdP), such as Google Workspace or Okta instead of managing usernames and passwords. Phase Two implementations of IdP connections support SAML and OpenID Connect standard protocols.

    You can add and manage IdP connections through the Admin UI under the "Identity Providers" section. Documentation for the administration of IdPs can be found in the Keycloak server adminstration docs.

    If you have enabled the Organization customer portal, or are using the Phase Two Connect onboarding wizards, your customers can manage their IdP connections on their own.

    - + \ No newline at end of file diff --git a/docs/sso/magic-link/index.html b/docs/sso/magic-link/index.html index 2bd8653ce..9fa22e514 100644 --- a/docs/sso/magic-link/index.html +++ b/docs/sso/magic-link/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Magic Link Authentication

    Magic Link Authentication allows your users to log in with a special token encoded in an email. Phase Two supports two different mechanisms of creating this link.

    Authentication Flow

    An Authenticator that can run as a form in your login flow. This takes an email, and can optionally create a user if none exists. This implementation sends the email using a fixed template. Installation can be achieved by duplicating the Browser flow, and replacing the normal Username/Password/OTP forms with the Magic Link execution type.

    Resource

    Endpoint:

    POST https://app.phasetwo.io/auth/realms/<realm>/magic-link

    Parameters:

    NameRequiredDefaultDescription
    emailYEmail address associated with the User to create the magic link for.
    client_idYClient ID the user will be logging in to.
    redirect_uriYRedirect URI. Must be valid for the given client.
    expiration_secondsN86400 (1 day)Amount of time the magic link is valid.
    force_createNfalseCreate a user with this email address as username/email if none exists.
    send_emailNfalseSend the magic link email using the built in template.

    Sample response:

    {
    "user_id": "386edecf-3e43-41fd-886c-c674eea41034",
    "link": "https://app.phasetwo.io/auth/realms/test/login-actions/action-token?key=eyJhbG...KWuDyE&client_id=account-console",
    "sent": false
    }
    - + \ No newline at end of file diff --git a/docs/sso/setup/index.html b/docs/sso/setup/index.html index 4b08ebe1e..8fa054e35 100644 --- a/docs/sso/setup/index.html +++ b/docs/sso/setup/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Setup

    caution

    This section is currently under construction. Check back soon for updates.

    Once you have setup the authentication flow for SSO as described in the previous section SSO, you can create connections to the Organizations' identity providers and then associating them with the Organizations they represent.

    Configuring identity providers

    In order to manually setup an identity provider to be used for single sign-on for an organization, in the Admin UI, you can select the Identity providers section. Using the Add provider button, you can add SAML or OpenID Connect types. There is further Keycloak documentation describing all of the options in detail.

    It is also possible to use the identity provider setup wizards, described in the next section: Wizards.

    Associating organizations

    If you have not already created an Organization, check out the section on Organizations. Once you have created an organization, navigate to that organization's Identity provider tab and select the identity provider you created that you wish to associate with the organization.

    This will setup the identity provider to automatically associate Users who authenticate with that identity provider with the associated organization, and allow the identity provider to be discovered during the authentication process using the domain of the User's email address.

    - + \ No newline at end of file diff --git a/docs/sso/sso-without-auth/index.html b/docs/sso/sso-without-auth/index.html index bbf422270..c3b2a5ff6 100644 --- a/docs/sso/sso-without-auth/index.html +++ b/docs/sso/sso-without-auth/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    SSO Without Auth

    Many Phase Two customers use their own authentication and user management systems, and only rely on Phase Two for its comprehensive SSO support. It is not required to use both in order to get the full power of our SSO integrations and customer self-management portal.

    Authentication flow setup

    In order to set up Phase Two for SSO only, there are a few steps you must take.

    1. When you are setting up your customer IdP, make a note of the alias you use. It is required to make it unique, and it is also recommended to make it something that is memorable and easy to associate with the customer. For example, acme-corp-saml.
    2. In the Authentication section of the Admin UI, select the SSO Only flow, and then use the Bind flow function of the action menu in the upper right to bind this flow to the Browser flow.

    Add identity provider redirect to your application

    1. Import the keycloak-js Javascript library into your code, and initialze as documented in the Securing Applications section.
      var keycloak = new Keycloak();
    keycloak.init().then(function(authenticated) {
    console.log(authenticated ? 'authenticated' : 'not authenticated');
    }).catch(function() {
    console.log('failed to initialize');
    });
    1. When you want to send the user to their SSO provider, set the idpHint parameter to the alias you used in Step 1. Refer to the documentation on options to control other features. Note that if you do not specify an idpHint parameter in the options variable, the login flow will automatically fall back to using the email domain rules you have specified to help the user find their SSO IdP using their email address.
      // call the login function with the following options in order to initiate the SSO login flow
    var options = { prompt: "login", idpHint: "acme-corp-saml" };
    keycloak.login(options);
    1. Once the user has complete the SSO login flow, the keycloak-js library will handle processing the secure token flow with Phase Two, and will then redirect the user to the page where you initiated the login flow (or where you directed it to based on the redirectUri value in the options variable). In order to capture the information received from the user IdP, you must register the onAuthSuccess callback function. It is also good practice to register onAuthError to catch problems with the user SSO authentication process.
      keycloak.onAuthSuccess = function () {
    token = keycloak.tokenParsed;
    // do something with the result
    console.log("Email", token['email']);
    console.log("First Name", token['given_name']);
    console.log("Last Name", token['family_name']);
    };
    keycloak.onAuthError = function(errorData) {
    // do something with the error
    console.log("Error", errorData.error);
    console.log("Description", errorData.error_description);
    };

    Note that this is very similar to the normal login process when you are using Phase Two as the primary source of user authentication. However, the significant difference is that the keycloak-js adapter is not initialized in a way that requires login (thus making the page inaccessible), and it is your responsibility to store the association between your user and their IdP alias.

    - + \ No newline at end of file diff --git a/docs/sso/wizards/index.html b/docs/sso/wizards/index.html index 6327629e2..3ae0c6ba7 100644 --- a/docs/sso/wizards/index.html +++ b/docs/sso/wizards/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Wizards

    caution

    This section is currently under construction. Check back soon for updates.

    In order to facilitate easy setup of identity providers for single sign-on, it is possible for you to use the identity provider setup wizards that are used in the Admin Portal and the Phase Two Connect onboarding tool. This may be useful when you are meeting with a customer IT admin, in order to show them how to set up their identity provider, or to help familiarize you with interfaces for third-party identity providers.

    Launching the wizards as an administrator

    From the Admin UI, go to the Clients section of the Realm you are trying to add an identity provider. In the idp-wizard client, click on the link in the Home URL column. This will open a separate browser window and launch the wizard as your admin user.

    Once you have configured and completed a wizard, you can close the window. Back in the Admin UI, you can select the Identity providers section in order to check that it was setup properly and view the data.

    - + \ No newline at end of file diff --git a/docs/terms/index.html b/docs/terms/index.html index 210b035b1..d8b20d095 100644 --- a/docs/terms/index.html +++ b/docs/terms/index.html @@ -12,8 +12,8 @@ - - + + @@ -161,7 +161,7 @@ us:

    - + \ No newline at end of file diff --git a/docs/user-migration/api/index.html b/docs/user-migration/api/index.html index 1d2799ca5..346e204f9 100644 --- a/docs/user-migration/api/index.html +++ b/docs/user-migration/api/index.html @@ -12,8 +12,8 @@ - - + + @@ -27,7 +27,7 @@ migrated anyway or simply ignored.

    Group role conversion

    If group names do not perfectly match those in the legacy system, you can configure the provider to automatically map legacy groups to new groups, by specifying the mapping in the format legacyGroup:newGroup.

    Migrate unmapped groups

    This switch can be toggled to decide whether groups which are not defined in the legacy group conversion map should be migrated anyway or simply ignored.

    - + \ No newline at end of file diff --git a/docs/user-migration/index.html b/docs/user-migration/index.html index f9774dc3a..266ff39ed 100644 --- a/docs/user-migration/index.html +++ b/docs/user-migration/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    User Migration

    Customers who have legacy user databases they wish to import can use this feature as part of initial or ongoing migration to Phase Two. We use a variant of this Keycloak user migration extension.

    In order to migrate your users with this extension, you must implement a web service so that we can securely validate the users and receive their information. The API to be implemented is documented in the next section User Migration API. There is an example implementation in Java on Github legacy-system-example.

    You can add a legacy migration in the User federation section of the Admin UI. Select the User migration using a REST client provider and configure your endpoints.

    - + \ No newline at end of file diff --git a/index.html b/index.html index 690ea44f3..5a376beda 100644 --- a/index.html +++ b/index.html @@ -12,16 +12,16 @@ - - + +
    -
    Gradient Background

    Future-Proof Your App

    Accelerate SaaS time-to-market and enterprise adoption by rapidly integrating the features you need.

    Open Source Logo

    We are open source

    Pictogram showing fixed US dollar sign

    Fixed pricing for peace of mind

    Pictogran showing cloud and on-prem servers

    Cloud or on-prem deployment

    Integrates with

    Integration Lines
    Okta Logo
    Auth0 Logo
    Azure Logo
    Google Workspace Logo
    Active Directory Logo
    JumpCloud Logo
    Onelogin Logo
    Ping Identity Logo
    Duo Security Logo

    + many more

    Color Gradient

    No-code Enterprise SSO

    Leap up market into enterprise adoption with seamless SSO support.

    SSO Login Examples
    • Pictogram showing 5 minutes on a hour
      5-minute integration

      One integration adds all enterprise identity providers. With or without adopting our identity feature, you can support all popular identity providers.

    • Pictogram showing puzzle pieces
      Integrate Once

      SAML, OIDC, OAuth2? Support all the standards without years of development and debugging.

    • Pictogram showing US dollar sign
      No variable cost

      We're not a parasite on your business model. Unlimited SSO connections for a single price.

    Admin Portal

    Seamless onboarding and self-management for your customer administrators and users. Empower your users and customers to easily manage every aspect of identity, organization and SSO. Drastically reduce customer support.

    Screenshots showing management of users, domains and SSO

    By Developers, For Developers

    Create delightful, seamless experiences for your customers. In just a few minutes!

    • Pictogram showing a code
      Simple Integration

      Our goal is to make it as easy as possible for developers to integrate with our system so they can add SSO and other features quickly and then move on to what's important—their app!

    • Pictogram showing documents
      Full Documentation

      We are building great documentation, tutorials and modern SDKs, so implementation is easy regardless of skill level or technology stack.

    • Pictogram showing a key inside the shield
      Secure and Standardized

      Standards compliance and security are our strengths so you can focus on your your customers.

    Protect a page
    var auth = new Keycloak({
    url: 'https://{host}/auth',
    realm: '{realm}',
    clientId: '{clientId}'
    });
    auth.init({
    onLoad: 'login-required'
    }).then(function(authenticated) {
    alert(authenticated ? 'authenticated' :
    'not authenticated');
    }).catch(function() {
    alert('failed to initialize');
    });
    Concentric Circles

    Phase Two Heart symbols Keycloak

    Phase Two is based on the Keycloak Open Source Identity and Access Management system, built and maintained by Red Hat.

    Diagram showing how Keycloak works with Phase Two
    Pictogram showing Open Source logo
    Always Open Source

    Phase Two is built as a collection of open source Keycloak extensions. While we endeavor to make Keycloak simple to use, operate and scale, in the cloud or on prem.

    Pictogram showing a fortress
    Battle-tested and hardened

    Keycloak has been battle-tested and hardened for over 7 years. Its security and reliability is depended on by organizations from small startups to governments and Fortune 500 companies.

    Pictogram showing a group of poeple interconnected
    Community Superpower

    We believe that community participation in building our software is a superpower, and can't wait to see what you will help us build.

    Color Gradient

    Phase Two is One Price Per Project

    No hidden fees, no unpredictable costs.

    Starter plan

    Starter

    Always FREE 1

    • CheckmarkShared cluster
    • Checkmark<1,000 users
    • Checkmark<10 SSO connections
    • CheckmarkCommunity support
    • No SLA
    Most Popular
    Premium plan

    Premium

    from $499/mo 2

    • CheckmarkDedicated cluster
    • CheckmarkUnlimited users
    • CheckmarkUnlimited SSO connections
    • CheckmarkCustom domain
    • CheckmarkEmail support
    • Checkmark99.9% uptime guarantee
    Enterprise plan

    Enterprise

    from $1999/mo 2

    • CheckmarkAll Premium features
    • CheckmarkGlobal deployment
    • CheckmarkCustom themes & extensions 3
    • CheckmarkDedicated support
    • Checkmark99.99% uptime guarantee

    (1) Subject to availabilty (2) When paid annually (3) Additional fees based on extension complexity

    For on-prem support and bundling options, please contact sales.

    Phase Two are Keycloak Experts

    Configuring, integrating and operating an Identity and Access Management system can be daunting.

    For both hosted, on-prem customers, or those with their own Keycloak deployment, our goal is to create an understanding in your organization of what is possible with Keycloak. We want to support your goals as you adopt and implement Keycloak in your products. Let us lend our expertise to every step of your journey.

    Enterprise plan

    Enterprise Support Packages

    from $3500/mo

    SilverGold
    Architecture reviewCheckmarkCheckmark
    Installation and configuration supportCheckmarkCheckmark
    Email supportCheckmarkCheckmark
    Slack supportCheckmark
    Phone supportCheckmark
    Support Hours (US EST)9x524x7x365
    Response time (hours)244
    Health assessmentQuarterlyMonthly
    Incl. service hours (/mth)1020
    - + \ No newline at end of file diff --git a/product/identity/index.html b/product/identity/index.html index a456ebae8..59593a64f 100644 --- a/product/identity/index.html +++ b/product/identity/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Frictionless Authentication

    Authenticate your users securely across every application, from the first click to the last.

    Concentric Circles
    Social Login Symbols

    Social Login

    Add social login with popular providers like Google, Github and Facebook to remove barriers to engagement, and allow your users to maintain one identity.

    Illustration showing Google and Microsoft social login buttons
    Color Gradient

    Multi-Factor Authentication

    Add an additional layer of security by allowing users to add second factors using advanced methods such as TOTP authenticator apps or WebAuthn devices like Yubikey or Passkeys.

    Illustration showing various Multi-Factor Authentiation Means

    Magic Links

    Removes friction to signup and identity using your user’s email, all without compromising account security. Immediately avoid password breaches, and remove the need for forgotten password problems.

    Illustration showing logging with magic link

    User Account Management

    Empower your users with a user account portal that allows them to self-manage their account details and identity. Massively reduces the most common customer service issues.

    Illustration showing managing users
    - + \ No newline at end of file diff --git a/product/onprem/index.html b/product/onprem/index.html index 5c52d4a02..99505dac1 100644 --- a/product/onprem/index.html +++ b/product/onprem/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Concentric Circles

    Bundle Identity and SSO When You Deploy On-Prem

    Sophisticated SaaS providers are realizing the value in bundling and deploying their solutions to customers in governments and regulated industries that can’t use cloud offerings for regulatory or compliance reasons.

    Data Residency? Wherever You Are

    With increasingly complex global data laws and regulations, having complete control over where and how your Users’ data is stored is necessary. For such use cases, the complete Phase Two product suite can be installed and supported on the customer cloud.

    Illustration showing a world map in dots with some dots highlighted in different color

    On-Prem IdP onboarding

    Onboarding an enterprise customer installing your software on site can be daunting. Phase Two Connect enables self onboarding by the customer’s IT Administrator.

    Diagram showing accessing customer app via IdP and PhaseTwo Connect
    Color Gradient

    Kubernetes and Replicated.com Compatible

    We provide Docker images and Helm charts to quickly integrate SSO and identity management into your application bundle. It will work on-prem, in private cloud, and even air gap environments.

    Illustration showing Kubernetes and Replicated.com logos
    - + \ No newline at end of file diff --git a/product/organizations/index.html b/product/organizations/index.html index 3b166b59b..0bd8715d1 100644 --- a/product/organizations/index.html +++ b/product/organizations/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Simple Business Customer Identity

    Organizations is a SaaS identity solution that delivers self-management for your customers and APIs for your developers, so that you can start to build workflows for your customers at scale.

    Programmatic toolset of APIs and SDKs for app development teams to manage and customize workflows for their end customers at scale.

    Concentric Circles
    Social Login Symbols

    Enterprise SSO Login for Each Customer

    Eliminate the barriers to onboarding and engagement by adopting.

    Illustration showing Google and Microsoft social login buttons
    Color Gradient

    Streamlined Invitations

    Empower users and organization administrators to invite users.

    Illustration showing inviting users

    Roles, Permissions And authorization per Customer

    Build your own custom roles and permissions for each organization. Build authorization logic that includes the organization as a first-class entity.

    Illustration showing three users with different roles and permissions
    - + \ No newline at end of file diff --git a/product/sso/index.html b/product/sso/index.html index 755e0ef56..728ad4786 100644 --- a/product/sso/index.html +++ b/product/sso/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@
    Concentric Circles
    Authentication symbols in different colors

    One Simple Integration to Support Many Identity Providers

    Support SSO with the most popular identity providers with just a few lines of code. 5 minutes to enable a universal login flow for a secure and consistent experience.

    Connect Your Customer’s Users to Your App

    Streamlined onboarding of your customer’s users leads to increased engagement, lower friction, and better retention.

    Diagram showing connecting user's apps
    Color Gradient

    Integrations Galore

    We support dozens of popular identity providers, and provide complete SAML and OpenID Connect implementations for most of the rest.

    Okta Logo
    Auth0 Logo
    Azure Logo
    Google Workspace Logo
    Active Directory Logo
    JumpCloud Logo
    Onelogin Logo
    Ping Identity Logo
    Duo Security Logo

    + many more

    - + \ No newline at end of file diff --git a/search/index.html b/search/index.html index 1cdee9e2c..740289dac 100644 --- a/search/index.html +++ b/search/index.html @@ -12,8 +12,8 @@ - - + + @@ -21,7 +21,7 @@

    Search the documentation

    - + \ No newline at end of file