From a350ae8452a627a326384df97a67ca8eb86b816d Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Tue, 7 Oct 2025 21:37:17 -0700 Subject: [PATCH 01/32] chore: migration guide updates --- docs/migrate-to-ory/migrate/create-project | 18 ++ docs/migrate-to-ory/migrate/define-id-schema | 21 +++ docs/migrate-to-ory/migrate/go-live | 27 +++ docs/migrate-to-ory/{ => migrate}/index.mdx | 170 +++++++++++++----- docs/migrate-to-ory/migrate/integrate-backend | 30 ++++ .../migrate-to-ory/migrate/integrate-frontend | 33 ++++ .../migrate-to-ory/migrate/migrate-strategies | 106 +++++++++++ docs/migrate-to-ory/migrate/user-identities | 40 +++++ src/sidebar.ts | 16 +- 9 files changed, 419 insertions(+), 42 deletions(-) create mode 100644 docs/migrate-to-ory/migrate/create-project create mode 100644 docs/migrate-to-ory/migrate/define-id-schema create mode 100644 docs/migrate-to-ory/migrate/go-live rename docs/migrate-to-ory/{ => migrate}/index.mdx (68%) create mode 100644 docs/migrate-to-ory/migrate/integrate-backend create mode 100644 docs/migrate-to-ory/migrate/integrate-frontend create mode 100644 docs/migrate-to-ory/migrate/migrate-strategies create mode 100644 docs/migrate-to-ory/migrate/user-identities diff --git a/docs/migrate-to-ory/migrate/create-project b/docs/migrate-to-ory/migrate/create-project new file mode 100644 index 000000000..a0643ee97 --- /dev/null +++ b/docs/migrate-to-ory/migrate/create-project @@ -0,0 +1,18 @@ +--- +id: create-project +title: Create project +sidebar_label: Create project +sidebar_position: 3 +--- + +## Create Ory Network projects + +Now that you have chosen your migration strategy, you can begin the actual migration process by setting up your Ory Network +projects. This involves creating a new project environment where the migration will take place. + +You can create a new Ory Network project using the Ory CLI. The command ory create project allows you to specify the environment +of the project, the output format, the name of the project, and the workspace to use. More details about creating a project can be +found [here](../cli/ory-create-project). + +Before migrating your production environment, perform the migration in a development or staging environment. This allows you to +test and refine the process without affecting your live data or users. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/define-id-schema b/docs/migrate-to-ory/migrate/define-id-schema new file mode 100644 index 000000000..3e92a070b --- /dev/null +++ b/docs/migrate-to-ory/migrate/define-id-schema @@ -0,0 +1,21 @@ +--- +id: define-id-schema +title: Define identity schema +sidebar_label: Define identity schema +sidebar_position: 3 +--- + + +## Define identity schema + +To match identities from your current system with the new Ory system, you can customize the identity schema in Ory to meet your +specific requirements. This schema defines the types of data that the system can store for users, including names, email +addresses, phone numbers, and other authentication-related information. Additionally, you can specify extra metadata fields to be +included in user profiles. Ory offers default presets to assist you in creating and managing identity schemas. More details about +identity schemas can be found [here](../kratos/manage-identities/identity-schema). + +- Do store profile data in your identity that is used across your system. This includes the usernames, email addresses, phone + numbers, first names, and last names. +- Do not store business logic in your identity, store this information in other systems, such as your application database. This + includes for example credit card information, shipping addresses, shopping cart items, or user preferences. See also the + following section on backend integration. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/go-live b/docs/migrate-to-ory/migrate/go-live new file mode 100644 index 000000000..8ccc24868 --- /dev/null +++ b/docs/migrate-to-ory/migrate/go-live @@ -0,0 +1,27 @@ +--- +id: go-live +title: Go live +sidebar_label: Go live +sidebar_position: 2 +--- + + +# Go live + +After successfully migrating your data and testing the integration in your development or staging environment, it's time to go +live with Ory in your production environment. + +1. Final testing: Before switching over to Ory in production, conduct thorough testing of all user authentication flows, identity + management features, and access controls. This includes testing edge cases, error handling, and load testing to ensure the + system can handle your user base. +1. Prepare a rollback plan: In case any issues arise during the go-live process, have a rollback plan in place. This may involve + reverting to your previous authentication system or restoring data from backups. +1. Schedule the go-live: Choose a time for the go-live that minimizes the impact on your users, such as during low-traffic + periods. Communicate the planned migration to your users in advance, including any expected downtime or changes they should be + aware of. +1. Monitor the transition: As you switch over to Ory, closely monitor the system for any issues, such as failed authentications, + performance bottlenecks, or user complaints. Use live events to monitor the system under + . +1. Optimize and refine: After the go-live, continue to monitor the system and gather user feedback. + +Once your Ory integration is stable and users are successfully authenticating with the new system, your migration is complete. \ No newline at end of file diff --git a/docs/migrate-to-ory/index.mdx b/docs/migrate-to-ory/migrate/index.mdx similarity index 68% rename from docs/migrate-to-ory/index.mdx rename to docs/migrate-to-ory/migrate/index.mdx index ff0e8e77d..3ff5bce2d 100644 --- a/docs/migrate-to-ory/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -1,56 +1,144 @@ --- id: index -title: Migrate to Ory tutorial -sidebar_label: Migrate to Ory +title: Migrate to Ory Network +sidebar_label: Migrate to Ory Network sidebar_position: 1 --- -This tutorial will guide you through the process of migrating your existing authentication system to Ory Identities, creating Ory -Network projects, setting up identity schemas, and customizing the user interface to match your current interface. +# Migrate to Ory Network -:::note +Before you can migrate smoothly, you need a complete picture of how your identity management system works today. This step ensures +nothing gets missed — from everyday login flows to rare edge cases — and sets the foundation for mapping existing features to +Ory Network's equivalent features. -This guide is an initial reference and may not cover all aspects of your use case. If you have any questions or need more help, -don't hesitate to get in [touch with Ory Support for help](https://www.ory.sh/contact). +Why it matters: Not understanding your existing system’s behavior is the #1 cause of unexpected regressions during cutover. With +Ory Network, you gain full control, and with it, the ability to shape your system’s flows exactly to your needs. -::: +# Understand your current identity management system -## Pre-migration considerations +Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific needs and +understand the unique requirements of each approach. Each scenario differs in complexity and implementation needs. Use these IAM +scenarios to help identify and map the authentication flows for your application. -Before starting the actual migration you should choose a migration strategy. +## Identify your IAM scenario +- CIAM +- B2B +- Workforce +- Agentic AI -### Migration scenario +### CIAM (Customer Identity and Access Management) -- **Scenario 1** - Migrating from a self-rolled solution +Your company sells products or services directly to individual consumers. - If you're coming from a custom-built authentication solution, consider using Ory Identities. This approach simplifies the - migration by focusing solely on identity management without introducing the complexities of OAuth2. If you plan to move towards - OAuth2 eventually, you can plan for that transition once the initial migration is complete. To get started, refer to the - "Integrate Frontend & Backend" sections later in this guide. +#### Key IAM requirements +- Self-service registration, login, and profile management for end users +- Social login, multi-factor passwordless options, and robust account recovery +- Privacy compliance (GDPR, CCPA) +- High-scale performance for millions of users -:::info +#### Ory Network features +- Identity management & authenitication (based on Ory Kratos) +- Authorization (based on Ory Hydra, optional for OAuth2/OIDC) -If you are not sure whether or not you need OAuth2 for your use case, please read the -[When to use OAuth2](../hydra/concepts/before-oauth2) documentation. +### B2B (Business-to-Business) -::: +Your company sells products or services directly to other businesses rather than individual consumers. Your customers are organizations +that use these products or services to run their own operations. -- **Scenario 2** - Migrating from Auth0 or another OAuth2-based solution +#### Key IAM requirements - If you're migrating from Auth0 or any other OAuth2-based login solution, you'll want to use Ory OAuth2 together with Ory - Identities. This setup allows you to keep your existing OAuth2-based flow while integrating it with Ory's identity management. - This approach is ideal if you want to keep your current implementation without making significant changes. To get started, refer - to the [Ory OAuth2 quickstart guide](../getting-started/oauth2-openid/add-oauth2-openid-connect-nodejs-expressjs) which provides - instructions on setting up an OAuth2/OIDC flow with Ory. +- Multi-organization user management +- SSO with SAML/OIDC providers +- Self-service partner onboarding +- Role-based permissions and API controls -- **Scenario 3** - Other +#### Ory Network features - If your authentication system doesn't fall into the categories described above - such as hybrid systems, legacy protocols, or - any other unique solution - you may need a different approach to migration. For such unique or complex migrations, we recommend - [consulting with Ory](https://www.ory.sh/contact/) to get a recommendation. The Ory team can help you design a custom migration - plan that aligns with your specific requirements. +- Identity management & authenitication (based on Ory Kratos) +- Authorization and single sign-on (based on Ory Hydra/Ory Polis) +- Permissions (based on Ory Ketos) +- Zero-trust access proxy (based on Ory Oathkeeper) -### Migration strategy +### Workforce (Business-to-Enterprise) + +Your company provides products or services. Your company wants to manage access for a single organization's extended workforce. You want to +consolidate employee user accounts and identities across multi-tenant brands, applications and systems. You need to seamlessly connect with +existing enterprise identity providers and other 3rd party systems and streamline user onboarding, offboarding, and permission management. + +#### Key IAM requirements +- Streamline onboarding/offboarding of employee, contractor, and temporary workers +- Role-based access aligned with organizational hierarchy +- HR system integration with flexible identity schemas +- Integrate with enterprise identity providers and third-party systems +- Time-bound permissions and role assignments +- Zero-trust security, MFA, and SSO for enterprise applications + +#### Ory Network features +- Identity management & authenitication (based on Ory Kratos) +- Authorization and single sign-on (based on Ory Hydra/Ory Polis) +- Permissions (based on Ory Ketos) +- Zero-trust access proxy (based on Ory Oathkeeper) +- Webhooks + +### Agentic AI + +Your company wants to enable AI applications to securely connect to data sources and tools. For example, servers that host resources and +clients (AI applications) to discover and use those resources. + +#### Key IAM requirements +- Standardized protocol that works across many tools and data sources +- Built-in authentication and access control + +#### Ory Network features +- Identity management & authenitication (based on Ory Kratos) +- Authorization (based on Ory Hydra) +- Permissions (based on Ory Ketos) +- Zero-trust access proxy (based on Ory Oathkeeper) + +## Map all authentication and identity flows in your application + +Build a complete picture of every authentication-related process in your system. Use your IAM scenario’s Key IAM requirements to help identify +authentication and identity flows. This ensures you don’t miss critical flows during migration. + +1. Identify all entry points where authentication occurs (e.g., web app login, mobile app sign-in, API tokens, social or enterprise sign-ins). +1. Create a comprehensive inventory of flows, for example: + - Registration / Sign-up + - Sign-in/Sign-out + - Multi-Factor Authentication (MFA) + - Password reset and account recovery + - Account linking (social, enterprise logins) + - User profile management + - Token refresh and session handling + - Recovery flows, consent screens, or partner-specific integrations +1. Visualize flows with diagrams (sequence diagrams or flow charts) to surface dependencies and hidden complexity. +1. Note where authentication interacts with other systems (databases, CRMs, partner apps, or external APIs). + + +### Map your existing IAM functionality to Ory Network’s equivalent capabilities +Using your list of authentication and identity flows, create a side-by-side table to map out existing functionality and Ory Network’s equivalent +capabilities. While the majority of the time you'll find your existing functionality neatly maps to Ory's capabilities, now is the best time to +identify when it does not. Some examples: + +- Not all vendors strickly comply with standards, whereas Ory does, so you might discover you need to +change how you implement functionality to be compliant. +- You might have a unique use case to solve that requires additional help from our support. + +Table 1: An example of mapping functionality + +| Existing Functionality | Ory Capabilities | +|---|---| +| JSON Web Tokens | [Ory Session cookies/tokens](https://www.ory.sh/docs/identities/session-to-jwt-cors)| + +At the end of this process you should have a living document with diagrams that capture: + +- All authentication and identity flows +- Your system's existing functionality and Ory Network's equivalent capabilites +- Any existing dependencies on external systems +- Edge cases requiring special handling + +This will serve as your blueprint for planning, designing, testing, and validating your migration. + +## Migration strategy When migrating user data from an old system to a new one, the process involves two main steps: transferring existing data and "go-live" when users start authenticating with the new system. The choice of migration strategy depends on your specific use case, @@ -63,18 +151,18 @@ the shape of existing data, and the number of "go-lives" you need to manage. Each migration strategy has its strengths and challenges. The ideal choice depends on factors such as the complexity of your system, the number of users, and your organization's tolerance for risk and downtime. -#### Big bang migration +### Big bang migration In a big bang migration, also known as "offline migration", all user data is transferred at once, and a single "go-live" event is scheduled, where all users start using the new system simultaneously. -##### Advantages of big bang migration +#### Advantages of big bang migration - Simplicity: Since there is only one "go-live", the migration process is easier to manage and plan. - Time efficiency: The migration process happens in one go, reducing the time needed for the transition. - Less complexity: There is no need to run two systems in parallel and you can retire the previous solution immediately. -##### Drawbacks of big bang migration +#### Drawbacks of big bang migration - High risk: If any issues occur during the cutover, the impact can be significant, affecting all users. - Downtime: This approach may require planned system downtime to ensure data consistency, which can disrupt users. @@ -87,20 +175,20 @@ A big bang migration is often not the best choice due to the risk and downtime, - downtime isn't a problem - you need to retire the current solution yesterday -#### Stepwise migration +### Stepwise migration Stepwise migration, also called "application-based migration", involves transferring user data in phases, focusing on specific applications, services, or user segments at a time. This approach results in multiple "go-lives", each affecting a defined group of users. -##### Advantages of stepwise migration +#### Advantages of stepwise migration - Reduced risk: By migrating in phases, issues are isolated to specific apps or user segments. - Flexibility: Allows for adjustments and optimizations between phases based on lessons learned. - Minimized/no downtime: Since the migration occurs in stages, downtime can be limited to smaller user groups or avoided completely. -##### Drawbacks of stepwise migration +#### Drawbacks of stepwise migration - Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. - Extended timeline: The migration process takes longer as it is broken down into phases. @@ -112,20 +200,20 @@ A stepwise migration is the best choice in most cases, especially when - downtime should be mostly avoided - you have some time to migrate -#### Graceful migration +### Graceful migration Graceful migration - also called "slow migration", "rolling migration", or "online migration" - involves running both the old and new systems in parallel, gradually migrating users as they authenticate. This approach features two "go-lives": the initial application "go-live" and subsequent user-specific cutovers during login. -##### Advantages of graceful migration +#### Advantages of graceful migration - Low risk: The gradual transition reduces the risk of widespread issues, as only a few users are affected at any given time. - No hashed credentials needed: Users are migrated during their "normal" authentication process, so you don't need to import credentials. Great if you don't have access to the hashed credentials. - No downtime: Both systems operate simultaneously until the migration is completed. -##### Drawbacks of graceful migration +#### Drawbacks of graceful migration - Extended migration period: The process takes longer as users are migrated individually over time. - Increased complexity: Maintaining synchronization between two systems adds complexity to the migration process. diff --git a/docs/migrate-to-ory/migrate/integrate-backend b/docs/migrate-to-ory/migrate/integrate-backend new file mode 100644 index 000000000..d870a8c20 --- /dev/null +++ b/docs/migrate-to-ory/migrate/integrate-backend @@ -0,0 +1,30 @@ +--- +id: integrate-backend +title: Integrate backend +sidebar_label: Integrate backend +sidebar_position: 1 +--- + +# Integrate backend + +When the frontend makes an API call to your backend, it will include the necessary cookies. Your backend must then forward these +cookies when calling the Ory API to validate the session. For example in a Go backend, you could use a +[middleware](../getting-started/integrate-auth/go#validate-and-login) to intercept API requests and validate the session by +calling Ory’s toSession() method. Ensure that the cookies received from the front end are forwarded in this call. Since backend +calls to Ory’s API won’t automatically include cookies, you must manually attach the relevant cookies to these requests. This is +important for the backend to be able to check the session. + +When using Ory to manage identities, it is best practice to store business logic in your application database and keep only +authentication-relevant data in Ory. Here’s a general approach: + +1. Configure [Ory Actions](../kratos/hooks/configure-hooks) to send webhooks to your server after user registration or other + identity-related events. The webhook payload will include the data of the newly created identity. +1. Upon receiving the webhook, your server can create a corresponding user record in your database. This allows your system to + link Ory-managed identities with your business logic. +1. Establish a connection between the Ory identity and the user record in your database by storing the `user.id` in + `identity.metadata_public.id`. This ensures that subsequent API calls can easily map the Ory identity to the correct internal + user. More about metadata in the [Identity metadata & traits ](../kratos/manage-identities/managing-users-identities-metadata) + documentation. +1. Now when the frontend makes API calls containing the Ory cookie or token, the backend should verify the session using the + whoami API endpoint. This endpoint returns the session details, including the identity, allowing the backend to authenticate + the request and link it to the internal user record. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/integrate-frontend b/docs/migrate-to-ory/migrate/integrate-frontend new file mode 100644 index 000000000..29abd4091 --- /dev/null +++ b/docs/migrate-to-ory/migrate/integrate-frontend @@ -0,0 +1,33 @@ +--- +id: integrate-frontend +title: Integrate frontend +sidebar_label: Integrate frontend +sidebar_position: 1 +--- + +# Integrate frontend + +To make authenticated API calls using Ory, start by properly configuring your domain and subdomains. By default the cookie domain +is set to the root domain (e.g., example.org) when you add a custom domain. This ensures that cookies can be shared across all +subdomains. + +- Example subdomain structure: + - Run Ory at auth.example.org. + - Host your backend API at api.example.org. + - Serve your frontend UI at www.example.org or another designated subdomain. + +This setup allows both your front end and back end to access the authentication session cookies managed by Ory. + +To begin integrating Ory into your frontend, it's helpful to start with the +["protect a page with login" guides](../getting-started/overview) that cover the basics of developing with Ory for various +programming languages and frameworks, including SDK usage and essential setup steps. + +Ory Network has two types of user interfaces. We recommend starting with the built-in +[Account Experience](../account-experience/index.mdx), which offers a standard user interface, covering all self-service flows +with the option to style branding to get you up and running. If you prefer a custom user interface that matches your current +design 1:1, Ory allows you to create and style a custom UI that integrates seamlessly with your existing setup. You can do this +using the API directly, the SDK for your language, or - if you are working in the React ecosystem - Ory Elements. Ory Elements is +a component library designed to make building login, registration, and account pages for Ory easy. It is modular and customizable, +allowing you to use only the components you need while tailoring them to fit your implementation's design. The UI created with Ory +Elements changes dynamically to adapt to your Ory Network configuration. More details about customizing the user interface with +Ory Elements can be found [here](../elements/index.mdx). \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/migrate-strategies b/docs/migrate-to-ory/migrate/migrate-strategies new file mode 100644 index 000000000..364e5363a --- /dev/null +++ b/docs/migrate-to-ory/migrate/migrate-strategies @@ -0,0 +1,106 @@ +--- +id: migrate-strategies +title: Migration strategies +sidebar_label: Migration strategies +sidebar_position: 2 +--- + +## Migration strategy + +When migrating user data from an old system to a new one, the process involves two main steps: transferring existing data and +"go-live" when users start authenticating with the new system. The choice of migration strategy depends on your specific use case, +the shape of existing data, and the number of "go-lives" you need to manage. + +- **Big bang** - Migrate everyone at once. +- **Stepped** - Migrate your applications or user segments individually. _This is the most common choice_. +- **Graceful** - Migrate when a user authenticates, running both solutions in parallel. + +Each migration strategy has its strengths and challenges. The ideal choice depends on factors such as the complexity of your +system, the number of users, and your organization's tolerance for risk and downtime. + +### Big bang migration + +In a big bang migration, also known as "offline migration", all user data is transferred at once, and a single "go-live" event is +scheduled, where all users start using the new system simultaneously. + +#### Advantages of big bang migration + +- Simplicity: Since there is only one "go-live", the migration process is easier to manage and plan. +- Time efficiency: The migration process happens in one go, reducing the time needed for the transition. +- Less complexity: There is no need to run two systems in parallel and you can retire the previous solution immediately. + +#### Drawbacks of big bang migration + +- High risk: If any issues occur during the cutover, the impact can be significant, affecting all users. +- Downtime: This approach may require planned system downtime to ensure data consistency, which can disrupt users. +- Increased preparation: Requires extensive planning and testing to mitigate risks, making it more resource-intensive during that + phase. + +A big bang migration is often not the best choice due to the risk and downtime, but it is recommended when + +- the number of users is low / app is simple +- downtime isn't a problem +- you need to retire the current solution yesterday + +### Stepwise migration + +Stepwise migration, also called "application-based migration", involves transferring user data in phases, focusing on specific +applications, services, or user segments at a time. This approach results in multiple "go-lives", each affecting a defined group +of users. + +#### Advantages of stepwise migration + +- Reduced risk: By migrating in phases, issues are isolated to specific apps or user segments. +- Flexibility: Allows for adjustments and optimizations between phases based on lessons learned. +- Minimized/no downtime: Since the migration occurs in stages, downtime can be limited to smaller user groups or avoided + completely. + +#### Drawbacks of stepwise migration + +- Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. +- Extended timeline: The migration process takes longer as it is broken down into phases. +- Resource demands: Running both systems in parallel during the transition can strain resources. + +A stepwise migration is the best choice in most cases, especially when + +- you manage multiple apps/segments of users with different underlying auth systems +- downtime should be mostly avoided +- you have some time to migrate + +### Graceful migration + +Graceful migration - also called "slow migration", "rolling migration", or "online migration" - involves running both the old and +new systems in parallel, gradually migrating users as they authenticate. This approach features two "go-lives": the initial +application "go-live" and subsequent user-specific cutovers during login. + +#### Advantages of graceful migration + +- Low risk: The gradual transition reduces the risk of widespread issues, as only a few users are affected at any given time. +- No hashed credentials needed: Users are migrated during their "normal" authentication process, so you don't need to import + credentials. Great if you don't have access to the hashed credentials. +- No downtime: Both systems operate simultaneously until the migration is completed. + +#### Drawbacks of graceful migration + +- Extended migration period: The process takes longer as users are migrated individually over time. +- Increased complexity: Maintaining synchronization between two systems adds complexity to the migration process. +- Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the two systems during the + transition period. + +A graceful migration is a good choice when + +- you don't have access to hashed credentials or they are hashed with a proprietary algorithm +- absolutely no downtime is acceptable +- running the current solution until the end of migration isn't a problem + +## Create Ory Network projects + +Now that you have chosen your migration strategy, you can begin the actual migration process by setting up your Ory Network +projects. This involves creating a new project environment where the migration will take place. + +You can create a new Ory Network project using the Ory CLI. The command ory create project allows you to specify the environment +of the project, the output format, the name of the project, and the workspace to use. More details about creating a project can be +found [here](../cli/ory-create-project). + +Before migrating your production environment, perform the migration in a development or staging environment. This allows you to +test and refine the process without affecting your live data or users. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/user-identities b/docs/migrate-to-ory/migrate/user-identities new file mode 100644 index 000000000..dbc8801c6 --- /dev/null +++ b/docs/migrate-to-ory/migrate/user-identities @@ -0,0 +1,40 @@ +--- +id: user-identities +title: Migrate user identities +sidebar_label: Migrate user identities +sidebar_position: 3 +--- + +## Get existing user identities ready for migration +With authentication now set up on your front end and back end, the next step is to prepare your existing user identities for +migration. If you're using a managed identity solution, it’s a good idea to start the export process early, especially if there's +no straightforward way to export the identities and you might need to go through a support process. + +Export user data from your existing authentication solution or database and find out the hashing algorithm used to hash their +credentials. If your passwords are not hashed, you can bypass this step, as Ory will handle password hashing automatically during +the import process. Ory supports a range of hashing algorithms; if yours is supported, use the +[create identity API](../reference/api#tag/identity/operation/createIdentity) to import your users. If the hashing algorithm isn't +supported or if you can't get the hashed passwords from your current authentication system, you may want to do a "graceful" +migration and use the password migration hook to migrate your existing users. You can find more details in the +[Import identities ](../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. + +## Import user identities + +You can use the [create identity API](../reference/api#tag/identity/operation/batchPatchIdentities) to bulk import identities into +Ory. A maximum of 2000 identities can be created in a single request. If you need to import more identities, you need to split the +import into multiple requests. The endpoint accepts a JSON array of identities, each of which must have a create property that +holds the identity that should be created. You can find more details in the +[Import identities ](../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. + +### Phased migration for active sessions + +Ory does not support the direct import of active sessions from your existing system. To ensure that users with active sessions +from the old system can continue accessing your services without needing to reauthenticate immediately, implement a transition +period during which both the old and new systems operate concurrently. During this period, all user authentication flows—including +login, registration, password recovery, and settings management—should be managed by Ory. However, your backend must be configured +to recognize and accept sessions from both the old system and Ory. + +As the transition progresses, gradually phase out the old system. Once most or all active sessions from the old system have +expired or been replaced by new sessions in Ory, you can complete the migration to Ory exclusively. This gradual approach +minimizes user disruption and provides your development team with the time needed to resolve any potential issues that may arise +during the migration. \ No newline at end of file diff --git a/src/sidebar.ts b/src/sidebar.ts index 804bae1d8..5ebf07998 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -197,7 +197,21 @@ const quickstart: SidebarItemsConfig = [ label: "Migrate to Ory", collapsed: false, collapsible: false, - items: ["migrate-to-ory/index", "migrate-to-ory/auth0"], + link: { + type: "doc", + id: "migrate-to-ory/migrate/index", + }, + items: [ + "migrate-to-ory/migrate/index", + "migrate-to-ory/migrate/migrate-strategies", + "migrate-to-ory/migrate/create-project", + "migrate-to-ory/migrate/define-id-schema", + "migrate-to-ory/migrate/integrate-frontend", + "migrate-to-ory/migrate/integrate-backend", + "migrate-to-ory/migrate/user-identities", + "migrate-to-ory/migrate/go-live", + "migrate-to-ory/auth0", + ], }, ] From c1cc94d04d729098bd494d6a27c4d8154bf25c42 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Thu, 9 Oct 2025 07:40:56 -0700 Subject: [PATCH 02/32] chore: migration guide updates --- docs/migrate-to-ory/auth0.mdx | 8 +- .../{create-project => create-project.mdx} | 8 +- ...{define-id-schema => define-id-schema.mdx} | 11 +- docs/migrate-to-ory/migrate/faq-migrate.mdx | 8 + .../migrate/{go-live => go-live.mdx} | 3 - docs/migrate-to-ory/migrate/index.mdx | 354 +----------------- ...ntegrate-backend => integrate-backend.mdx} | 12 +- ...egrate-frontend => integrate-frontend.mdx} | 12 +- ...rate-strategies => migrate-strategies.mdx} | 4 +- .../migrate-to-ory/migrate/migrate-to-ory.mdx | 130 +++++++ docs/migrate-to-ory/migrate/test-validate.mdx | 9 + .../{user-identities => user-identities.mdx} | 19 +- src/sidebar.ts | 33 +- 13 files changed, 209 insertions(+), 402 deletions(-) rename docs/migrate-to-ory/migrate/{create-project => create-project.mdx} (85%) rename docs/migrate-to-ory/migrate/{define-id-schema => define-id-schema.mdx} (83%) create mode 100644 docs/migrate-to-ory/migrate/faq-migrate.mdx rename docs/migrate-to-ory/migrate/{go-live => go-live.mdx} (99%) rename docs/migrate-to-ory/migrate/{integrate-backend => integrate-backend.mdx} (81%) rename docs/migrate-to-ory/migrate/{integrate-frontend => integrate-frontend.mdx} (79%) rename docs/migrate-to-ory/migrate/{migrate-strategies => migrate-strategies.mdx} (98%) create mode 100644 docs/migrate-to-ory/migrate/migrate-to-ory.mdx create mode 100644 docs/migrate-to-ory/migrate/test-validate.mdx rename docs/migrate-to-ory/migrate/{user-identities => user-identities.mdx} (76%) diff --git a/docs/migrate-to-ory/auth0.mdx b/docs/migrate-to-ory/auth0.mdx index 5d8a3bd91..792adbb40 100644 --- a/docs/migrate-to-ory/auth0.mdx +++ b/docs/migrate-to-ory/auth0.mdx @@ -1,12 +1,10 @@ --- id: auth0 -title: Migrate users from Auth0 to Ory -sidebar_label: Migrate from Auth0 +title: Migrate user identities from Auth0 to Ory +sidebar_label: Migrate user identities from Auth0 to Ory slug: migrate-from-auth0 --- -# Migrate from Auth0 - This guide shows you how to migrate user accounts from Auth0 to Ory. The instructions in this document assume that your Auth0 setup includes a database connection and that the users whose accounts you migrate use emails and passwords as their login credentials. @@ -148,3 +146,5 @@ Follow these steps to import Auth0 users to Ory: ```shell ory list identities --project --workspace ``` +# FAQ +TODO: Living section for specific questions/answers that Sales get asked but which don't neatly fit within the Auth0 migration process. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/create-project b/docs/migrate-to-ory/migrate/create-project.mdx similarity index 85% rename from docs/migrate-to-ory/migrate/create-project rename to docs/migrate-to-ory/migrate/create-project.mdx index a0643ee97..218de1b76 100644 --- a/docs/migrate-to-ory/migrate/create-project +++ b/docs/migrate-to-ory/migrate/create-project.mdx @@ -1,18 +1,16 @@ --- id: create-project -title: Create project -sidebar_label: Create project +title: Create Ory Network project +sidebar_label: Create Ory Network project sidebar_position: 3 --- -## Create Ory Network projects - Now that you have chosen your migration strategy, you can begin the actual migration process by setting up your Ory Network projects. This involves creating a new project environment where the migration will take place. You can create a new Ory Network project using the Ory CLI. The command ory create project allows you to specify the environment of the project, the output format, the name of the project, and the workspace to use. More details about creating a project can be -found [here](../cli/ory-create-project). +found [here](../../cli/ory-create-project). Before migrating your production environment, perform the migration in a development or staging environment. This allows you to test and refine the process without affecting your live data or users. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/define-id-schema b/docs/migrate-to-ory/migrate/define-id-schema.mdx similarity index 83% rename from docs/migrate-to-ory/migrate/define-id-schema rename to docs/migrate-to-ory/migrate/define-id-schema.mdx index 3e92a070b..a39c25131 100644 --- a/docs/migrate-to-ory/migrate/define-id-schema +++ b/docs/migrate-to-ory/migrate/define-id-schema.mdx @@ -1,18 +1,15 @@ --- -id: define-id-schema -title: Define identity schema -sidebar_label: Define identity schema +id: design-id-schema +title: Design identity schema +sidebar_label: Design identity schema sidebar_position: 3 --- - -## Define identity schema - To match identities from your current system with the new Ory system, you can customize the identity schema in Ory to meet your specific requirements. This schema defines the types of data that the system can store for users, including names, email addresses, phone numbers, and other authentication-related information. Additionally, you can specify extra metadata fields to be included in user profiles. Ory offers default presets to assist you in creating and managing identity schemas. More details about -identity schemas can be found [here](../kratos/manage-identities/identity-schema). +identity schemas can be found [here](../../kratos/manage-identities/identity-schema). - Do store profile data in your identity that is used across your system. This includes the usernames, email addresses, phone numbers, first names, and last names. diff --git a/docs/migrate-to-ory/migrate/faq-migrate.mdx b/docs/migrate-to-ory/migrate/faq-migrate.mdx new file mode 100644 index 000000000..8c863e60c --- /dev/null +++ b/docs/migrate-to-ory/migrate/faq-migrate.mdx @@ -0,0 +1,8 @@ +--- +id: faq-migrate +title: Migrate FAQ +sidebar_label: Migrate FAQ +sidebar_position: 1 +--- + +TODO: Living section for specific questions/answers that Sales get asked but which don't neatly fit within the migration process. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/go-live b/docs/migrate-to-ory/migrate/go-live.mdx similarity index 99% rename from docs/migrate-to-ory/migrate/go-live rename to docs/migrate-to-ory/migrate/go-live.mdx index 8ccc24868..77bb34b05 100644 --- a/docs/migrate-to-ory/migrate/go-live +++ b/docs/migrate-to-ory/migrate/go-live.mdx @@ -5,9 +5,6 @@ sidebar_label: Go live sidebar_position: 2 --- - -# Go live - After successfully migrating your data and testing the integration in your development or staging environment, it's time to go live with Ory in your production environment. diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index 3ff5bce2d..c3bb39af6 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -14,352 +14,18 @@ Ory Network's equivalent features. Why it matters: Not understanding your existing system’s behavior is the #1 cause of unexpected regressions during cutover. With Ory Network, you gain full control, and with it, the ability to shape your system’s flows exactly to your needs. -# Understand your current identity management system - -Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific needs and -understand the unique requirements of each approach. Each scenario differs in complexity and implementation needs. Use these IAM -scenarios to help identify and map the authentication flows for your application. - -## Identify your IAM scenario -- CIAM -- B2B -- Workforce -- Agentic AI - -### CIAM (Customer Identity and Access Management) - -Your company sells products or services directly to individual consumers. - -#### Key IAM requirements -- Self-service registration, login, and profile management for end users -- Social login, multi-factor passwordless options, and robust account recovery -- Privacy compliance (GDPR, CCPA) -- High-scale performance for millions of users - -#### Ory Network features -- Identity management & authenitication (based on Ory Kratos) -- Authorization (based on Ory Hydra, optional for OAuth2/OIDC) - -### B2B (Business-to-Business) - -Your company sells products or services directly to other businesses rather than individual consumers. Your customers are organizations -that use these products or services to run their own operations. - -#### Key IAM requirements - -- Multi-organization user management -- SSO with SAML/OIDC providers -- Self-service partner onboarding -- Role-based permissions and API controls - -#### Ory Network features - -- Identity management & authenitication (based on Ory Kratos) -- Authorization and single sign-on (based on Ory Hydra/Ory Polis) -- Permissions (based on Ory Ketos) -- Zero-trust access proxy (based on Ory Oathkeeper) - -### Workforce (Business-to-Enterprise) - -Your company provides products or services. Your company wants to manage access for a single organization's extended workforce. You want to -consolidate employee user accounts and identities across multi-tenant brands, applications and systems. You need to seamlessly connect with -existing enterprise identity providers and other 3rd party systems and streamline user onboarding, offboarding, and permission management. - -#### Key IAM requirements -- Streamline onboarding/offboarding of employee, contractor, and temporary workers -- Role-based access aligned with organizational hierarchy -- HR system integration with flexible identity schemas -- Integrate with enterprise identity providers and third-party systems -- Time-bound permissions and role assignments -- Zero-trust security, MFA, and SSO for enterprise applications - -#### Ory Network features -- Identity management & authenitication (based on Ory Kratos) -- Authorization and single sign-on (based on Ory Hydra/Ory Polis) -- Permissions (based on Ory Ketos) -- Zero-trust access proxy (based on Ory Oathkeeper) -- Webhooks - -### Agentic AI - -Your company wants to enable AI applications to securely connect to data sources and tools. For example, servers that host resources and -clients (AI applications) to discover and use those resources. - -#### Key IAM requirements -- Standardized protocol that works across many tools and data sources -- Built-in authentication and access control - -#### Ory Network features -- Identity management & authenitication (based on Ory Kratos) -- Authorization (based on Ory Hydra) -- Permissions (based on Ory Ketos) -- Zero-trust access proxy (based on Ory Oathkeeper) - -## Map all authentication and identity flows in your application - -Build a complete picture of every authentication-related process in your system. Use your IAM scenario’s Key IAM requirements to help identify -authentication and identity flows. This ensures you don’t miss critical flows during migration. - -1. Identify all entry points where authentication occurs (e.g., web app login, mobile app sign-in, API tokens, social or enterprise sign-ins). -1. Create a comprehensive inventory of flows, for example: - - Registration / Sign-up - - Sign-in/Sign-out - - Multi-Factor Authentication (MFA) - - Password reset and account recovery - - Account linking (social, enterprise logins) - - User profile management - - Token refresh and session handling - - Recovery flows, consent screens, or partner-specific integrations -1. Visualize flows with diagrams (sequence diagrams or flow charts) to surface dependencies and hidden complexity. -1. Note where authentication interacts with other systems (databases, CRMs, partner apps, or external APIs). - - -### Map your existing IAM functionality to Ory Network’s equivalent capabilities -Using your list of authentication and identity flows, create a side-by-side table to map out existing functionality and Ory Network’s equivalent -capabilities. While the majority of the time you'll find your existing functionality neatly maps to Ory's capabilities, now is the best time to -identify when it does not. Some examples: - -- Not all vendors strickly comply with standards, whereas Ory does, so you might discover you need to -change how you implement functionality to be compliant. -- You might have a unique use case to solve that requires additional help from our support. - -Table 1: An example of mapping functionality - -| Existing Functionality | Ory Capabilities | -|---|---| -| JSON Web Tokens | [Ory Session cookies/tokens](https://www.ory.sh/docs/identities/session-to-jwt-cors)| - -At the end of this process you should have a living document with diagrams that capture: - -- All authentication and identity flows -- Your system's existing functionality and Ory Network's equivalent capabilites -- Any existing dependencies on external systems -- Edge cases requiring special handling - -This will serve as your blueprint for planning, designing, testing, and validating your migration. - -## Migration strategy - -When migrating user data from an old system to a new one, the process involves two main steps: transferring existing data and -"go-live" when users start authenticating with the new system. The choice of migration strategy depends on your specific use case, -the shape of existing data, and the number of "go-lives" you need to manage. - -- **Big bang** - Migrate everyone at once. -- **Stepped** - Migrate your applications or user segments individually. _This is the most common choice_. -- **Graceful** - Migrate when a user authenticates, running both solutions in parallel. - -Each migration strategy has its strengths and challenges. The ideal choice depends on factors such as the complexity of your -system, the number of users, and your organization's tolerance for risk and downtime. - -### Big bang migration - -In a big bang migration, also known as "offline migration", all user data is transferred at once, and a single "go-live" event is -scheduled, where all users start using the new system simultaneously. - -#### Advantages of big bang migration - -- Simplicity: Since there is only one "go-live", the migration process is easier to manage and plan. -- Time efficiency: The migration process happens in one go, reducing the time needed for the transition. -- Less complexity: There is no need to run two systems in parallel and you can retire the previous solution immediately. - -#### Drawbacks of big bang migration - -- High risk: If any issues occur during the cutover, the impact can be significant, affecting all users. -- Downtime: This approach may require planned system downtime to ensure data consistency, which can disrupt users. -- Increased preparation: Requires extensive planning and testing to mitigate risks, making it more resource-intensive during that - phase. - -A big bang migration is often not the best choice due to the risk and downtime, but it is recommended when - -- the number of users is low / app is simple -- downtime isn't a problem -- you need to retire the current solution yesterday - -### Stepwise migration - -Stepwise migration, also called "application-based migration", involves transferring user data in phases, focusing on specific -applications, services, or user segments at a time. This approach results in multiple "go-lives", each affecting a defined group -of users. - -#### Advantages of stepwise migration - -- Reduced risk: By migrating in phases, issues are isolated to specific apps or user segments. -- Flexibility: Allows for adjustments and optimizations between phases based on lessons learned. -- Minimized/no downtime: Since the migration occurs in stages, downtime can be limited to smaller user groups or avoided - completely. - -#### Drawbacks of stepwise migration - -- Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. -- Extended timeline: The migration process takes longer as it is broken down into phases. -- Resource demands: Running both systems in parallel during the transition can strain resources. - -A stepwise migration is the best choice in most cases, especially when - -- you manage multiple apps/segments of users with different underlying auth systems -- downtime should be mostly avoided -- you have some time to migrate - -### Graceful migration - -Graceful migration - also called "slow migration", "rolling migration", or "online migration" - involves running both the old and -new systems in parallel, gradually migrating users as they authenticate. This approach features two "go-lives": the initial -application "go-live" and subsequent user-specific cutovers during login. - -#### Advantages of graceful migration - -- Low risk: The gradual transition reduces the risk of widespread issues, as only a few users are affected at any given time. -- No hashed credentials needed: Users are migrated during their "normal" authentication process, so you don't need to import - credentials. Great if you don't have access to the hashed credentials. -- No downtime: Both systems operate simultaneously until the migration is completed. - -#### Drawbacks of graceful migration - -- Extended migration period: The process takes longer as users are migrated individually over time. -- Increased complexity: Maintaining synchronization between two systems adds complexity to the migration process. -- Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the two systems during the - transition period. - -A graceful migration is a good choice when - -- you don't have access to hashed credentials or they are hashed with a proprietary algorithm -- absolutely no downtime is acceptable -- running the current solution until the end of migration isn't a problem - -## Create Ory Network projects - -Now that you have chosen your migration strategy, you can begin the actual migration process by setting up your Ory Network -projects. This involves creating a new project environment where the migration will take place. - -You can create a new Ory Network project using the Ory CLI. The command ory create project allows you to specify the environment -of the project, the output format, the name of the project, and the workspace to use. More details about creating a project can be -found [here](../cli/ory-create-project). - -Before migrating your production environment, perform the migration in a development or staging environment. This allows you to -test and refine the process without affecting your live data or users. - -## Define identity schema - -To match identities from your current system with the new Ory system, you can customize the identity schema in Ory to meet your -specific requirements. This schema defines the types of data that the system can store for users, including names, email -addresses, phone numbers, and other authentication-related information. Additionally, you can specify extra metadata fields to be -included in user profiles. Ory offers default presets to assist you in creating and managing identity schemas. More details about -identity schemas can be found [here](../kratos/manage-identities/identity-schema). - -- Do store profile data in your identity that is used across your system. This includes the usernames, email addresses, phone - numbers, first names, and last names. -- Do not store business logic in your identity, store this information in other systems, such as your application database. This - includes for example credit card information, shipping addresses, shopping cart items, or user preferences. See also the - following section on backend integration. - -## Integrate frontend - -To make authenticated API calls using Ory, start by properly configuring your domain and subdomains. By default the cookie domain -is set to the root domain (e.g., example.org) when you add a custom domain. This ensures that cookies can be shared across all -subdomains. - -- Example subdomain structure: - - Run Ory at auth.example.org. - - Host your backend API at api.example.org. - - Serve your frontend UI at www.example.org or another designated subdomain. - -This setup allows both your front end and back end to access the authentication session cookies managed by Ory. - -To begin integrating Ory into your frontend, it's helpful to start with the -["protect a page with login" guides](../getting-started/overview) that cover the basics of developing with Ory for various -programming languages and frameworks, including SDK usage and essential setup steps. - -Ory Network has two types of user interfaces. We recommend starting with the built-in -[Account Experience](../account-experience/index.mdx), which offers a standard user interface, covering all self-service flows -with the option to style branding to get you up and running. If you prefer a custom user interface that matches your current -design 1:1, Ory allows you to create and style a custom UI that integrates seamlessly with your existing setup. You can do this -using the API directly, the SDK for your language, or - if you are working in the React ecosystem - Ory Elements. Ory Elements is -a component library designed to make building login, registration, and account pages for Ory easy. It is modular and customizable, -allowing you to use only the components you need while tailoring them to fit your implementation's design. The UI created with Ory -Elements changes dynamically to adapt to your Ory Network configuration. More details about customizing the user interface with -Ory Elements can be found [here](../elements/index.mdx). - -## Integrate backend - -When the frontend makes an API call to your backend, it will include the necessary cookies. Your backend must then forward these -cookies when calling the Ory API to validate the session. For example in a Go backend, you could use a -[middleware](../getting-started/integrate-auth/go#validate-and-login) to intercept API requests and validate the session by -calling Ory’s toSession() method. Ensure that the cookies received from the front end are forwarded in this call. Since backend -calls to Ory’s API won’t automatically include cookies, you must manually attach the relevant cookies to these requests. This is -important for the backend to be able to check the session. - -When using Ory to manage identities, it is best practice to store business logic in your application database and keep only -authentication-relevant data in Ory. Here’s a general approach: - -1. Configure [Ory Actions](../kratos/hooks/configure-hooks) to send webhooks to your server after user registration or other - identity-related events. The webhook payload will include the data of the newly created identity. -1. Upon receiving the webhook, your server can create a corresponding user record in your database. This allows your system to - link Ory-managed identities with your business logic. -1. Establish a connection between the Ory identity and the user record in your database by storing the `user.id` in - `identity.metadata_public.id`. This ensures that subsequent API calls can easily map the Ory identity to the correct internal - user. More about metadata in the [Identity metadata & traits ](../kratos/manage-identities/managing-users-identities-metadata) - documentation. -1. Now when the frontend makes API calls containing the Ory cookie or token, the backend should verify the session using the - whoami API endpoint. This endpoint returns the session details, including the identity, allowing the backend to authenticate - the request and link it to the internal user record. - -## Get user data and credentials - -With authentication now set up on your front end and back end, the next step is to prepare your existing user identities for -migration. If you're using a managed identity solution, it’s a good idea to start the export process early, especially if there's -no straightforward way to export the identities and you might need to go through a support process. - -Export user data from your existing authentication solution or database and find out the hashing algorithm used to hash their -credentials. If your passwords are not hashed, you can bypass this step, as Ory will handle password hashing automatically during -the import process. Ory supports a range of hashing algorithms; if yours is supported, use the -[create identity API](../reference/api#tag/identity/operation/createIdentity) to import your users. If the hashing algorithm isn't -supported or if you can't get the hashed passwords from your current authentication system, you may want to do a "graceful" -migration and use the password migration hook to migrate your existing users. You can find more details in the -[Import identities ](../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. - -## Import identities - -You can use the [create identity API](../reference/api#tag/identity/operation/batchPatchIdentities) to bulk import identities into -Ory. A maximum of 2000 identities can be created in a single request. If you need to import more identities, you need to split the -import into multiple requests. The endpoint accepts a JSON array of identities, each of which must have a create property that -holds the identity that should be created. You can find more details in the -[Import identities ](../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. - -### Phased migration for active sessions - -Ory does not support the direct import of active sessions from your existing system. To ensure that users with active sessions -from the old system can continue accessing your services without needing to reauthenticate immediately, implement a transition -period during which both the old and new systems operate concurrently. During this period, all user authentication flows—including -login, registration, password recovery, and settings management—should be managed by Ory. However, your backend must be configured -to recognize and accept sessions from both the old system and Ory. - -As the transition progresses, gradually phase out the old system. Once most or all active sessions from the old system have -expired or been replaced by new sessions in Ory, you can complete the migration to Ory exclusively. This gradual approach -minimizes user disruption and provides your development team with the time needed to resolve any potential issues that may arise -during the migration. - -## Go live - -After successfully migrating your data and testing the integration in your development or staging environment, it's time to go -live with Ory in your production environment. - -1. Final testing: Before switching over to Ory in production, conduct thorough testing of all user authentication flows, identity - management features, and access controls. This includes testing edge cases, error handling, and load testing to ensure the - system can handle your user base. -1. Prepare a rollback plan: In case any issues arise during the go-live process, have a rollback plan in place. This may involve - reverting to your previous authentication system or restoring data from backups. -1. Schedule the go-live: Choose a time for the go-live that minimizes the impact on your users, such as during low-traffic - periods. Communicate the planned migration to your users in advance, including any expected downtime or changes they should be - aware of. -1. Monitor the transition: As you switch over to Ory, closely monitor the system for any issues, such as failed authentications, - performance bottlenecks, or user complaints. Use live events to monitor the system under - . -1. Optimize and refine: After the go-live, continue to monitor the system and gather user feedback. - -Once your Ory integration is stable and users are successfully authenticating with the new system, your migration is complete. +1. [Understand your current identity management system](migrate-to-ory) +1. [Migration strategies](migrate-strategies) +1. [Create Ory Network project](create-project) +1. [Design identity schema](design-id-schema) +1. [Frontend integration and UI migration](integrate-frontend) +1. [Backend integration](integrate-backend) +1. [Data migration strategies](migrate-identities) +1. [Test and validate](test-validate) +1. [Go live](go-live) ```mdx-code-block import Help from '@site/docs/_common/need-help.mdx' -``` +``` \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/integrate-backend b/docs/migrate-to-ory/migrate/integrate-backend.mdx similarity index 81% rename from docs/migrate-to-ory/migrate/integrate-backend rename to docs/migrate-to-ory/migrate/integrate-backend.mdx index d870a8c20..7e6364329 100644 --- a/docs/migrate-to-ory/migrate/integrate-backend +++ b/docs/migrate-to-ory/migrate/integrate-backend.mdx @@ -1,15 +1,13 @@ --- id: integrate-backend -title: Integrate backend -sidebar_label: Integrate backend +title: Backend integration +sidebar_label: Backend integration sidebar_position: 1 --- -# Integrate backend - When the frontend makes an API call to your backend, it will include the necessary cookies. Your backend must then forward these cookies when calling the Ory API to validate the session. For example in a Go backend, you could use a -[middleware](../getting-started/integrate-auth/go#validate-and-login) to intercept API requests and validate the session by +[middleware](../../getting-started/integrate-auth/go#validate-and-login) to intercept API requests and validate the session by calling Ory’s toSession() method. Ensure that the cookies received from the front end are forwarded in this call. Since backend calls to Ory’s API won’t automatically include cookies, you must manually attach the relevant cookies to these requests. This is important for the backend to be able to check the session. @@ -17,13 +15,13 @@ important for the backend to be able to check the session. When using Ory to manage identities, it is best practice to store business logic in your application database and keep only authentication-relevant data in Ory. Here’s a general approach: -1. Configure [Ory Actions](../kratos/hooks/configure-hooks) to send webhooks to your server after user registration or other +1. Configure [Ory Actions](../../kratos/hooks/configure-hooks) to send webhooks to your server after user registration or other identity-related events. The webhook payload will include the data of the newly created identity. 1. Upon receiving the webhook, your server can create a corresponding user record in your database. This allows your system to link Ory-managed identities with your business logic. 1. Establish a connection between the Ory identity and the user record in your database by storing the `user.id` in `identity.metadata_public.id`. This ensures that subsequent API calls can easily map the Ory identity to the correct internal - user. More about metadata in the [Identity metadata & traits ](../kratos/manage-identities/managing-users-identities-metadata) + user. More about metadata in the [Identity metadata & traits ](../../kratos/manage-identities/managing-users-identities-metadata) documentation. 1. Now when the frontend makes API calls containing the Ory cookie or token, the backend should verify the session using the whoami API endpoint. This endpoint returns the session details, including the identity, allowing the backend to authenticate diff --git a/docs/migrate-to-ory/migrate/integrate-frontend b/docs/migrate-to-ory/migrate/integrate-frontend.mdx similarity index 79% rename from docs/migrate-to-ory/migrate/integrate-frontend rename to docs/migrate-to-ory/migrate/integrate-frontend.mdx index 29abd4091..cadc97678 100644 --- a/docs/migrate-to-ory/migrate/integrate-frontend +++ b/docs/migrate-to-ory/migrate/integrate-frontend.mdx @@ -1,12 +1,10 @@ --- id: integrate-frontend -title: Integrate frontend -sidebar_label: Integrate frontend +title: Frontend integration and UI migration +sidebar_label: Frontend integration and UI migration sidebar_position: 1 --- -# Integrate frontend - To make authenticated API calls using Ory, start by properly configuring your domain and subdomains. By default the cookie domain is set to the root domain (e.g., example.org) when you add a custom domain. This ensures that cookies can be shared across all subdomains. @@ -19,15 +17,15 @@ subdomains. This setup allows both your front end and back end to access the authentication session cookies managed by Ory. To begin integrating Ory into your frontend, it's helpful to start with the -["protect a page with login" guides](../getting-started/overview) that cover the basics of developing with Ory for various +["protect a page with login" guides](../../getting-started/overview) that cover the basics of developing with Ory for various programming languages and frameworks, including SDK usage and essential setup steps. Ory Network has two types of user interfaces. We recommend starting with the built-in -[Account Experience](../account-experience/index.mdx), which offers a standard user interface, covering all self-service flows +[Account Experience](../../account-experience/index.mdx), which offers a standard user interface, covering all self-service flows with the option to style branding to get you up and running. If you prefer a custom user interface that matches your current design 1:1, Ory allows you to create and style a custom UI that integrates seamlessly with your existing setup. You can do this using the API directly, the SDK for your language, or - if you are working in the React ecosystem - Ory Elements. Ory Elements is a component library designed to make building login, registration, and account pages for Ory easy. It is modular and customizable, allowing you to use only the components you need while tailoring them to fit your implementation's design. The UI created with Ory Elements changes dynamically to adapt to your Ory Network configuration. More details about customizing the user interface with -Ory Elements can be found [here](../elements/index.mdx). \ No newline at end of file +Ory Elements can be found [here](../../elements/index.mdx). \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/migrate-strategies b/docs/migrate-to-ory/migrate/migrate-strategies.mdx similarity index 98% rename from docs/migrate-to-ory/migrate/migrate-strategies rename to docs/migrate-to-ory/migrate/migrate-strategies.mdx index 364e5363a..14aa1521e 100644 --- a/docs/migrate-to-ory/migrate/migrate-strategies +++ b/docs/migrate-to-ory/migrate/migrate-strategies.mdx @@ -5,8 +5,6 @@ sidebar_label: Migration strategies sidebar_position: 2 --- -## Migration strategy - When migrating user data from an old system to a new one, the process involves two main steps: transferring existing data and "go-live" when users start authenticating with the new system. The choice of migration strategy depends on your specific use case, the shape of existing data, and the number of "go-lives" you need to manage. @@ -100,7 +98,7 @@ projects. This involves creating a new project environment where the migration w You can create a new Ory Network project using the Ory CLI. The command ory create project allows you to specify the environment of the project, the output format, the name of the project, and the workspace to use. More details about creating a project can be -found [here](../cli/ory-create-project). +found [here](../../cli/ory-create-project). Before migrating your production environment, perform the migration in a development or staging environment. This allows you to test and refine the process without affecting your live data or users. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/migrate-to-ory.mdx b/docs/migrate-to-ory/migrate/migrate-to-ory.mdx new file mode 100644 index 000000000..184bf94ae --- /dev/null +++ b/docs/migrate-to-ory/migrate/migrate-to-ory.mdx @@ -0,0 +1,130 @@ +--- +id: migrate-to-ory +title: Understand your current identity management system +sidebar_label: Understand your current identity management system +sidebar_position: 1 +--- +# Understand your current identity management system + +Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific needs and +understand the unique requirements of each approach. Each scenario differs in complexity and implementation needs. Use these IAM +scenarios to help identify and map the authentication flows for your application. + +## Identify your IAM scenario +- [CIAM](#ciam-customer-identity-and-access-management) +- [B2B](#b2b-business-to-business) +- [Workforce](#workforce-business-to-enterprise) +- [Agentic AI](#agentic-ai) + + +### CIAM (Customer Identity and Access Management) + +Your company sells products or services directly to individual consumers. + +#### Key IAM requirements +- Self-service registration, login, and profile management for end users +- Social login, multi-factor passwordless options, and robust account recovery +- Privacy compliance (GDPR, CCPA) +- High-scale performance for millions of users + +#### Ory Network features +- Identity management & authenitication (based on Ory Kratos) +- Authorization (based on Ory Hydra, optional for OAuth2/OIDC) + +### B2B (Business-to-Business) + +Your company sells products or services directly to other businesses rather than individual consumers. Your customers are organizations +that use these products or services to run their own operations. + +#### Key IAM requirements + +- Multi-organization user management +- SSO with SAML/OIDC providers +- Self-service partner onboarding +- Role-based permissions and API controls + +#### Ory Network features + +- Identity management & authenitication (based on Ory Kratos) +- Authorization and single sign-on (based on Ory Hydra/Ory Polis) +- Permissions (based on Ory Ketos) +- Zero-trust access proxy (based on Ory Oathkeeper) + +### Workforce (Business-to-Enterprise) + +Your company provides products or services. Your company wants to manage access for a single organization's extended workforce. You want to +consolidate employee user accounts and identities across multi-tenant brands, applications and systems. You need to seamlessly connect with +existing enterprise identity providers and other 3rd party systems and streamline user onboarding, offboarding, and permission management. + +#### Key IAM requirements +- Streamline onboarding/offboarding of employee, contractor, and temporary workers +- Role-based access aligned with organizational hierarchy +- HR system integration with flexible identity schemas +- Integrate with enterprise identity providers and third-party systems +- Time-bound permissions and role assignments +- Zero-trust security, MFA, and SSO for enterprise applications + +#### Ory Network features +- Identity management & authenitication (based on Ory Kratos) +- Authorization and single sign-on (based on Ory Hydra/Ory Polis) +- Permissions (based on Ory Ketos) +- Zero-trust access proxy (based on Ory Oathkeeper) +- Webhooks + +### Agentic AI + +Your company wants to enable AI applications to securely connect to data sources and tools. For example, servers that host resources and +clients (AI applications) to discover and use those resources. + +#### Key IAM requirements +- Standardized protocol that works across many tools and data sources +- Built-in authentication and access control + +#### Ory Network features +- Identity management & authenitication (based on Ory Kratos) +- Authorization (based on Ory Hydra) +- Permissions (based on Ory Ketos) +- Zero-trust access proxy (based on Ory Oathkeeper) + +## Map all authentication and identity flows in your application + +Build a complete picture of every authentication-related process in your system. Use your IAM scenario’s Key IAM requirements to help identify +authentication and identity flows. This ensures you don’t miss critical flows during migration. + +1. Identify all entry points where authentication occurs (e.g., web app login, mobile app sign-in, API tokens, social or enterprise sign-ins). +1. Create a comprehensive inventory of flows, for example: + - Registration / Sign-up + - Sign-in/Sign-out + - Multi-Factor Authentication (MFA) + - Password reset and account recovery + - Account linking (social, enterprise logins) + - User profile management + - Token refresh and session handling + - Recovery flows, consent screens, or partner-specific integrations +1. Visualize flows with diagrams (sequence diagrams or flow charts) to surface dependencies and hidden complexity. +1. Note where authentication interacts with other systems (databases, CRMs, partner apps, or external APIs). + + +## Map existing IAM functionality to Ory Network’s equivalent capabilities +Using your list of authentication and identity flows, create a side-by-side table to map out existing functionality and Ory Network’s equivalent +capabilities. While the majority of the time you'll find your existing functionality neatly maps to Ory's capabilities, now is the best time to +identify when it does not. Some examples: + +- Not all vendors strickly comply with standards, whereas Ory does, so you might discover you need to +change how you implement functionality to be compliant. +- You might have a unique use case to solve that requires additional help from our support. + +Table 1: An example of mapping functionality + +| Existing Functionality | Ory Capabilities | +|---|---| +| JSON Web Tokens | [Ory Session cookies/tokens](https://www.ory.sh/docs/identities/session-to-jwt-cors)| + +At the end of this process you should have a living document with diagrams that capture: + +- All authentication and identity flows +- Your system's existing functionality and Ory Network's equivalent capabilites +- Any existing dependencies on external systems +- Edge cases requiring special handling + +This will serve as your blueprint for planning, designing, testing, and validating your migration. diff --git a/docs/migrate-to-ory/migrate/test-validate.mdx b/docs/migrate-to-ory/migrate/test-validate.mdx new file mode 100644 index 000000000..6f6d1298e --- /dev/null +++ b/docs/migrate-to-ory/migrate/test-validate.mdx @@ -0,0 +1,9 @@ +--- +id: test-validate +title: Test and validate +sidebar_label: Test and validate +sidebar_position: 1 +--- + + +TBD \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/user-identities b/docs/migrate-to-ory/migrate/user-identities.mdx similarity index 76% rename from docs/migrate-to-ory/migrate/user-identities rename to docs/migrate-to-ory/migrate/user-identities.mdx index dbc8801c6..a35ea1f4b 100644 --- a/docs/migrate-to-ory/migrate/user-identities +++ b/docs/migrate-to-ory/migrate/user-identities.mdx @@ -1,30 +1,31 @@ --- -id: user-identities -title: Migrate user identities -sidebar_label: Migrate user identities +id: migrate-identities +title: Data migration strategies +sidebar_label: Data migration strategies sidebar_position: 3 --- +With authentication now set up on your front end and back end, the next step is to prepare your existing user identities for +migration. ## Get existing user identities ready for migration -With authentication now set up on your front end and back end, the next step is to prepare your existing user identities for -migration. If you're using a managed identity solution, it’s a good idea to start the export process early, especially if there's +If you're using a managed identity solution, it’s a good idea to start the export process early, especially if there's no straightforward way to export the identities and you might need to go through a support process. Export user data from your existing authentication solution or database and find out the hashing algorithm used to hash their credentials. If your passwords are not hashed, you can bypass this step, as Ory will handle password hashing automatically during the import process. Ory supports a range of hashing algorithms; if yours is supported, use the -[create identity API](../reference/api#tag/identity/operation/createIdentity) to import your users. If the hashing algorithm isn't +[create identity API](../../reference/api#tag/identity/operation/createIdentity) to import your users. If the hashing algorithm isn't supported or if you can't get the hashed passwords from your current authentication system, you may want to do a "graceful" migration and use the password migration hook to migrate your existing users. You can find more details in the -[Import identities ](../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. +[Import identities ](../../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. ## Import user identities -You can use the [create identity API](../reference/api#tag/identity/operation/batchPatchIdentities) to bulk import identities into +You can use the [create identity API] (../../reference/api#tag/identity/operation/batchPatchIdentities) to bulk import identities into Ory. A maximum of 2000 identities can be created in a single request. If you need to import more identities, you need to split the import into multiple requests. The endpoint accepts a JSON array of identities, each of which must have a create property that holds the identity that should be created. You can find more details in the -[Import identities ](../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. +[Import identities ](../../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. ### Phased migration for active sessions diff --git a/src/sidebar.ts b/src/sidebar.ts index 5ebf07998..cd83fa0ea 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -184,7 +184,6 @@ const quickstart: SidebarItemsConfig = [ type: "doc", id: "getting-started/overview", }, - items: [ { type: "autogenerated", @@ -197,19 +196,27 @@ const quickstart: SidebarItemsConfig = [ label: "Migrate to Ory", collapsed: false, collapsible: false, - link: { - type: "doc", - id: "migrate-to-ory/migrate/index", - }, items: [ - "migrate-to-ory/migrate/index", - "migrate-to-ory/migrate/migrate-strategies", - "migrate-to-ory/migrate/create-project", - "migrate-to-ory/migrate/define-id-schema", - "migrate-to-ory/migrate/integrate-frontend", - "migrate-to-ory/migrate/integrate-backend", - "migrate-to-ory/migrate/user-identities", - "migrate-to-ory/migrate/go-live", + { + type: "category", + label: "Migrate to Ory Network", + link: { + type: "doc", + id: "migrate-to-ory/migrate/index", + }, + items: [ + "migrate-to-ory/migrate/migrate-to-ory", + "migrate-to-ory/migrate/migrate-strategies", + "migrate-to-ory/migrate/create-project", + "migrate-to-ory/migrate/design-id-schema", + "migrate-to-ory/migrate/integrate-frontend", + "migrate-to-ory/migrate/integrate-backend", + "migrate-to-ory/migrate/migrate-identities", + "migrate-to-ory/migrate/test-validate", + "migrate-to-ory/migrate/go-live", + "migrate-to-ory/migrate/faq-migrate", + ], + }, "migrate-to-ory/auth0", ], }, From 67f2ad3dacad4edc8b562962a1d2fd4074ec268c Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Thu, 9 Oct 2025 21:52:28 -0700 Subject: [PATCH 03/32] chore: migration guide updates --- .../migrate-to-ory/migrate/create-project.mdx | 4 +- ...ine-id-schema.mdx => design-id-schema.mdx} | 4 +- docs/migrate-to-ory/migrate/faq-migrate.mdx | 4 +- docs/migrate-to-ory/migrate/index.mdx | 14 ++--- .../migrate/integrate-backend.mdx | 4 +- .../migrate/integrate-frontend.mdx | 4 +- docs/migrate-to-ory/migrate/map-to-orycap.mdx | 20 ++++++ ...-identities.mdx => migrate-identities.mdx} | 6 +- .../migrate/migrate-strategies.mdx | 16 +---- .../migrate-to-ory/migrate/migrate-to-ory.mdx | 62 ++++--------------- docs/migrate-to-ory/migrate/test-validate.mdx | 26 +++++++- src/sidebar.ts | 1 + 12 files changed, 78 insertions(+), 87 deletions(-) rename docs/migrate-to-ory/migrate/{define-id-schema.mdx => design-id-schema.mdx} (93%) create mode 100644 docs/migrate-to-ory/migrate/map-to-orycap.mdx rename docs/migrate-to-ory/migrate/{user-identities.mdx => migrate-identities.mdx} (93%) diff --git a/docs/migrate-to-ory/migrate/create-project.mdx b/docs/migrate-to-ory/migrate/create-project.mdx index 218de1b76..81b5708f3 100644 --- a/docs/migrate-to-ory/migrate/create-project.mdx +++ b/docs/migrate-to-ory/migrate/create-project.mdx @@ -1,7 +1,7 @@ --- id: create-project -title: Create Ory Network project -sidebar_label: Create Ory Network project +title: Create an Ory Network project +sidebar_label: Create an Ory Network project sidebar_position: 3 --- diff --git a/docs/migrate-to-ory/migrate/define-id-schema.mdx b/docs/migrate-to-ory/migrate/design-id-schema.mdx similarity index 93% rename from docs/migrate-to-ory/migrate/define-id-schema.mdx rename to docs/migrate-to-ory/migrate/design-id-schema.mdx index a39c25131..9f898b3b0 100644 --- a/docs/migrate-to-ory/migrate/define-id-schema.mdx +++ b/docs/migrate-to-ory/migrate/design-id-schema.mdx @@ -1,7 +1,7 @@ --- id: design-id-schema -title: Design identity schema -sidebar_label: Design identity schema +title: Design your identity schema +sidebar_label: Design your identity schema sidebar_position: 3 --- diff --git a/docs/migrate-to-ory/migrate/faq-migrate.mdx b/docs/migrate-to-ory/migrate/faq-migrate.mdx index 8c863e60c..e85dd4587 100644 --- a/docs/migrate-to-ory/migrate/faq-migrate.mdx +++ b/docs/migrate-to-ory/migrate/faq-migrate.mdx @@ -1,7 +1,7 @@ --- id: faq-migrate -title: Migrate FAQ -sidebar_label: Migrate FAQ +title: Migration FAQ +sidebar_label: Migration FAQ sidebar_position: 1 --- diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index c3bb39af6..8b72b41e7 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -15,13 +15,13 @@ Why it matters: Not understanding your existing system’s behavior is the #1 ca Ory Network, you gain full control, and with it, the ability to shape your system’s flows exactly to your needs. 1. [Understand your current identity management system](migrate-to-ory) -1. [Migration strategies](migrate-strategies) -1. [Create Ory Network project](create-project) -1. [Design identity schema](design-id-schema) -1. [Frontend integration and UI migration](integrate-frontend) -1. [Backend integration](integrate-backend) -1. [Data migration strategies](migrate-identities) -1. [Test and validate](test-validate) +1. [Choose your migration strategy](migrate-strategies) +1. [Create an Ory Network project](create-project) +1. [Design your identity schema](design-id-schema) +1. [Integrate your frontend and UI](integrate-frontend) +1. [Integrate your backend](integrate-backend) +1. [Migrate your existing identities](migrate-identities) +1. [Test and validate your integration](test-validate) 1. [Go live](go-live) ```mdx-code-block diff --git a/docs/migrate-to-ory/migrate/integrate-backend.mdx b/docs/migrate-to-ory/migrate/integrate-backend.mdx index 7e6364329..a29698aa9 100644 --- a/docs/migrate-to-ory/migrate/integrate-backend.mdx +++ b/docs/migrate-to-ory/migrate/integrate-backend.mdx @@ -1,7 +1,7 @@ --- id: integrate-backend -title: Backend integration -sidebar_label: Backend integration +title: Integrate your backend +sidebar_label: Integrate your backend sidebar_position: 1 --- diff --git a/docs/migrate-to-ory/migrate/integrate-frontend.mdx b/docs/migrate-to-ory/migrate/integrate-frontend.mdx index cadc97678..bbac68ac0 100644 --- a/docs/migrate-to-ory/migrate/integrate-frontend.mdx +++ b/docs/migrate-to-ory/migrate/integrate-frontend.mdx @@ -1,7 +1,7 @@ --- id: integrate-frontend -title: Frontend integration and UI migration -sidebar_label: Frontend integration and UI migration +title: Integrate your frontend and UI +sidebar_label: Integrate your frontend and UI sidebar_position: 1 --- diff --git a/docs/migrate-to-ory/migrate/map-to-orycap.mdx b/docs/migrate-to-ory/migrate/map-to-orycap.mdx new file mode 100644 index 000000000..5130e6c13 --- /dev/null +++ b/docs/migrate-to-ory/migrate/map-to-orycap.mdx @@ -0,0 +1,20 @@ +--- +id: map-to-orycap +title: Map your existing functionality to Ory Network capabilities +sidebar_label: Map your existing functionality to Ory Network capabilities +sidebar_position: 1 +--- + +Using your list of identity flows, create a side-by-side table to map existing functionality to Ory Network’s equivalent +capabilities. While the majority of the time you'll find your existing functionality neatly maps to Ory's capabilities, now is the best time to +identify when it does not. Some examples: + +- Not all vendors strickly comply with standards, whereas Ory does, so you might discover you need to +change how you implement functionality to be compliant. +- You might have a unique use case to solve that requires additional help from our support. + +Table 1: An example of mapping functionality + +| Existing Functionality | Ory Capabilities | +|---|---| +| JSON Web Tokens | [Ory Session cookies/tokens](https://www.ory.sh/docs/identities/session-to-jwt-cors)| diff --git a/docs/migrate-to-ory/migrate/user-identities.mdx b/docs/migrate-to-ory/migrate/migrate-identities.mdx similarity index 93% rename from docs/migrate-to-ory/migrate/user-identities.mdx rename to docs/migrate-to-ory/migrate/migrate-identities.mdx index a35ea1f4b..1efd93298 100644 --- a/docs/migrate-to-ory/migrate/user-identities.mdx +++ b/docs/migrate-to-ory/migrate/migrate-identities.mdx @@ -1,7 +1,7 @@ --- id: migrate-identities -title: Data migration strategies -sidebar_label: Data migration strategies +title: Migrate your existing identities +sidebar_label: Migrate your existing identities sidebar_position: 3 --- With authentication now set up on your front end and back end, the next step is to prepare your existing user identities for @@ -19,6 +19,8 @@ supported or if you can't get the hashed passwords from your current authenticat migration and use the password migration hook to migrate your existing users. You can find more details in the [Import identities ](../../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. +- For migrating from Auth0s, see [Migrate user identities from Auth0 to Ory](../migrate-from-auth0) + ## Import user identities You can use the [create identity API] (../../reference/api#tag/identity/operation/batchPatchIdentities) to bulk import identities into diff --git a/docs/migrate-to-ory/migrate/migrate-strategies.mdx b/docs/migrate-to-ory/migrate/migrate-strategies.mdx index 14aa1521e..ad7c113f1 100644 --- a/docs/migrate-to-ory/migrate/migrate-strategies.mdx +++ b/docs/migrate-to-ory/migrate/migrate-strategies.mdx @@ -1,7 +1,7 @@ --- id: migrate-strategies -title: Migration strategies -sidebar_label: Migration strategies +title: Choose your migration strategy +sidebar_label: Choose your migration strategy sidebar_position: 2 --- @@ -90,15 +90,3 @@ A graceful migration is a good choice when - you don't have access to hashed credentials or they are hashed with a proprietary algorithm - absolutely no downtime is acceptable - running the current solution until the end of migration isn't a problem - -## Create Ory Network projects - -Now that you have chosen your migration strategy, you can begin the actual migration process by setting up your Ory Network -projects. This involves creating a new project environment where the migration will take place. - -You can create a new Ory Network project using the Ory CLI. The command ory create project allows you to specify the environment -of the project, the output format, the name of the project, and the workspace to use. More details about creating a project can be -found [here](../../cli/ory-create-project). - -Before migrating your production environment, perform the migration in a development or staging environment. This allows you to -test and refine the process without affecting your live data or users. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/migrate-to-ory.mdx b/docs/migrate-to-ory/migrate/migrate-to-ory.mdx index 184bf94ae..9ca9aff59 100644 --- a/docs/migrate-to-ory/migrate/migrate-to-ory.mdx +++ b/docs/migrate-to-ory/migrate/migrate-to-ory.mdx @@ -8,7 +8,7 @@ sidebar_position: 1 Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific needs and understand the unique requirements of each approach. Each scenario differs in complexity and implementation needs. Use these IAM -scenarios to help identify and map the authentication flows for your application. +scenarios to map the identity flows for your application. ## Identify your IAM scenario - [CIAM](#ciam-customer-identity-and-access-management) @@ -27,10 +27,6 @@ Your company sells products or services directly to individual consumers. - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users -#### Ory Network features -- Identity management & authenitication (based on Ory Kratos) -- Authorization (based on Ory Hydra, optional for OAuth2/OIDC) - ### B2B (Business-to-Business) Your company sells products or services directly to other businesses rather than individual consumers. Your customers are organizations @@ -43,13 +39,6 @@ that use these products or services to run their own operations. - Self-service partner onboarding - Role-based permissions and API controls -#### Ory Network features - -- Identity management & authenitication (based on Ory Kratos) -- Authorization and single sign-on (based on Ory Hydra/Ory Polis) -- Permissions (based on Ory Ketos) -- Zero-trust access proxy (based on Ory Oathkeeper) - ### Workforce (Business-to-Enterprise) Your company provides products or services. Your company wants to manage access for a single organization's extended workforce. You want to @@ -64,13 +53,6 @@ existing enterprise identity providers and other 3rd party systems and streamlin - Time-bound permissions and role assignments - Zero-trust security, MFA, and SSO for enterprise applications -#### Ory Network features -- Identity management & authenitication (based on Ory Kratos) -- Authorization and single sign-on (based on Ory Hydra/Ory Polis) -- Permissions (based on Ory Ketos) -- Zero-trust access proxy (based on Ory Oathkeeper) -- Webhooks - ### Agentic AI Your company wants to enable AI applications to securely connect to data sources and tools. For example, servers that host resources and @@ -80,50 +62,28 @@ clients (AI applications) to discover and use those resources. - Standardized protocol that works across many tools and data sources - Built-in authentication and access control -#### Ory Network features -- Identity management & authenitication (based on Ory Kratos) -- Authorization (based on Ory Hydra) -- Permissions (based on Ory Ketos) -- Zero-trust access proxy (based on Ory Oathkeeper) - -## Map all authentication and identity flows in your application +## Map all identity flows in your application -Build a complete picture of every authentication-related process in your system. Use your IAM scenario’s Key IAM requirements to help identify -authentication and identity flows. This ensures you don’t miss critical flows during migration. +Build a complete picture of every identity-related process in your system. Use your IAM scenario’s Key IAM requirements to identify +these flows. This ensures you don’t miss critical flows during migration. -1. Identify all entry points where authentication occurs (e.g., web app login, mobile app sign-in, API tokens, social or enterprise sign-ins). +1. Identify all entry points where an identity-related process occurs (e.g., web app login, mobile app sign-in, API tokens, social or enterprise sign-ins). 1. Create a comprehensive inventory of flows, for example: - - Registration / Sign-up - - Sign-in/Sign-out + - Registration/sign-up + - Sign-in/sign-out - Multi-Factor Authentication (MFA) - Password reset and account recovery - Account linking (social, enterprise logins) - User profile management - Token refresh and session handling - Recovery flows, consent screens, or partner-specific integrations -1. Visualize flows with diagrams (sequence diagrams or flow charts) to surface dependencies and hidden complexity. -1. Note where authentication interacts with other systems (databases, CRMs, partner apps, or external APIs). - - -## Map existing IAM functionality to Ory Network’s equivalent capabilities -Using your list of authentication and identity flows, create a side-by-side table to map out existing functionality and Ory Network’s equivalent -capabilities. While the majority of the time you'll find your existing functionality neatly maps to Ory's capabilities, now is the best time to -identify when it does not. Some examples: - -- Not all vendors strickly comply with standards, whereas Ory does, so you might discover you need to -change how you implement functionality to be compliant. -- You might have a unique use case to solve that requires additional help from our support. - -Table 1: An example of mapping functionality - -| Existing Functionality | Ory Capabilities | -|---|---| -| JSON Web Tokens | [Ory Session cookies/tokens](https://www.ory.sh/docs/identities/session-to-jwt-cors)| +1. Create flow diagrams (sequence diagrams or flow charts) to surface dependencies and hidden complexity. +1. Note where identity (authentication and authorization) interacts with other systems (databases, CRMs, partner apps, or external APIs). At the end of this process you should have a living document with diagrams that capture: -- All authentication and identity flows -- Your system's existing functionality and Ory Network's equivalent capabilites +- All identity (authentication and authorization) flows +- Your system's existing functionality - Any existing dependencies on external systems - Edge cases requiring special handling diff --git a/docs/migrate-to-ory/migrate/test-validate.mdx b/docs/migrate-to-ory/migrate/test-validate.mdx index 6f6d1298e..db33da9b0 100644 --- a/docs/migrate-to-ory/migrate/test-validate.mdx +++ b/docs/migrate-to-ory/migrate/test-validate.mdx @@ -1,9 +1,29 @@ --- id: test-validate -title: Test and validate -sidebar_label: Test and validate +title: Test and validate your integration +sidebar_label: Test and validate your integration sidebar_position: 1 --- +Before going live with your Ory Network integration, thorough testing across multiple environments is essential +to ensure your authentication system works securely and reliably. You should test progressively through development, +staging, and production environments, validating that configurations, custom domains, and integrations work correctly +at each stage. +Create a comprehensive list of testing requirements. For example: -TBD \ No newline at end of file +- Authentication Flows: Login, registration, password reset, account recovery +- Session Management: Creation, validation, expiration, security +- Data Integrity: User profiles, metadata synchronization, permissions +- API Functionality: All protected routes, authorization checks +- User Interface: Forms, redirects, error handling, responsive design +- Performance: Response times, concurrent user handling, load capacity +- Security: CSRF protection, session security, data encryption + +## Migration Validation Process +Implement systematic validation to ensure migration completeness, data accuracy, and functional parity with existing systems. +Compare user counts between systems, validate authentication flows for sample users, and verify all business logic continues +to function correctly. + +## User Acceptance Testing +Conduct thorough user acceptance testing with representative user groups to ensure the migrated system meets usability +requirements and maintains expected functionality. \ No newline at end of file diff --git a/src/sidebar.ts b/src/sidebar.ts index cd83fa0ea..c0d0f6899 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -206,6 +206,7 @@ const quickstart: SidebarItemsConfig = [ }, items: [ "migrate-to-ory/migrate/migrate-to-ory", + "migrate-to-ory/migrate/map-to-orycap", "migrate-to-ory/migrate/migrate-strategies", "migrate-to-ory/migrate/create-project", "migrate-to-ory/migrate/design-id-schema", From 0ecca7d98c78232c6b4249290ed8a8f8557f3c30 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Tue, 14 Oct 2025 08:04:39 -0700 Subject: [PATCH 04/32] chore: edits to migration guide --- .../migrate-to-ory/migrate/create-project.mdx | 4 +- docs/migrate-to-ory/migrate/index.mdx | 15 +++-- .../migrate/integrate-frontend.mdx | 4 +- docs/migrate-to-ory/migrate/map-to-orycap.mdx | 2 +- .../migrate/migrate-strategies.mdx | 62 ++++++++++++------- .../migrate-to-ory/migrate/migrate-to-ory.mdx | 10 +-- 6 files changed, 58 insertions(+), 39 deletions(-) diff --git a/docs/migrate-to-ory/migrate/create-project.mdx b/docs/migrate-to-ory/migrate/create-project.mdx index 81b5708f3..7d8853f87 100644 --- a/docs/migrate-to-ory/migrate/create-project.mdx +++ b/docs/migrate-to-ory/migrate/create-project.mdx @@ -6,11 +6,11 @@ sidebar_position: 3 --- Now that you have chosen your migration strategy, you can begin the actual migration process by setting up your Ory Network -projects. This involves creating a new project environment where the migration will take place. +projects in a development, staging, and production environment. You can create a new Ory Network project using the Ory CLI. The command ory create project allows you to specify the environment of the project, the output format, the name of the project, and the workspace to use. More details about creating a project can be found [here](../../cli/ory-create-project). -Before migrating your production environment, perform the migration in a development or staging environment. This allows you to +Before migrating to your production environment, perform the migration in a development or staging environment. This allows you to test and refine the process without affecting your live data or users. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index 8b72b41e7..3712f9eac 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -8,17 +8,22 @@ sidebar_position: 1 # Migrate to Ory Network Before you can migrate smoothly, you need a complete picture of how your identity management system works today. This step ensures -nothing gets missed — from everyday login flows to rare edge cases — and sets the foundation for mapping existing features to -Ory Network's equivalent features. +nothing gets missed — from everyday login flows to rare edge cases — and sets the foundation for mapping existing functionality to +Ory Network's equivalent capabilities. -Why it matters: Not understanding your existing system’s behavior is the #1 cause of unexpected regressions during cutover. With -Ory Network, you gain full control, and with it, the ability to shape your system’s flows exactly to your needs. +:::info Why it matters + +Not understanding your existing system’s behavior is the #1 cause of unexpected regressions during cutover. + +::: + +With Ory Network, you gain full control, and with it, the ability to shape your system’s flows exactly to your needs. 1. [Understand your current identity management system](migrate-to-ory) 1. [Choose your migration strategy](migrate-strategies) 1. [Create an Ory Network project](create-project) 1. [Design your identity schema](design-id-schema) -1. [Integrate your frontend and UI](integrate-frontend) +1. [Integrate your frontend](integrate-frontend) 1. [Integrate your backend](integrate-backend) 1. [Migrate your existing identities](migrate-identities) 1. [Test and validate your integration](test-validate) diff --git a/docs/migrate-to-ory/migrate/integrate-frontend.mdx b/docs/migrate-to-ory/migrate/integrate-frontend.mdx index bbac68ac0..41a44ce6d 100644 --- a/docs/migrate-to-ory/migrate/integrate-frontend.mdx +++ b/docs/migrate-to-ory/migrate/integrate-frontend.mdx @@ -1,7 +1,7 @@ --- id: integrate-frontend -title: Integrate your frontend and UI -sidebar_label: Integrate your frontend and UI +title: Integrate your frontend +sidebar_label: Integrate your frontend sidebar_position: 1 --- diff --git a/docs/migrate-to-ory/migrate/map-to-orycap.mdx b/docs/migrate-to-ory/migrate/map-to-orycap.mdx index 5130e6c13..f8bf736d0 100644 --- a/docs/migrate-to-ory/migrate/map-to-orycap.mdx +++ b/docs/migrate-to-ory/migrate/map-to-orycap.mdx @@ -5,7 +5,7 @@ sidebar_label: Map your existing functionality to Ory Network capabilities sidebar_position: 1 --- -Using your list of identity flows, create a side-by-side table to map existing functionality to Ory Network’s equivalent +Using your list of identity-related flows, create a side-by-side table to map existing functionality to Ory Network’s equivalent capabilities. While the majority of the time you'll find your existing functionality neatly maps to Ory's capabilities, now is the best time to identify when it does not. Some examples: diff --git a/docs/migrate-to-ory/migrate/migrate-strategies.mdx b/docs/migrate-to-ory/migrate/migrate-strategies.mdx index ad7c113f1..ead96cb7e 100644 --- a/docs/migrate-to-ory/migrate/migrate-strategies.mdx +++ b/docs/migrate-to-ory/migrate/migrate-strategies.mdx @@ -5,22 +5,39 @@ sidebar_label: Choose your migration strategy sidebar_position: 2 --- -When migrating user data from an old system to a new one, the process involves two main steps: transferring existing data and -"go-live" when users start authenticating with the new system. The choice of migration strategy depends on your specific use case, -the shape of existing data, and the number of "go-lives" you need to manage. +When migrating user data, there are two main steps in the migration process; the first is a backfill, in which existing data +is transferred from the old system to the new system, and the second is a cutover or "go-live event", in which users authenticate with the new +system for the first time. There are three main migration strategies that consider these steps as well as when you turn-off your old system. -- **Big bang** - Migrate everyone at once. +The three main migration strategies are: + +- **Big bang** - Migrate all users at once. - **Stepped** - Migrate your applications or user segments individually. _This is the most common choice_. - **Graceful** - Migrate when a user authenticates, running both solutions in parallel. -Each migration strategy has its strengths and challenges. The ideal choice depends on factors such as the complexity of your -system, the number of users, and your organization's tolerance for risk and downtime. +Each migration strategy has its strengths and challenges. Which migration strategy you choose depends on factors such as the +complexity of your system, the number of users, and your organization's tolerance for risk and downtime. + +:::info + +While your choice of migration strategy is mostly influenced by user data, if you have a complex system or are risk adverse, you might +choose your migration strategy based on its ability to breakdown the migration into a series of migrations. This lets you test your +processes in production by migrating less critical, sets of users or applications first and learning lessons to improve the migration process. + +::: ### Big bang migration In a big bang migration, also known as "offline migration", all user data is transferred at once, and a single "go-live" event is scheduled, where all users start using the new system simultaneously. +#### Recommendation +A big bang migration is often not the best choice due to the risk and downtime, but it is recommended when: + +- The number of users is low / app is simple +- Downtime isn't a problem +- You need to retire the current solution yesterday + #### Advantages of big bang migration - Simplicity: Since there is only one "go-live", the migration process is easier to manage and plan. @@ -34,18 +51,20 @@ scheduled, where all users start using the new system simultaneously. - Increased preparation: Requires extensive planning and testing to mitigate risks, making it more resource-intensive during that phase. -A big bang migration is often not the best choice due to the risk and downtime, but it is recommended when - -- the number of users is low / app is simple -- downtime isn't a problem -- you need to retire the current solution yesterday - ### Stepwise migration Stepwise migration, also called "application-based migration", involves transferring user data in phases, focusing on specific applications, services, or user segments at a time. This approach results in multiple "go-lives", each affecting a defined group of users. +#### Recommendation +A stepwise migration is the best choice in most cases, especially when: + +- You manage multiple apps/segments of users with different underlying auth systems +- You need to keep active sessions live during the migration +- Downtime should be mostly avoided +- You have some time to migrate + #### Advantages of stepwise migration - Reduced risk: By migrating in phases, issues are isolated to specific apps or user segments. @@ -59,18 +78,19 @@ of users. - Extended timeline: The migration process takes longer as it is broken down into phases. - Resource demands: Running both systems in parallel during the transition can strain resources. -A stepwise migration is the best choice in most cases, especially when - -- you manage multiple apps/segments of users with different underlying auth systems -- downtime should be mostly avoided -- you have some time to migrate - ### Graceful migration Graceful migration - also called "slow migration", "rolling migration", or "online migration" - involves running both the old and new systems in parallel, gradually migrating users as they authenticate. This approach features two "go-lives": the initial application "go-live" and subsequent user-specific cutovers during login. +#### Recommendation +A graceful migration is a good choice when: + +- You don't have access to hashed credentials or they are hashed with a proprietary algorithm +- Absolutely no downtime is acceptable +- Running the current solution until the end of migration isn't a problem + #### Advantages of graceful migration - Low risk: The gradual transition reduces the risk of widespread issues, as only a few users are affected at any given time. @@ -84,9 +104,3 @@ application "go-live" and subsequent user-specific cutovers during login. - Increased complexity: Maintaining synchronization between two systems adds complexity to the migration process. - Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the two systems during the transition period. - -A graceful migration is a good choice when - -- you don't have access to hashed credentials or they are hashed with a proprietary algorithm -- absolutely no downtime is acceptable -- running the current solution until the end of migration isn't a problem diff --git a/docs/migrate-to-ory/migrate/migrate-to-ory.mdx b/docs/migrate-to-ory/migrate/migrate-to-ory.mdx index 9ca9aff59..4b62ba809 100644 --- a/docs/migrate-to-ory/migrate/migrate-to-ory.mdx +++ b/docs/migrate-to-ory/migrate/migrate-to-ory.mdx @@ -6,7 +6,7 @@ sidebar_position: 1 --- # Understand your current identity management system -Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific needs and +Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific IAM needs and understand the unique requirements of each approach. Each scenario differs in complexity and implementation needs. Use these IAM scenarios to map the identity flows for your application. @@ -43,7 +43,7 @@ that use these products or services to run their own operations. Your company provides products or services. Your company wants to manage access for a single organization's extended workforce. You want to consolidate employee user accounts and identities across multi-tenant brands, applications and systems. You need to seamlessly connect with -existing enterprise identity providers and other 3rd party systems and streamline user onboarding, offboarding, and permission management. +existing enterprise identity providers and other 3rd party systems, and streamline user onboarding, offboarding, and permission management. #### Key IAM requirements - Streamline onboarding/offboarding of employee, contractor, and temporary workers @@ -56,7 +56,7 @@ existing enterprise identity providers and other 3rd party systems and streamlin ### Agentic AI Your company wants to enable AI applications to securely connect to data sources and tools. For example, servers that host resources and -clients (AI applications) to discover and use those resources. +clients (AI applications) that discover and use those resources. #### Key IAM requirements - Standardized protocol that works across many tools and data sources @@ -78,11 +78,11 @@ these flows. This ensures you don’t miss critical flows during migration. - Token refresh and session handling - Recovery flows, consent screens, or partner-specific integrations 1. Create flow diagrams (sequence diagrams or flow charts) to surface dependencies and hidden complexity. -1. Note where identity (authentication and authorization) interacts with other systems (databases, CRMs, partner apps, or external APIs). +1. Note where identity-related (authentication and authorization) processes interact with other systems (databases, CRMs, partner apps, or external APIs). At the end of this process you should have a living document with diagrams that capture: -- All identity (authentication and authorization) flows +- All identity-related (authentication and authorization) flows - Your system's existing functionality - Any existing dependencies on external systems - Edge cases requiring special handling From a0ef5d132b70dcb22b38b5ed5cbc170afa20cfe3 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Thu, 23 Oct 2025 22:37:56 -0700 Subject: [PATCH 05/32] docs: updated migration strategies and identity schema content --- .../migrate/design-id-schema.mdx | 41 +++-- docs/migrate-to-ory/migrate/index.mdx | 107 +++++++++++-- .../migrate/migrate-identities.mdx | 44 +++-- .../migrate/migrate-strategies.mdx | 150 ++++++++++-------- .../migrate-to-ory/migrate/migrate-to-ory.mdx | 6 +- src/sidebar.ts | 60 +++++-- 6 files changed, 270 insertions(+), 138 deletions(-) diff --git a/docs/migrate-to-ory/migrate/design-id-schema.mdx b/docs/migrate-to-ory/migrate/design-id-schema.mdx index 9f898b3b0..9000ecbf1 100644 --- a/docs/migrate-to-ory/migrate/design-id-schema.mdx +++ b/docs/migrate-to-ory/migrate/design-id-schema.mdx @@ -5,14 +5,33 @@ sidebar_label: Design your identity schema sidebar_position: 3 --- -To match identities from your current system with the new Ory system, you can customize the identity schema in Ory to meet your -specific requirements. This schema defines the types of data that the system can store for users, including names, email -addresses, phone numbers, and other authentication-related information. Additionally, you can specify extra metadata fields to be -included in user profiles. Ory offers default presets to assist you in creating and managing identity schemas. More details about -identity schemas can be found [here](../../kratos/manage-identities/identity-schema). - -- Do store profile data in your identity that is used across your system. This includes the usernames, email addresses, phone - numbers, first names, and last names. -- Do not store business logic in your identity, store this information in other systems, such as your application database. This - includes for example credit card information, shipping addresses, shopping cart items, or user preferences. See also the - following section on backend integration. \ No newline at end of file +To align identities from your current system with Ory, you can customize the identity schema to meet your specific requirements. +This ability to customize the identity schema means you can enhance and improve your old identity schema during the migration process. + +The identity schema, which implements the JSON Schema standard, defines the types of data the system can store for users—such as +names, email addresses, phone numbers, or birthdays—and controls business logic by specifying which fields serve as login +identifiers and which are used for verification or recovery. + +Identities have two main data types: traits (attributes that users can modify themselves, such as username or email address) +and metadata (attributes defined by system admins that can only be updated through admin APIs. Metadata is useful for storing +information like subscription status, legacy user IDs, or basic roles). Metadata comes in two forms: public (visible to +users via session endpoints) and admin (only accessible through admin APIs). + +## Best practices for identity schema design: + +### Do: + +- Use the identity schema for basic profile information that's used across your system, including usernames, email addresses, +phone numbers, first names, and last names +- Store authentication-related data like login identifiers and verification addresses in traits +- Use metadata fields for system-managed information like legacy user IDs, subscription status, or basic roles + +### Don't: + +- Store sensitive internal data or information that should be obfuscated from users in the identity schema, since users can +see traits and other data (except credentials) using the `/sessions/whoami` endpoint +- Store business logic or application-specific data like credit card information, shipping addresses, shopping cart items, or +detailed user preferences—keep this in your application database instead + +Ory provides preset schemas to help you get started quickly. For detailed guidance on customizing schemas, see the +[identity schemas](../../kratos/manage-identities/identity-schema) documentation. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index 3712f9eac..62e49fd96 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -1,11 +1,10 @@ --- id: index -title: Migrate to Ory Network -sidebar_label: Migrate to Ory Network +title: Understand your current IAM system +sidebar_label: Understand your current IAM system sidebar_position: 1 --- - -# Migrate to Ory Network +# Understand your current IAM system Before you can migrate smoothly, you need a complete picture of how your identity management system works today. This step ensures nothing gets missed — from everyday login flows to rare edge cases — and sets the foundation for mapping existing functionality to @@ -13,21 +12,97 @@ Ory Network's equivalent capabilities. :::info Why it matters -Not understanding your existing system’s behavior is the #1 cause of unexpected regressions during cutover. +Not understanding your existing system’s behavior is the #1 cause of unexpected regressions during cutover. With Ory Network, you +gain full control, and with it, the ability to shape your system’s flows exactly to your needs. ::: -With Ory Network, you gain full control, and with it, the ability to shape your system’s flows exactly to your needs. - -1. [Understand your current identity management system](migrate-to-ory) -1. [Choose your migration strategy](migrate-strategies) -1. [Create an Ory Network project](create-project) -1. [Design your identity schema](design-id-schema) -1. [Integrate your frontend](integrate-frontend) -1. [Integrate your backend](integrate-backend) -1. [Migrate your existing identities](migrate-identities) -1. [Test and validate your integration](test-validate) -1. [Go live](go-live) +Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific IAM needs and +understand the unique requirements of each approach. Each scenario differs in complexity and implementation needs. Use these IAM +scenarios to map the identity flows for your application. + +## Identify your IAM scenario +- [CIAM](#ciam-customer-identity-and-access-management) +- [B2B](#b2b-business-to-business) +- [Workforce](#workforce-business-to-enterprise) +- [Agentic AI](#agentic-ai) + + +### CIAM (Customer Identity and Access Management) + +Your company sells products or services directly to individual consumers. + +#### Key IAM requirements +- Self-service registration, login, and profile management for end users +- Social login, multi-factor passwordless options, and robust account recovery +- Privacy compliance (GDPR, CCPA) +- High-scale performance for millions of users + +### B2B (Business-to-Business) + +Your company sells products or services directly to other businesses rather than individual consumers. Your customers are organizations +that use these products or services to run their own operations. + +#### Key IAM requirements + +- Multi-organization user management +- SSO with SAML/OIDC providers +- Self-service partner onboarding +- Role-based permissions and API controls +- Privacy compliance (GDPR, CCPA) +- High-scale performance for millions of users + +### Workforce (Business-to-Enterprise) + +Your company provides products or services. Your company wants to manage access for a single organization's extended workforce. You want to +consolidate employee user accounts and identities across multi-tenant brands, applications and systems. You need to seamlessly connect with +existing enterprise identity providers and other 3rd party systems, and streamline user onboarding, offboarding, and permission management. + +#### Key IAM requirements +- Streamline onboarding/offboarding of employee, contractor, and temporary workers +- Role-based access aligned with organizational hierarchy +- HR system integration with flexible identity schemas +- Integrate with enterprise identity providers and third-party systems +- Time-bound permissions and role assignments +- Zero-trust security, MFA, and SSO for enterprise applications +- Privacy compliance (GDPR, CCPA) +- High-scale performance for millions of users + +### Agentic AI + +Your company wants to enable AI applications to securely connect to data sources and tools. For example, servers that host resources and +clients (AI applications) that discover and use those resources. + +#### Key IAM requirements +- Standardized protocol that works across many tools and data sources +- Built-in authentication and access control + +## Map all identity flows in your application + +Build a complete picture of every identity-related process in your system. Use your IAM scenario’s Key IAM requirements to identify +these flows. This ensures you don’t miss critical flows during migration. + +1. Identify all entry points where an identity-related process occurs (e.g., web app login, mobile app sign-in, API tokens, social or enterprise sign-ins). +1. Create a comprehensive inventory of flows, for example: + - Registration/sign-up + - Sign-in/sign-out + - Multi-Factor Authentication (MFA) + - Password reset and account recovery + - Account linking (social, enterprise logins) + - User profile management + - Token refresh and session handling + - Recovery flows, consent screens, or partner-specific integrations +1. Create flow diagrams (sequence diagrams or flow charts) to surface dependencies and hidden complexity. +1. Note where identity-related (authentication and authorization) processes interact with other systems (databases, CRMs, partner apps, or external APIs). + +At the end of this process you should have a living document with diagrams that capture: + +- All identity-related (authentication and authorization) flows +- Your system's existing functionality +- Any existing dependencies on external systems +- Edge cases requiring special handling + +This will serve as your blueprint for planning, designing, testing, and validating your migration. ```mdx-code-block import Help from '@site/docs/_common/need-help.mdx' diff --git a/docs/migrate-to-ory/migrate/migrate-identities.mdx b/docs/migrate-to-ory/migrate/migrate-identities.mdx index 1efd93298..5119be06e 100644 --- a/docs/migrate-to-ory/migrate/migrate-identities.mdx +++ b/docs/migrate-to-ory/migrate/migrate-identities.mdx @@ -4,39 +4,33 @@ title: Migrate your existing identities sidebar_label: Migrate your existing identities sidebar_position: 3 --- -With authentication now set up on your front end and back end, the next step is to prepare your existing user identities for -migration. +With authentication now set up on your frontend and backend, the next step is to prepare your existing user identities for +migration. You should have already chosen your identity migration strategy in [Phase 1: Plan and prepare](http://localhost:3002/docs/migrate-to-ory/migrate/migrate-strategies); +"bulk" or "graceful" migration. You will now implement your chosen identity migration strategy. -## Get existing user identities ready for migration -If you're using a managed identity solution, it’s a good idea to start the export process early, especially if there's -no straightforward way to export the identities and you might need to go through a support process. +## Bulk import of identities -Export user data from your existing authentication solution or database and find out the hashing algorithm used to hash their -credentials. If your passwords are not hashed, you can bypass this step, as Ory will handle password hashing automatically during -the import process. Ory supports a range of hashing algorithms; if yours is supported, use the -[create identity API](../../reference/api#tag/identity/operation/createIdentity) to import your users. If the hashing algorithm isn't -supported or if you can't get the hashed passwords from your current authentication system, you may want to do a "graceful" -migration and use the password migration hook to migrate your existing users. You can find more details in the -[Import identities ](../../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. +If you're using a managed identity solution, it’s a good idea to start the export of existing identities early, especially +if there's no straightforward way to export the identities and you might need to go through a support process. Identify the +hashing algorithm used for credentials—if your passwords aren't hashed, Ory handles hashing automatically during import. +If Ory supports your hashing algorithm, use the [create identity API](../../reference/api#tag/identity/operation/createIdentity) +to import users. If the hashing algorithm isn't supported or if you can't get the hashed passwords from your current +authentication system, you may want to do a "graceful" migration. -- For migrating from Auth0s, see [Migrate user identities from Auth0 to Ory](../migrate-from-auth0) +The create identity API supports bulk imports with a maximum of 1000 identities per request—larger imports require multiple +requests. The endpoint accepts a JSON array of identities, each of which must have a create property that holds the identity +that should be created. See the [Import identities ](../../kratos/manage-identities/25_import-user-accounts-identities.mdx) +documentation for complete details. -## Import user identities +- For Auth0 migrations, see the dedicated [Migrate user identities from Auth0 to Ory](../migrate-from-auth0) guide. -You can use the [create identity API] (../../reference/api#tag/identity/operation/batchPatchIdentities) to bulk import identities into -Ory. A maximum of 2000 identities can be created in a single request. If you need to import more identities, you need to split the -import into multiple requests. The endpoint accepts a JSON array of identities, each of which must have a create property that -holds the identity that should be created. You can find more details in the -[Import identities ](../../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. - -### Phased migration for active sessions +## Graceful import of identities Ory does not support the direct import of active sessions from your existing system. To ensure that users with active sessions from the old system can continue accessing your services without needing to reauthenticate immediately, implement a transition -period during which both the old and new systems operate concurrently. During this period, all user authentication flows—including -login, registration, password recovery, and settings management—should be managed by Ory. However, your backend must be configured -to recognize and accept sessions from both the old system and Ory. - +period during which both the old and new systems operate concurrently. During this period, use the [password migration hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook) +to migrate your existing users. All user authentication flows—including login, registration, password recovery, and settings management—should be +managed by Ory. However, your backend must be configured to recognize and accept sessions from both the old system and Ory. As the transition progresses, gradually phase out the old system. Once most or all active sessions from the old system have expired or been replaced by new sessions in Ory, you can complete the migration to Ory exclusively. This gradual approach minimizes user disruption and provides your development team with the time needed to resolve any potential issues that may arise diff --git a/docs/migrate-to-ory/migrate/migrate-strategies.mdx b/docs/migrate-to-ory/migrate/migrate-strategies.mdx index ead96cb7e..3ba8719a7 100644 --- a/docs/migrate-to-ory/migrate/migrate-strategies.mdx +++ b/docs/migrate-to-ory/migrate/migrate-strategies.mdx @@ -1,106 +1,116 @@ --- id: migrate-strategies -title: Choose your migration strategy -sidebar_label: Choose your migration strategy +title: Choose your IAM migration strategy +sidebar_label: Choose your IAM migration strategy sidebar_position: 2 --- +This section covers both the strategies for migrating user identities to Ory and the broader considerations for +rolling out the Ory IAM system across all your applications. While user identity migration focuses on transferring user +accounts, credentials, and profile data, the IAM rollout determines how and when each application transitions to using Ory +for authentication and authorization. Understanding both dimensions is essential for planning a successful transition—the +strategy you choose for migrating user identities may differ for each phase in which you roll out the Ory IAM functionality across your +application portfolio. -When migrating user data, there are two main steps in the migration process; the first is a backfill, in which existing data -is transferred from the old system to the new system, and the second is a cutover or "go-live event", in which users authenticate with the new -system for the first time. There are three main migration strategies that consider these steps as well as when you turn-off your old system. +Your choice of migration strategy depends on your specific requirements, technical constraints, and tolerance for user impact. +It should be driven by several key factors: the complexity of your system, the size of your user base, and your organization's +risk tolerance and downtime constraints. -The three main migration strategies are: +## Choosing your user identity migration strategy -- **Big bang** - Migrate all users at once. -- **Stepped** - Migrate your applications or user segments individually. _This is the most common choice_. -- **Graceful** - Migrate when a user authenticates, running both solutions in parallel. +Ory supports two primary identity migration strategies: -Each migration strategy has its strengths and challenges. Which migration strategy you choose depends on factors such as the -complexity of your system, the number of users, and your organization's tolerance for risk and downtime. +- **Bulk migration** - Migrate all users at once. +- **Graceful migration** - Migrate when a user authenticates, running old and new solutions in parallel. -:::info +By carefully understanding these methods and planning accordingly, you can ensure a smooth and secure transition for your users to +Ory Network. -While your choice of migration strategy is mostly influenced by user data, if you have a complex system or are risk adverse, you might -choose your migration strategy based on its ability to breakdown the migration into a series of migrations. This lets you test your -processes in production by migrating less critical, sets of users or applications first and learning lessons to improve the migration process. +### Bulk identity migration -::: +In a bulk identity migration, also known as "big bang" or "offline" migration, all user data is migrated at once, and a single +"go-live" event is scheduled, where all users start using the new system simultaneously. -### Big bang migration +#### When to use bulk identity migration +A bulk identity migration can involve some risk and downtime, but it is recommended when: -In a big bang migration, also known as "offline migration", all user data is transferred at once, and a single "go-live" event is -scheduled, where all users start using the new system simultaneously. +- The number of users is low and applications or services are simple +- Planned system downtime due to the "go-live" event isn't a problem +- You need to retire the current solution soon +- Your legacy IAM vendor restricts your ability to use a graceful identity migration strategy (for example, Auth0). -#### Recommendation -A big bang migration is often not the best choice due to the risk and downtime, but it is recommended when: +##### Advantages of bulk identity migration -- The number of users is low / app is simple -- Downtime isn't a problem -- You need to retire the current solution yesterday +- Simplicity: Since there is only one "go-live", the identity migration process is easier to manage and plan. +- Time efficiency: The identity migration process happens in one go, reducing the transition time. +- Less complexity: There is no need to run two systems in parallel and you can retire the previous solution immediately after +testing and validating the migration. -#### Advantages of big bang migration +##### Drawbacks of bulk identitiy migration -- Simplicity: Since there is only one "go-live", the migration process is easier to manage and plan. -- Time efficiency: The migration process happens in one go, reducing the time needed for the transition. -- Less complexity: There is no need to run two systems in parallel and you can retire the previous solution immediately. - -#### Drawbacks of big bang migration - -- High risk: If any issues occur during the cutover, the impact can be significant, affecting all users. +- High risk: If any issues occur during the cutover, the impact can be significant, affecting all users. If might be difficult +to perform a rollback. - Downtime: This approach may require planned system downtime to ensure data consistency, which can disrupt users. - Increased preparation: Requires extensive planning and testing to mitigate risks, making it more resource-intensive during that phase. -### Stepwise migration +### Graceful identity migration -Stepwise migration, also called "application-based migration", involves transferring user data in phases, focusing on specific -applications, services, or user segments at a time. This approach results in multiple "go-lives", each affecting a defined group -of users. +Graceful identity migration - also called "automatic", "trickle", "just-in-time", or "online" migration - involves running both the old and +new systems in parallel, gradually migrating user identities as the user authenticates. This approach features two "go-lives": the initial +application "go-live" and subsequent user-specific cutovers during login. -#### Recommendation -A stepwise migration is the best choice in most cases, especially when: +#### When to use graceful identity migration +A graceful identity migration is a good choice when: -- You manage multiple apps/segments of users with different underlying auth systems -- You need to keep active sessions live during the migration -- Downtime should be mostly avoided -- You have some time to migrate +- You don't have access to hashed credentials or they are hashed with a proprietary algorithm +- Authentication system downtime is not acceptable +- Running the current solution until the end of migration isn't a problem +- You need a smooth user experience, so your users will not notice the migration -#### Advantages of stepwise migration +##### Advantages of graceful identity migration -- Reduced risk: By migrating in phases, issues are isolated to specific apps or user segments. -- Flexibility: Allows for adjustments and optimizations between phases based on lessons learned. -- Minimized/no downtime: Since the migration occurs in stages, downtime can be limited to smaller user groups or avoided - completely. +- Low risk: The gradual identity migration reduces the risk of widespread issues, as only a few users are affected at any given time. +- No hashed credentials needed: Users are migrated during their "normal" authentication process, so you don't need to import + credentials. Great if you don't have access to the hashed credentials. +- No downtime: Both authentication systems operate simultaneously until the identity migration is completed. -#### Drawbacks of stepwise migration +##### Drawbacks of graceful identity migration -- Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. -- Extended timeline: The migration process takes longer as it is broken down into phases. -- Resource demands: Running both systems in parallel during the transition can strain resources. +- Extended migration period: The process takes longer as users are migrated individually over time. +- Increased complexity: Maintaining synchronization between two systems adds complexity to the migration process. (You need to +ensure identites are synchronized in both systems in case a rollback is required.) In addition, a graceful migration requires +additional coding effort to implement Ory's [password migration using a web hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook). +- Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the old and new systems during the + transition period. -### Graceful migration +## Choosing your IAM system rollout strategy -Graceful migration - also called "slow migration", "rolling migration", or "online migration" - involves running both the old and -new systems in parallel, gradually migrating users as they authenticate. This approach features two "go-lives": the initial -application "go-live" and subsequent user-specific cutovers during login. +Simpler systems with homogenous segments of users and only one legacy authenication system to migrate from can be migrated in +one IAM system rollout. However, complex systems or risk-averse organizations may benefit from a "stepwise", "application-based", +or a "phased" rollout approach. This allows you to test processes in production by migrating less critical user segments or +applications first, learning valuable lessons to refine your approach before a broader rollout. -#### Recommendation -A graceful migration is a good choice when: +A stepwise approach involves migrating both user identities and applications/services in phases, focusing on specific applications, +services, or user segments. This approach results in multiple "go-lives", each affecting a defined group of users or applications/services. +A stepwise approach can employ bulk migration or graceful migration strategies, or a hybrid of both. The key difference is that a +specific segment of users or authenication for a specific application or service is migrated separately, each in its own specific phase. -- You don't have access to hashed credentials or they are hashed with a proprietary algorithm -- Absolutely no downtime is acceptable -- Running the current solution until the end of migration isn't a problem +### When to use a stepwise rollout approach +A stepwise approach is the best choice in most cases, especially when: -#### Advantages of graceful migration +- You manage multiple enterpise-level applications with different underlying authentication systems +- You manage diverse segments of users with different underlying authentication systems +- You have a complex system, and you want to reduce the risk of unknown consequences and improve the process iteratively. -- Low risk: The gradual transition reduces the risk of widespread issues, as only a few users are affected at any given time. -- No hashed credentials needed: Users are migrated during their "normal" authentication process, so you don't need to import - credentials. Great if you don't have access to the hashed credentials. -- No downtime: Both systems operate simultaneously until the migration is completed. +### Advantages of stepwise rollout approach + +- Reduced risk: By migrating in phases, issues are isolated to specific apps or user segments. +- Flexibility: Allows for adjustments and optimizations between phases based on lessons learned. +- Minimized/no downtime: Since the migration occurs in stages, downtime can be limited to smaller user groups or avoided + completely. -#### Drawbacks of graceful migration +### Drawbacks of stepwise rollout approach -- Extended migration period: The process takes longer as users are migrated individually over time. -- Increased complexity: Maintaining synchronization between two systems adds complexity to the migration process. -- Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the two systems during the - transition period. +- Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. +- Extended timeline: The time to migrate the whole system takes longer as it is broken down into phases. +- Resource demands: Running both systems in parallel during the transition can strain resources. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/migrate-to-ory.mdx b/docs/migrate-to-ory/migrate/migrate-to-ory.mdx index 4b62ba809..d05884a97 100644 --- a/docs/migrate-to-ory/migrate/migrate-to-ory.mdx +++ b/docs/migrate-to-ory/migrate/migrate-to-ory.mdx @@ -4,7 +4,7 @@ title: Understand your current identity management system sidebar_label: Understand your current identity management system sidebar_position: 1 --- -# Understand your current identity management system +# Understand your current IAM system Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific IAM needs and understand the unique requirements of each approach. Each scenario differs in complexity and implementation needs. Use these IAM @@ -38,6 +38,8 @@ that use these products or services to run their own operations. - SSO with SAML/OIDC providers - Self-service partner onboarding - Role-based permissions and API controls +- Privacy compliance (GDPR, CCPA) +- High-scale performance for millions of users ### Workforce (Business-to-Enterprise) @@ -52,6 +54,8 @@ existing enterprise identity providers and other 3rd party systems, and streamli - Integrate with enterprise identity providers and third-party systems - Time-bound permissions and role assignments - Zero-trust security, MFA, and SSO for enterprise applications +- Privacy compliance (GDPR, CCPA) +- High-scale performance for millions of users ### Agentic AI diff --git a/src/sidebar.ts b/src/sidebar.ts index c0d0f6899..b6560b2e9 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -200,22 +200,52 @@ const quickstart: SidebarItemsConfig = [ { type: "category", label: "Migrate to Ory Network", - link: { - type: "doc", - id: "migrate-to-ory/migrate/index", - }, + collapsed: true, + collapsible: true, items: [ - "migrate-to-ory/migrate/migrate-to-ory", - "migrate-to-ory/migrate/map-to-orycap", - "migrate-to-ory/migrate/migrate-strategies", - "migrate-to-ory/migrate/create-project", - "migrate-to-ory/migrate/design-id-schema", - "migrate-to-ory/migrate/integrate-frontend", - "migrate-to-ory/migrate/integrate-backend", - "migrate-to-ory/migrate/migrate-identities", - "migrate-to-ory/migrate/test-validate", - "migrate-to-ory/migrate/go-live", - "migrate-to-ory/migrate/faq-migrate", + { + type: "category", + label: "Phase 1: Plan & prepare", + collapsed: true, + collapsible: true, + items: [ + "migrate-to-ory/migrate/index", + "migrate-to-ory/migrate/map-to-orycap", + "migrate-to-ory/migrate/migrate-strategies", + "migrate-to-ory/migrate/faq-migrate", + ] + }, + { + type: "category", + label: "Phase 2: Migrate", + collapsed: true, + collapsible: true, + items: [ + "migrate-to-ory/migrate/create-project", + "migrate-to-ory/migrate/design-id-schema", + "migrate-to-ory/migrate/integrate-frontend", + "migrate-to-ory/migrate/integrate-backend", + "migrate-to-ory/migrate/migrate-identities", + ] + }, + { + type: "category", + label: "Phase 3: Test & validate", + collapsed: true, + collapsible: true, + items: [ + "migrate-to-ory/migrate/test-validate", + ] + }, + { + type: "category", + label: "Phase 4: Go live", + collapsed: true, + collapsible: true, + items: [ + "migrate-to-ory/migrate/go-live", + ] + }, ], }, "migrate-to-ory/auth0", From 6f448c2b0e2ee156ec1da0d41440fed52ec28cdd Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Wed, 5 Nov 2025 10:30:22 -0800 Subject: [PATCH 06/32] Update docs/migrate-to-ory/migrate/create-project.mdx Co-authored-by: Jonas Hungershausen --- docs/migrate-to-ory/migrate/create-project.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migrate-to-ory/migrate/create-project.mdx b/docs/migrate-to-ory/migrate/create-project.mdx index 7d8853f87..3b8debde3 100644 --- a/docs/migrate-to-ory/migrate/create-project.mdx +++ b/docs/migrate-to-ory/migrate/create-project.mdx @@ -8,7 +8,7 @@ sidebar_position: 3 Now that you have chosen your migration strategy, you can begin the actual migration process by setting up your Ory Network projects in a development, staging, and production environment. -You can create a new Ory Network project using the Ory CLI. The command ory create project allows you to specify the environment +You can create a new Ory Network project using the Ory CLI. The command `ory create project` allows you to specify the environment of the project, the output format, the name of the project, and the workspace to use. More details about creating a project can be found [here](../../cli/ory-create-project). From b80511d9ecd9ca4eade249c055069faa4315e666 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Fri, 7 Nov 2025 22:54:19 -0800 Subject: [PATCH 07/32] docs: final review comments --- docs/migrate-to-ory/auth0.mdx | 4 +- .../migrate-to-ory/migrate/create-project.mdx | 19 +- .../migrate/design-id-schema.mdx | 4 +- docs/migrate-to-ory/migrate/go-live.mdx | 8 +- docs/migrate-to-ory/migrate/index.mdx | 44 +++-- .../migrate/integrate-backend.mdx | 19 +- .../migrate/integrate-frontend.mdx | 34 ++-- docs/migrate-to-ory/migrate/map-to-orycap.mdx | 12 +- .../migrate/migrate-identities.mdx | 36 ++-- .../migrate/migrate-strategies.mdx | 166 ++++++++++++------ docs/migrate-to-ory/migrate/test-validate.mdx | 27 +-- src/sidebar.ts | 2 +- 12 files changed, 233 insertions(+), 142 deletions(-) diff --git a/docs/migrate-to-ory/auth0.mdx b/docs/migrate-to-ory/auth0.mdx index 792adbb40..d54d735b8 100644 --- a/docs/migrate-to-ory/auth0.mdx +++ b/docs/migrate-to-ory/auth0.mdx @@ -145,6 +145,4 @@ Follow these steps to import Auth0 users to Ory: ```shell ory list identities --project --workspace - ``` -# FAQ -TODO: Living section for specific questions/answers that Sales get asked but which don't neatly fit within the Auth0 migration process. \ No newline at end of file + ``` \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/create-project.mdx b/docs/migrate-to-ory/migrate/create-project.mdx index 3b8debde3..05a314f55 100644 --- a/docs/migrate-to-ory/migrate/create-project.mdx +++ b/docs/migrate-to-ory/migrate/create-project.mdx @@ -5,12 +5,17 @@ sidebar_label: Create an Ory Network project sidebar_position: 3 --- -Now that you have chosen your migration strategy, you can begin the actual migration process by setting up your Ory Network -projects in a development, staging, and production environment. +Now that you have chosen your migration strategy, you can begin the actual migration process. Perform the migration in a +development or staging environment before migrating to your production environment. This allows you to test and refine the +process without affecting your live data or users. -You can create a new Ory Network project using the Ory CLI. The command `ory create project` allows you to specify the environment -of the project, the output format, the name of the project, and the workspace to use. More details about creating a project can be -found [here](../../cli/ory-create-project). +1. Get an [Ory Network account](https://console.ory.sh/login?flow=c59cbae0-ea41-44e4-b46a-f9e1857be3a2). +1. Install Ory CLI and [set up your local environment](https://www.ory.com/docs/getting-started/local-development) to start +developing with Ory. +1. Create an Ory project and get your Ory project ID. +1. [Set up the necessary dependencies and configurations](https://www.ory.com/docs/identities/get-started/setup) to integrate +Ory's features into your application. +1. Review a [quick start](https://www.ory.com/docs/welcome) for your framework. -Before migrating to your production environment, perform the migration in a development or staging environment. This allows you to -test and refine the process without affecting your live data or users. \ No newline at end of file +You can use the Ory CLI to specify the environment of the project, the output format, the name of the project, and the workspace to use. +See the [Ory CLI Reference](../../cli/ory-create-project) for the `ory create project` command and additional options. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/design-id-schema.mdx b/docs/migrate-to-ory/migrate/design-id-schema.mdx index 9000ecbf1..32506fd8b 100644 --- a/docs/migrate-to-ory/migrate/design-id-schema.mdx +++ b/docs/migrate-to-ory/migrate/design-id-schema.mdx @@ -9,8 +9,8 @@ To align identities from your current system with Ory, you can customize the ide This ability to customize the identity schema means you can enhance and improve your old identity schema during the migration process. The identity schema, which implements the JSON Schema standard, defines the types of data the system can store for users—such as -names, email addresses, phone numbers, or birthdays—and controls business logic by specifying which fields serve as login -identifiers and which are used for verification or recovery. +names, email addresses, phone numbers, or birthdays. It also controls business logic by specifying which fields serve as login +identifiers and which are used for verification or recovery. Identities have two main data types: traits (attributes that users can modify themselves, such as username or email address) and metadata (attributes defined by system admins that can only be updated through admin APIs. Metadata is useful for storing diff --git a/docs/migrate-to-ory/migrate/go-live.mdx b/docs/migrate-to-ory/migrate/go-live.mdx index 77bb34b05..62383bc8e 100644 --- a/docs/migrate-to-ory/migrate/go-live.mdx +++ b/docs/migrate-to-ory/migrate/go-live.mdx @@ -17,8 +17,10 @@ live with Ory in your production environment. periods. Communicate the planned migration to your users in advance, including any expected downtime or changes they should be aware of. 1. Monitor the transition: As you switch over to Ory, closely monitor the system for any issues, such as failed authentications, - performance bottlenecks, or user complaints. Use live events to monitor the system under + performance bottlenecks, or user complaints. To monitor the system, view live events at . -1. Optimize and refine: After the go-live, continue to monitor the system and gather user feedback. +1. Optimize and refine: After the go-live, continue to monitor the system, gather user feedback, and make adjustments as needed to +improve performance and user experience. -Once your Ory integration is stable and users are successfully authenticating with the new system, your migration is complete. \ No newline at end of file +Once your Ory integration is stable and users are successfully authenticating with the new system, your migration is complete. +Continue to leverage Ory's features to enhance your authentication and identity management over time. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index 62e49fd96..17ae9fcc6 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -7,26 +7,29 @@ sidebar_position: 1 # Understand your current IAM system Before you can migrate smoothly, you need a complete picture of how your identity management system works today. This step ensures -nothing gets missed — from everyday login flows to rare edge cases — and sets the foundation for mapping existing functionality to +nothing gets missed—from everyday login flows to rare edge cases—and sets the foundation for mapping existing functionality to Ory Network's equivalent capabilities. :::info Why it matters -Not understanding your existing system’s behavior is the #1 cause of unexpected regressions during cutover. With Ory Network, you -gain full control, and with it, the ability to shape your system’s flows exactly to your needs. - +Mapping your full login lifecycle is the best way to de-risk migration. Your current IAM system may be abstracting away key +functionality without you realizing it. With Ory Network, you gain full control to shape and optimize every flow to your needs. ::: +## Identify your IAM scenario Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific IAM needs and understand the unique requirements of each approach. Each scenario differs in complexity and implementation needs. Use these IAM scenarios to map the identity flows for your application. -## Identify your IAM scenario -- [CIAM](#ciam-customer-identity-and-access-management) -- [B2B](#b2b-business-to-business) -- [Workforce](#workforce-business-to-enterprise) -- [Agentic AI](#agentic-ai) - + + ### CIAM (Customer Identity and Access Management) @@ -38,6 +41,9 @@ Your company sells products or services directly to individual consumers. - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users + + + ### B2B (Business-to-Business) Your company sells products or services directly to other businesses rather than individual consumers. Your customers are organizations @@ -51,6 +57,8 @@ that use these products or services to run their own operations. - Role-based permissions and API controls - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users + + ### Workforce (Business-to-Enterprise) @@ -67,6 +75,8 @@ existing enterprise identity providers and other 3rd party systems, and streamli - Zero-trust security, MFA, and SSO for enterprise applications - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users + + ### Agentic AI @@ -76,16 +86,18 @@ clients (AI applications) that discover and use those resources. #### Key IAM requirements - Standardized protocol that works across many tools and data sources - Built-in authentication and access control + + ## Map all identity flows in your application -Build a complete picture of every identity-related process in your system. Use your IAM scenario’s Key IAM requirements to identify +Document every identity-related (authentication and authorization) process in your system. Use your IAM scenario’s Key IAM requirements to identify these flows. This ensures you don’t miss critical flows during migration. 1. Identify all entry points where an identity-related process occurs (e.g., web app login, mobile app sign-in, API tokens, social or enterprise sign-ins). 1. Create a comprehensive inventory of flows, for example: - - Registration/sign-up - - Sign-in/sign-out + - Registration + - Sign-in and sign-out - Multi-Factor Authentication (MFA) - Password reset and account recovery - Account linking (social, enterprise logins) @@ -93,11 +105,11 @@ these flows. This ensures you don’t miss critical flows during migration. - Token refresh and session handling - Recovery flows, consent screens, or partner-specific integrations 1. Create flow diagrams (sequence diagrams or flow charts) to surface dependencies and hidden complexity. -1. Note where identity-related (authentication and authorization) processes interact with other systems (databases, CRMs, partner apps, or external APIs). +1. Note where identity-related processes interact with other systems (databases, CRMs, partner apps, or external APIs). -At the end of this process you should have a living document with diagrams that capture: +At the end of this process you should have a living document (one that you'll update as you discover more) with diagrams that capture: -- All identity-related (authentication and authorization) flows +- All identity-related flows - Your system's existing functionality - Any existing dependencies on external systems - Edge cases requiring special handling diff --git a/docs/migrate-to-ory/migrate/integrate-backend.mdx b/docs/migrate-to-ory/migrate/integrate-backend.mdx index a29698aa9..40062d8a3 100644 --- a/docs/migrate-to-ory/migrate/integrate-backend.mdx +++ b/docs/migrate-to-ory/migrate/integrate-backend.mdx @@ -6,11 +6,11 @@ sidebar_position: 1 --- When the frontend makes an API call to your backend, it will include the necessary cookies. Your backend must then forward these -cookies when calling the Ory API to validate the session. For example in a Go backend, you could use a +cookies when calling the Ory API to validate the session. For example, in a Go backend, you could use a [middleware](../../getting-started/integrate-auth/go#validate-and-login) to intercept API requests and validate the session by -calling Ory’s toSession() method. Ensure that the cookies received from the front end are forwarded in this call. Since backend -calls to Ory’s API won’t automatically include cookies, you must manually attach the relevant cookies to these requests. This is -important for the backend to be able to check the session. +calling Ory’s `toSession()` method. Ensure that the cookies received from the frontend are forwarded in this call. Since backend +calls to Ory’s API won’t automatically include cookies, you must manually attach the relevant cookies to these requests. This +allows the backend to validate the session. When using Ory to manage identities, it is best practice to store business logic in your application database and keep only authentication-relevant data in Ory. Here’s a general approach: @@ -21,8 +21,9 @@ authentication-relevant data in Ory. Here’s a general approach: link Ory-managed identities with your business logic. 1. Establish a connection between the Ory identity and the user record in your database by storing the `user.id` in `identity.metadata_public.id`. This ensures that subsequent API calls can easily map the Ory identity to the correct internal - user. More about metadata in the [Identity metadata & traits ](../../kratos/manage-identities/managing-users-identities-metadata) - documentation. -1. Now when the frontend makes API calls containing the Ory cookie or token, the backend should verify the session using the - whoami API endpoint. This endpoint returns the session details, including the identity, allowing the backend to authenticate - the request and link it to the internal user record. \ No newline at end of file + user. See [Identity metadata & traits ](../../kratos/manage-identities/managing-users-identities-metadata) documentation for details. +1. When the frontend makes API calls containing the Ory cookie or token, the backend should verify the session using the + [`whoami`](https://www.ory.com/docs/kratos/reference/api#tag/frontend/operation/toSession) API endpoint. This endpoint returns the session details, including the identity, allowing the backend to authenticate + the request and link it to the internal user record. + + \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/integrate-frontend.mdx b/docs/migrate-to-ory/migrate/integrate-frontend.mdx index 41a44ce6d..e50a489cf 100644 --- a/docs/migrate-to-ory/migrate/integrate-frontend.mdx +++ b/docs/migrate-to-ory/migrate/integrate-frontend.mdx @@ -9,23 +9,31 @@ To make authenticated API calls using Ory, start by properly configuring your do is set to the root domain (e.g., example.org) when you add a custom domain. This ensures that cookies can be shared across all subdomains. -- Example subdomain structure: - - Run Ory at auth.example.org. - - Host your backend API at api.example.org. - - Serve your frontend UI at www.example.org or another designated subdomain. +Example subdomain structure: +- Run Ory at auth.example.org. +- Host your backend API at api.example.org. +- Serve your frontend UI at www.example.org or another designated subdomain. -This setup allows both your front end and back end to access the authentication session cookies managed by Ory. +This setup allows both your frontend and backend applications to access the authentication session cookies managed by Ory. -To begin integrating Ory into your frontend, it's helpful to start with the +To begin integrating Ory into your frontend, start with the ["protect a page with login" guides](../../getting-started/overview) that cover the basics of developing with Ory for various programming languages and frameworks, including SDK usage and essential setup steps. +## Account Experience + Ory Network has two types of user interfaces. We recommend starting with the built-in [Account Experience](../../account-experience/index.mdx), which offers a standard user interface, covering all self-service flows -with the option to style branding to get you up and running. If you prefer a custom user interface that matches your current -design 1:1, Ory allows you to create and style a custom UI that integrates seamlessly with your existing setup. You can do this -using the API directly, the SDK for your language, or - if you are working in the React ecosystem - Ory Elements. Ory Elements is -a component library designed to make building login, registration, and account pages for Ory easy. It is modular and customizable, -allowing you to use only the components you need while tailoring them to fit your implementation's design. The UI created with Ory -Elements changes dynamically to adapt to your Ory Network configuration. More details about customizing the user interface with -Ory Elements can be found [here](../../elements/index.mdx). \ No newline at end of file +with the option to style branding to get you up and running. + +## Custom user interface + +If you prefer a custom user interface that matches your current design exactly, Ory allows you to create and style a custom UI that +integrates seamlessly with your existing setup. You can do this using the API directly, the SDK for your language, or, if you are working +in the React ecosystem, Ory Elements. + +### Ory Elements + +[Ory Elements](../../elements/index.mdx) is a component library designed to make building login, registration, and account +pages for Ory easy. It is modular and customizable, allowing you to use only the components you need while tailoring them to fit your +implementation's design. The UI created with Ory Elements changes dynamically to adapt to your Ory Network configuration. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/map-to-orycap.mdx b/docs/migrate-to-ory/migrate/map-to-orycap.mdx index f8bf736d0..56de58f05 100644 --- a/docs/migrate-to-ory/migrate/map-to-orycap.mdx +++ b/docs/migrate-to-ory/migrate/map-to-orycap.mdx @@ -6,12 +6,14 @@ sidebar_position: 1 --- Using your list of identity-related flows, create a side-by-side table to map existing functionality to Ory Network’s equivalent -capabilities. While the majority of the time you'll find your existing functionality neatly maps to Ory's capabilities, now is the best time to -identify when it does not. Some examples: +capabilities. While most of the time you'll find your existing functionality neatly maps to Ory's capabilities, this is +the best time to identify when it does not. Some examples: -- Not all vendors strickly comply with standards, whereas Ory does, so you might discover you need to -change how you implement functionality to be compliant. -- You might have a unique use case to solve that requires additional help from our support. + + +- Not all vendors strictly comply with standards, whereas Ory does, so you might need to change how you implement functionality +to be compliant with standards +- You might have a unique use case that requires additional help from our support team Table 1: An example of mapping functionality diff --git a/docs/migrate-to-ory/migrate/migrate-identities.mdx b/docs/migrate-to-ory/migrate/migrate-identities.mdx index 5119be06e..0201128bc 100644 --- a/docs/migrate-to-ory/migrate/migrate-identities.mdx +++ b/docs/migrate-to-ory/migrate/migrate-identities.mdx @@ -5,33 +5,33 @@ sidebar_label: Migrate your existing identities sidebar_position: 3 --- With authentication now set up on your frontend and backend, the next step is to prepare your existing user identities for -migration. You should have already chosen your identity migration strategy in [Phase 1: Plan and prepare](http://localhost:3002/docs/migrate-to-ory/migrate/migrate-strategies); -"bulk" or "graceful" migration. You will now implement your chosen identity migration strategy. +migration. You should have already chosen your identity migration strategy in [Choose your IAM migration strategy](https://www.ory.com/docs/migrate-to-ory/migrate/migrate-strategies): +bulk or graceful identity migration. You will now implement your chosen identity migration strategy. -## Bulk import of identities +- For Auth0 migrations, see the dedicated [Migrate user identities from Auth0 to Ory](../migrate-from-auth0) guide. + +## Bulk identity migration -If you're using a managed identity solution, it’s a good idea to start the export of existing identities early, especially +If you're using a managed identity solution, start the export of existing identities early, especially if there's no straightforward way to export the identities and you might need to go through a support process. Identify the -hashing algorithm used for credentials—if your passwords aren't hashed, Ory handles hashing automatically during import. -If Ory supports your hashing algorithm, use the [create identity API](../../reference/api#tag/identity/operation/createIdentity) +hashing algorithm used for credentials. If your passwords aren't hashed, Ory handles hashing automatically during import. +If Ory supports your hashing algorithm, use the [`createIdentity`](https://www.ory.com/docs/reference/api#tag/identity/operation/createIdentity) API to import users. If the hashing algorithm isn't supported or if you can't get the hashed passwords from your current -authentication system, you may want to do a "graceful" migration. +authentication system, you may want to do a graceful identity migration. -The create identity API supports bulk imports with a maximum of 1000 identities per request—larger imports require multiple -requests. The endpoint accepts a JSON array of identities, each of which must have a create property that holds the identity -that should be created. See the [Import identities ](../../kratos/manage-identities/25_import-user-accounts-identities.mdx) +The `createIdentity` API supports bulk imports with a maximum of 1000 identities per request—larger imports require multiple +requests. The endpoint accepts a JSON array of identities, each of which must have a create property containing the identity +data. See the [Import identities ](../../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation for complete details. -- For Auth0 migrations, see the dedicated [Migrate user identities from Auth0 to Ory](../migrate-from-auth0) guide. - -## Graceful import of identities +## Graceful identity migration Ory does not support the direct import of active sessions from your existing system. To ensure that users with active sessions -from the old system can continue accessing your services without needing to reauthenticate immediately, implement a transition -period during which both the old and new systems operate concurrently. During this period, use the [password migration hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook) -to migrate your existing users. All user authentication flows—including login, registration, password recovery, and settings management—should be +can continue accessing your services without needing to reauthenticate immediately, implement a transition +period where both systems operate concurrently. During this period, use the [password migration hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook) +to migrate existing users. All user authentication flows—including login, registration, password recovery, and settings management—should be managed by Ory. However, your backend must be configured to recognize and accept sessions from both the old system and Ory. As the transition progresses, gradually phase out the old system. Once most or all active sessions from the old system have expired or been replaced by new sessions in Ory, you can complete the migration to Ory exclusively. This gradual approach -minimizes user disruption and provides your development team with the time needed to resolve any potential issues that may arise -during the migration. \ No newline at end of file +minimizes user disruption and provides your development team with time to resolve any issues that arise +during the migration. diff --git a/docs/migrate-to-ory/migrate/migrate-strategies.mdx b/docs/migrate-to-ory/migrate/migrate-strategies.mdx index 3ba8719a7..0123c6b4c 100644 --- a/docs/migrate-to-ory/migrate/migrate-strategies.mdx +++ b/docs/migrate-to-ory/migrate/migrate-strategies.mdx @@ -4,31 +4,119 @@ title: Choose your IAM migration strategy sidebar_label: Choose your IAM migration strategy sidebar_position: 2 --- -This section covers both the strategies for migrating user identities to Ory and the broader considerations for -rolling out the Ory IAM system across all your applications. While user identity migration focuses on transferring user -accounts, credentials, and profile data, the IAM rollout determines how and when each application transitions to using Ory -for authentication and authorization. Understanding both dimensions is essential for planning a successful transition—the -strategy you choose for migrating user identities may differ for each phase in which you roll out the Ory IAM functionality across your -application portfolio. +This section covers two aspects of migrating to Ory: transferring user identities and rolling out IAM functionality to your +applications. User identity migration involves moving user accounts, credentials, and profile data from your existing system +to Ory. IAM rollout determines the sequence and timing for transitioning each application to use Ory for authentication and +authorization. -Your choice of migration strategy depends on your specific requirements, technical constraints, and tolerance for user impact. -It should be driven by several key factors: the complexity of your system, the size of your user base, and your organization's -risk tolerance and downtime constraints. +These two aspects are distinct but interconnected. You might choose different identity migration strategies for different +applications or rollout phases. For example, you could use bulk identity migration for your first application in a single-phased +rollout, and later use graceful identity migration for subsequent multi-phased rollouts. Understanding both aspects helps you design a +transition plan that balances technical constraints, business priorities, and user impact. Your choice of approach should be +driven by several key factors: the complexity of your system, the size of your user base, and your organization's risk +tolerance and downtime constraints. -## Choosing your user identity migration strategy +## Choose your IAM system rollout strategy + +Simpler systems with homogeneous user segments and a single legacy authentication system can be implemented in a single-phase IAM system rollout. +However, complex systems or risk-averse organizations may benefit from a multi-phased, application-based, or phased rollout approach. This strategy +allows you to test processes in production by first rolling out to less critical user segments or applications, learning valuable lessons to +refine your approach before a broader rollout. + + + + +### Single-phase rollout + +A single-phase approach involves migrating user identities and rolling out applications/services in a single "go-live" event. +This approach results in all users and applications/services being impacted at the same time. The single-phase rollout requires +you to perform a bulk identity migration. + +A single-phase rollout approach is the best choice when: + +- The number of users is low and applications or services are simple +- Planned system downtime due to the "go-live" event isn't a problem +- You need to retire the current solution soon + +#### Advantages of single-phase rollout + +- Faster time to value: All users gain access to the new system immediately rather than waiting months or years for their turn. +- Simplifies planning: You only need to plan, execute, and close out one major deployment rather than coordinating multiple +phases with interdependencies, handoffs, and varying timelines. +- Lower total cost: Running one migration event typically costs less than maintaining parallel systems, conducting multiple training sessions, +and supporting both old and new systems across an extended timeline. +- Eliminates integration complexity: You avoid the technical challenges and data synchronization issues that arise when different user groups +operate on different systems that need to communicate with each other. + +#### Drawbacks of single-phase rollout + +- Higher risk concentration: If something goes wrong during deployment, it impacts your entire organization simultaneously rather than being +contained to a smaller group, potentially causing widespread business disruption. +- Support burden: Your support team must handle issues from all users at once, which can lead to long resolution times, frustrated +users, and inability to provide quality assistance when everyone needs help simultaneously. +- Limited learning opportunity: You can't incorporate lessons learned from early adopters because there are no early adopters—any design flaws, +training gaps, or technical issues only become apparent when it's too late to adjust your approach. +- Difficult rollback: Reverting to the old system after a full cutover can be complex and potentially impossible if you've already +decommissioned infrastructure or migrated data irreversibly. + + + +### Multi-phase rollout + +A multi-phase approach involves migrating user identities and rolling out applications/services in phases, focusing on specific applications, +services, or user segments. This approach results in multiple "go-lives", each affecting a defined group of users or applications/services. +A multi-phase approach can employ bulk or graceful identity imigration strategies, or a hybrid of both. The key difference is that a +specific segment of users or authenication for a specific application or service is rolled out separately, each in its own specific phase. + +A multi-phase rollout is the best choice in most cases, especially when: + +- You manage multiple enterpise-level applications with different underlying authentication systems +- You manage diverse segments of users with different underlying authentication systems +- You have a complex system, and you want to reduce the risk of unknown consequences and improve the process iteratively. + +#### Advantages of multi-phase rollout + +- Reduced risk: By rolling out in phases, issues are isolated to specific apps or user segments. +- Flexibility: Allows for adjustments and optimizations between phases based on lessons learned. +- Minimized/no downtime: Since the rollout occurs in stages, downtime can be limited to smaller user groups or avoided + completely. + +#### Drawbacks of multi-phase rollout + +- Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. +- Extended timeline: The time to roll out the whole system takes longer as it is broken down into phases. +- Resource demands: Running both systems in parallel during the transition can strain resources. + + + +## Choose your user identity migration strategy Ory supports two primary identity migration strategies: -- **Bulk migration** - Migrate all users at once. -- **Graceful migration** - Migrate when a user authenticates, running old and new solutions in parallel. +- **Bulk identity migration** - Migrate all users at once. +- **Graceful identity migration** - Migrate when a user authenticates, running old and new solutions in parallel. -By carefully understanding these methods and planning accordingly, you can ensure a smooth and secure transition for your users to +By understanding these methods and planning accordingly, you can ensure a smooth and secure transition for your users to Ory Network. + + + ### Bulk identity migration -In a bulk identity migration, also known as "big bang" or "offline" migration, all user data is migrated at once, and a single -"go-live" event is scheduled, where all users start using the new system simultaneously. +In a bulk identity migration, also known as big-bang or offline migration, all user data is migrated at once. The identity +migration must happen in close coordination with the overall rollout. This creates a single "go-live" event, where all +users included in the bulk identity migration start using the new system simultaneously. #### When to use bulk identity migration A bulk identity migration can involve some risk and downtime, but it is recommended when: @@ -36,26 +124,28 @@ A bulk identity migration can involve some risk and downtime, but it is recommen - The number of users is low and applications or services are simple - Planned system downtime due to the "go-live" event isn't a problem - You need to retire the current solution soon -- Your legacy IAM vendor restricts your ability to use a graceful identity migration strategy (for example, Auth0). +- Your legacy IAM vendor restricts your ability to use a graceful identity migration strategy. ##### Advantages of bulk identity migration -- Simplicity: Since there is only one "go-live", the identity migration process is easier to manage and plan. -- Time efficiency: The identity migration process happens in one go, reducing the transition time. +- Simplifies planning: Since there is only one "go-live", the identity migration process is easier to manage and plan. +- Reduces transition time: The identity migration process happens in one go, reducing the transition time. - Less complexity: There is no need to run two systems in parallel and you can retire the previous solution immediately after testing and validating the migration. -##### Drawbacks of bulk identitiy migration +##### Drawbacks of bulk identity migration -- High risk: If any issues occur during the cutover, the impact can be significant, affecting all users. If might be difficult +- High risk: If any issues occur during the cutover, the impact can be significant, affecting all users. It might be difficult to perform a rollback. - Downtime: This approach may require planned system downtime to ensure data consistency, which can disrupt users. - Increased preparation: Requires extensive planning and testing to mitigate risks, making it more resource-intensive during that phase. + + ### Graceful identity migration -Graceful identity migration - also called "automatic", "trickle", "just-in-time", or "online" migration - involves running both the old and +Graceful identity migration—also called automatic, trickle, just-in-time, or online migration—involves running both the old and new systems in parallel, gradually migrating user identities as the user authenticates. This approach features two "go-lives": the initial application "go-live" and subsequent user-specific cutovers during login. @@ -82,35 +172,5 @@ ensure identites are synchronized in both systems in case a rollback is required additional coding effort to implement Ory's [password migration using a web hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook). - Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the old and new systems during the transition period. - -## Choosing your IAM system rollout strategy - -Simpler systems with homogenous segments of users and only one legacy authenication system to migrate from can be migrated in -one IAM system rollout. However, complex systems or risk-averse organizations may benefit from a "stepwise", "application-based", -or a "phased" rollout approach. This allows you to test processes in production by migrating less critical user segments or -applications first, learning valuable lessons to refine your approach before a broader rollout. - -A stepwise approach involves migrating both user identities and applications/services in phases, focusing on specific applications, -services, or user segments. This approach results in multiple "go-lives", each affecting a defined group of users or applications/services. -A stepwise approach can employ bulk migration or graceful migration strategies, or a hybrid of both. The key difference is that a -specific segment of users or authenication for a specific application or service is migrated separately, each in its own specific phase. - -### When to use a stepwise rollout approach -A stepwise approach is the best choice in most cases, especially when: - -- You manage multiple enterpise-level applications with different underlying authentication systems -- You manage diverse segments of users with different underlying authentication systems -- You have a complex system, and you want to reduce the risk of unknown consequences and improve the process iteratively. - -### Advantages of stepwise rollout approach - -- Reduced risk: By migrating in phases, issues are isolated to specific apps or user segments. -- Flexibility: Allows for adjustments and optimizations between phases based on lessons learned. -- Minimized/no downtime: Since the migration occurs in stages, downtime can be limited to smaller user groups or avoided - completely. - -### Drawbacks of stepwise rollout approach - -- Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. -- Extended timeline: The time to migrate the whole system takes longer as it is broken down into phases. -- Resource demands: Running both systems in parallel during the transition can strain resources. \ No newline at end of file + + \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/test-validate.mdx b/docs/migrate-to-ory/migrate/test-validate.mdx index db33da9b0..a73e6a71e 100644 --- a/docs/migrate-to-ory/migrate/test-validate.mdx +++ b/docs/migrate-to-ory/migrate/test-validate.mdx @@ -9,21 +9,24 @@ to ensure your authentication system works securely and reliably. You should tes staging, and production environments, validating that configurations, custom domains, and integrations work correctly at each stage. -Create a comprehensive list of testing requirements. For example: +## Create a comprehensive list of testing requirements -- Authentication Flows: Login, registration, password reset, account recovery -- Session Management: Creation, validation, expiration, security -- Data Integrity: User profiles, metadata synchronization, permissions -- API Functionality: All protected routes, authorization checks -- User Interface: Forms, redirects, error handling, responsive design +Your testing should cover: + +- Authentication flows: Login, registration, password reset, account recovery +- Session management: Creation, validation, expiration, security +- Data integrity: User profiles, metadata synchronization, permissions +- API functionality: All protected routes, authorization checks +- User interface: Forms, redirects, error handling, responsive design - Performance: Response times, concurrent user handling, load capacity - Security: CSRF protection, session security, data encryption -## Migration Validation Process -Implement systematic validation to ensure migration completeness, data accuracy, and functional parity with existing systems. -Compare user counts between systems, validate authentication flows for sample users, and verify all business logic continues -to function correctly. +## Implement systematic validation to ensure migration completeness + +Your validation should cover: -## User Acceptance Testing -Conduct thorough user acceptance testing with representative user groups to ensure the migrated system meets usability +- Compare user counts between systems (should match within acceptable margin) +- Validate authentication flows for representative sample users +- Verify all business logic continues to function correctly +- Conduct user acceptance testing with representative user groups to ensure the migrated system meets usability requirements and maintains expected functionality. \ No newline at end of file diff --git a/src/sidebar.ts b/src/sidebar.ts index b6560b2e9..931d5c0e6 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -212,7 +212,7 @@ const quickstart: SidebarItemsConfig = [ "migrate-to-ory/migrate/index", "migrate-to-ory/migrate/map-to-orycap", "migrate-to-ory/migrate/migrate-strategies", - "migrate-to-ory/migrate/faq-migrate", + //"migrate-to-ory/migrate/faq-migrate", ] }, { From e1650cfe15cef9d687e17bf5d3fe806fc9bf0f13 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Fri, 7 Nov 2025 23:22:04 -0800 Subject: [PATCH 08/32] chore: format --- docs/migrate-to-ory/auth0.mdx | 2 +- .../migrate-to-ory/migrate/create-project.mdx | 18 +-- .../migrate/design-id-schema.mdx | 33 ++--- docs/migrate-to-ory/migrate/faq-migrate.mdx | 2 +- docs/migrate-to-ory/migrate/go-live.mdx | 8 +- docs/migrate-to-ory/migrate/index.mdx | 68 +++++----- .../migrate/integrate-backend.mdx | 12 +- .../migrate/integrate-frontend.mdx | 21 +-- docs/migrate-to-ory/migrate/map-to-orycap.mdx | 18 ++- .../migrate/migrate-identities.mdx | 47 +++---- .../migrate/migrate-strategies.mdx | 121 +++++++++--------- .../migrate-to-ory/migrate/migrate-to-ory.mdx | 53 ++++---- docs/migrate-to-ory/migrate/test-validate.mdx | 14 +- src/sidebar.ts | 30 ++--- 14 files changed, 233 insertions(+), 214 deletions(-) diff --git a/docs/migrate-to-ory/auth0.mdx b/docs/migrate-to-ory/auth0.mdx index d54d735b8..2ee8d8fc8 100644 --- a/docs/migrate-to-ory/auth0.mdx +++ b/docs/migrate-to-ory/auth0.mdx @@ -145,4 +145,4 @@ Follow these steps to import Auth0 users to Ory: ```shell ory list identities --project --workspace - ``` \ No newline at end of file + ``` diff --git a/docs/migrate-to-ory/migrate/create-project.mdx b/docs/migrate-to-ory/migrate/create-project.mdx index 05a314f55..ee24d5c9c 100644 --- a/docs/migrate-to-ory/migrate/create-project.mdx +++ b/docs/migrate-to-ory/migrate/create-project.mdx @@ -5,17 +5,17 @@ sidebar_label: Create an Ory Network project sidebar_position: 3 --- -Now that you have chosen your migration strategy, you can begin the actual migration process. Perform the migration in a -development or staging environment before migrating to your production environment. This allows you to test and refine the -process without affecting your live data or users. +Now that you have chosen your migration strategy, you can begin the actual migration process. Perform the migration in a +development or staging environment before migrating to your production environment. This allows you to test and refine the process +without affecting your live data or users. 1. Get an [Ory Network account](https://console.ory.sh/login?flow=c59cbae0-ea41-44e4-b46a-f9e1857be3a2). -1. Install Ory CLI and [set up your local environment](https://www.ory.com/docs/getting-started/local-development) to start -developing with Ory. +1. Install Ory CLI and [set up your local environment](https://www.ory.com/docs/getting-started/local-development) to start + developing with Ory. 1. Create an Ory project and get your Ory project ID. -1. [Set up the necessary dependencies and configurations](https://www.ory.com/docs/identities/get-started/setup) to integrate -Ory's features into your application. +1. [Set up the necessary dependencies and configurations](https://www.ory.com/docs/identities/get-started/setup) to integrate + Ory's features into your application. 1. Review a [quick start](https://www.ory.com/docs/welcome) for your framework. -You can use the Ory CLI to specify the environment of the project, the output format, the name of the project, and the workspace to use. -See the [Ory CLI Reference](../../cli/ory-create-project) for the `ory create project` command and additional options. \ No newline at end of file +You can use the Ory CLI to specify the environment of the project, the output format, the name of the project, and the workspace +to use. See the [Ory CLI Reference](../../cli/ory-create-project) for the `ory create project` command and additional options. diff --git a/docs/migrate-to-ory/migrate/design-id-schema.mdx b/docs/migrate-to-ory/migrate/design-id-schema.mdx index 32506fd8b..abbc7aa23 100644 --- a/docs/migrate-to-ory/migrate/design-id-schema.mdx +++ b/docs/migrate-to-ory/migrate/design-id-schema.mdx @@ -5,33 +5,34 @@ sidebar_label: Design your identity schema sidebar_position: 3 --- -To align identities from your current system with Ory, you can customize the identity schema to meet your specific requirements. -This ability to customize the identity schema means you can enhance and improve your old identity schema during the migration process. +To align identities from your current system with Ory, you can customize the identity schema to meet your specific requirements. +This ability to customize the identity schema means you can enhance and improve your old identity schema during the migration +process. -The identity schema, which implements the JSON Schema standard, defines the types of data the system can store for users—such as -names, email addresses, phone numbers, or birthdays. It also controls business logic by specifying which fields serve as login +The identity schema, which implements the JSON Schema standard, defines the types of data the system can store for users—such as +names, email addresses, phone numbers, or birthdays. It also controls business logic by specifying which fields serve as login identifiers and which are used for verification or recovery. -Identities have two main data types: traits (attributes that users can modify themselves, such as username or email address) -and metadata (attributes defined by system admins that can only be updated through admin APIs. Metadata is useful for storing -information like subscription status, legacy user IDs, or basic roles). Metadata comes in two forms: public (visible to -users via session endpoints) and admin (only accessible through admin APIs). +Identities have two main data types: traits (attributes that users can modify themselves, such as username or email address) and +metadata (attributes defined by system admins that can only be updated through admin APIs. Metadata is useful for storing +information like subscription status, legacy user IDs, or basic roles). Metadata comes in two forms: public (visible to users via +session endpoints) and admin (only accessible through admin APIs). ## Best practices for identity schema design: ### Do: -- Use the identity schema for basic profile information that's used across your system, including usernames, email addresses, -phone numbers, first names, and last names +- Use the identity schema for basic profile information that's used across your system, including usernames, email addresses, + phone numbers, first names, and last names - Store authentication-related data like login identifiers and verification addresses in traits - Use metadata fields for system-managed information like legacy user IDs, subscription status, or basic roles ### Don't: -- Store sensitive internal data or information that should be obfuscated from users in the identity schema, since users can -see traits and other data (except credentials) using the `/sessions/whoami` endpoint -- Store business logic or application-specific data like credit card information, shipping addresses, shopping cart items, or -detailed user preferences—keep this in your application database instead +- Store sensitive internal data or information that should be obfuscated from users in the identity schema, since users can see + traits and other data (except credentials) using the `/sessions/whoami` endpoint +- Store business logic or application-specific data like credit card information, shipping addresses, shopping cart items, or + detailed user preferences—keep this in your application database instead -Ory provides preset schemas to help you get started quickly. For detailed guidance on customizing schemas, see the -[identity schemas](../../kratos/manage-identities/identity-schema) documentation. \ No newline at end of file +Ory provides preset schemas to help you get started quickly. For detailed guidance on customizing schemas, see the +[identity schemas](../../kratos/manage-identities/identity-schema) documentation. diff --git a/docs/migrate-to-ory/migrate/faq-migrate.mdx b/docs/migrate-to-ory/migrate/faq-migrate.mdx index e85dd4587..d8a7be9d8 100644 --- a/docs/migrate-to-ory/migrate/faq-migrate.mdx +++ b/docs/migrate-to-ory/migrate/faq-migrate.mdx @@ -5,4 +5,4 @@ sidebar_label: Migration FAQ sidebar_position: 1 --- -TODO: Living section for specific questions/answers that Sales get asked but which don't neatly fit within the migration process. \ No newline at end of file +TODO: Living section for specific questions/answers that Sales get asked but which don't neatly fit within the migration process. diff --git a/docs/migrate-to-ory/migrate/go-live.mdx b/docs/migrate-to-ory/migrate/go-live.mdx index 62383bc8e..0a4833f18 100644 --- a/docs/migrate-to-ory/migrate/go-live.mdx +++ b/docs/migrate-to-ory/migrate/go-live.mdx @@ -17,10 +17,10 @@ live with Ory in your production environment. periods. Communicate the planned migration to your users in advance, including any expected downtime or changes they should be aware of. 1. Monitor the transition: As you switch over to Ory, closely monitor the system for any issues, such as failed authentications, - performance bottlenecks, or user complaints. To monitor the system, view live events at + performance bottlenecks, or user complaints. To monitor the system, view live events at . -1. Optimize and refine: After the go-live, continue to monitor the system, gather user feedback, and make adjustments as needed to -improve performance and user experience. +1. Optimize and refine: After the go-live, continue to monitor the system, gather user feedback, and make adjustments as needed to + improve performance and user experience. Once your Ory integration is stable and users are successfully authenticating with the new system, your migration is complete. -Continue to leverage Ory's features to enhance your authentication and identity management over time. \ No newline at end of file +Continue to leverage Ory's features to enhance your authentication and identity management over time. diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index 17ae9fcc6..928189f52 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -4,22 +4,24 @@ title: Understand your current IAM system sidebar_label: Understand your current IAM system sidebar_position: 1 --- + # Understand your current IAM system -Before you can migrate smoothly, you need a complete picture of how your identity management system works today. This step ensures -nothing gets missed—from everyday login flows to rare edge cases—and sets the foundation for mapping existing functionality to -Ory Network's equivalent capabilities. +Before you can migrate smoothly, you need a complete picture of how your identity management system works today. This step ensures +nothing gets missed—from everyday login flows to rare edge cases—and sets the foundation for mapping existing functionality to Ory +Network's equivalent capabilities. :::info Why it matters -Mapping your full login lifecycle is the best way to de-risk migration. Your current IAM system may be abstracting away key +Mapping your full login lifecycle is the best way to de-risk migration. Your current IAM system may be abstracting away key functionality without you realizing it. With Ory Network, you gain full control to shape and optimize every flow to your needs. ::: ## Identify your IAM scenario -Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific IAM needs and -understand the unique requirements of each approach. Each scenario differs in complexity and implementation needs. Use these IAM -scenarios to map the identity flows for your application. + +Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific IAM needs +and understand the unique requirements of each approach. Each scenario differs in complexity and implementation needs. Use these +IAM scenarios to map the identity flows for your application. + ### Workforce (Business-to-Enterprise) -Your company provides products or services. Your company wants to manage access for a single organization's extended workforce. You want to -consolidate employee user accounts and identities across multi-tenant brands, applications and systems. You need to seamlessly connect with -existing enterprise identity providers and other 3rd party systems, and streamline user onboarding, offboarding, and permission management. +Your company provides products or services. Your company wants to manage access for a single organization's extended workforce. +You want to consolidate employee user accounts and identities across multi-tenant brands, applications and systems. You need to +seamlessly connect with existing enterprise identity providers and other 3rd party systems, and streamline user onboarding, +offboarding, and permission management. #### Key IAM requirements + - Streamline onboarding/offboarding of employee, contractor, and temporary workers - Role-based access aligned with organizational hierarchy - HR system integration with flexible identity schemas @@ -75,39 +80,42 @@ existing enterprise identity providers and other 3rd party systems, and streamli - Zero-trust security, MFA, and SSO for enterprise applications - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users - + ### Agentic AI -Your company wants to enable AI applications to securely connect to data sources and tools. For example, servers that host resources and -clients (AI applications) that discover and use those resources. +Your company wants to enable AI applications to securely connect to data sources and tools. For example, servers that host +resources and clients (AI applications) that discover and use those resources. #### Key IAM requirements + - Standardized protocol that works across many tools and data sources - Built-in authentication and access control - + ## Map all identity flows in your application -Document every identity-related (authentication and authorization) process in your system. Use your IAM scenario’s Key IAM requirements to identify -these flows. This ensures you don’t miss critical flows during migration. +Document every identity-related (authentication and authorization) process in your system. Use your IAM scenario’s Key IAM +requirements to identify these flows. This ensures you don’t miss critical flows during migration. -1. Identify all entry points where an identity-related process occurs (e.g., web app login, mobile app sign-in, API tokens, social or enterprise sign-ins). +1. Identify all entry points where an identity-related process occurs (e.g., web app login, mobile app sign-in, API tokens, social + or enterprise sign-ins). 1. Create a comprehensive inventory of flows, for example: - - Registration - - Sign-in and sign-out - - Multi-Factor Authentication (MFA) - - Password reset and account recovery - - Account linking (social, enterprise logins) - - User profile management - - Token refresh and session handling - - Recovery flows, consent screens, or partner-specific integrations + - Registration + - Sign-in and sign-out + - Multi-Factor Authentication (MFA) + - Password reset and account recovery + - Account linking (social, enterprise logins) + - User profile management + - Token refresh and session handling + - Recovery flows, consent screens, or partner-specific integrations 1. Create flow diagrams (sequence diagrams or flow charts) to surface dependencies and hidden complexity. 1. Note where identity-related processes interact with other systems (databases, CRMs, partner apps, or external APIs). -At the end of this process you should have a living document (one that you'll update as you discover more) with diagrams that capture: +At the end of this process you should have a living document (one that you'll update as you discover more) with diagrams that +capture: - All identity-related flows - Your system's existing functionality @@ -120,4 +128,4 @@ This will serve as your blueprint for planning, designing, testing, and validati import Help from '@site/docs/_common/need-help.mdx' -``` \ No newline at end of file +``` diff --git a/docs/migrate-to-ory/migrate/integrate-backend.mdx b/docs/migrate-to-ory/migrate/integrate-backend.mdx index 40062d8a3..4e8e29ef3 100644 --- a/docs/migrate-to-ory/migrate/integrate-backend.mdx +++ b/docs/migrate-to-ory/migrate/integrate-backend.mdx @@ -9,7 +9,7 @@ When the frontend makes an API call to your backend, it will include the necessa cookies when calling the Ory API to validate the session. For example, in a Go backend, you could use a [middleware](../../getting-started/integrate-auth/go#validate-and-login) to intercept API requests and validate the session by calling Ory’s `toSession()` method. Ensure that the cookies received from the frontend are forwarded in this call. Since backend -calls to Ory’s API won’t automatically include cookies, you must manually attach the relevant cookies to these requests. This +calls to Ory’s API won’t automatically include cookies, you must manually attach the relevant cookies to these requests. This allows the backend to validate the session. When using Ory to manage identities, it is best practice to store business logic in your application database and keep only @@ -21,9 +21,9 @@ authentication-relevant data in Ory. Here’s a general approach: link Ory-managed identities with your business logic. 1. Establish a connection between the Ory identity and the user record in your database by storing the `user.id` in `identity.metadata_public.id`. This ensures that subsequent API calls can easily map the Ory identity to the correct internal - user. See [Identity metadata & traits ](../../kratos/manage-identities/managing-users-identities-metadata) documentation for details. + user. See [Identity metadata & traits ](../../kratos/manage-identities/managing-users-identities-metadata) documentation for + details. 1. When the frontend makes API calls containing the Ory cookie or token, the backend should verify the session using the - [`whoami`](https://www.ory.com/docs/kratos/reference/api#tag/frontend/operation/toSession) API endpoint. This endpoint returns the session details, including the identity, allowing the backend to authenticate - the request and link it to the internal user record. - - \ No newline at end of file + [`whoami`](https://www.ory.com/docs/kratos/reference/api#tag/frontend/operation/toSession) API endpoint. This endpoint returns + the session details, including the identity, allowing the backend to authenticate the request and link it to the internal user + record. diff --git a/docs/migrate-to-ory/migrate/integrate-frontend.mdx b/docs/migrate-to-ory/migrate/integrate-frontend.mdx index e50a489cf..4d989a329 100644 --- a/docs/migrate-to-ory/migrate/integrate-frontend.mdx +++ b/docs/migrate-to-ory/migrate/integrate-frontend.mdx @@ -10,30 +10,31 @@ is set to the root domain (e.g., example.org) when you add a custom domain. This subdomains. Example subdomain structure: + - Run Ory at auth.example.org. - Host your backend API at api.example.org. - Serve your frontend UI at www.example.org or another designated subdomain. This setup allows both your frontend and backend applications to access the authentication session cookies managed by Ory. -To begin integrating Ory into your frontend, start with the -["protect a page with login" guides](../../getting-started/overview) that cover the basics of developing with Ory for various -programming languages and frameworks, including SDK usage and essential setup steps. +To begin integrating Ory into your frontend, start with the ["protect a page with login" guides](../../getting-started/overview) +that cover the basics of developing with Ory for various programming languages and frameworks, including SDK usage and essential +setup steps. ## Account Experience Ory Network has two types of user interfaces. We recommend starting with the built-in [Account Experience](../../account-experience/index.mdx), which offers a standard user interface, covering all self-service flows -with the option to style branding to get you up and running. +with the option to style branding to get you up and running. ## Custom user interface -If you prefer a custom user interface that matches your current design exactly, Ory allows you to create and style a custom UI that -integrates seamlessly with your existing setup. You can do this using the API directly, the SDK for your language, or, if you are working -in the React ecosystem, Ory Elements. +If you prefer a custom user interface that matches your current design exactly, Ory allows you to create and style a custom UI +that integrates seamlessly with your existing setup. You can do this using the API directly, the SDK for your language, or, if you +are working in the React ecosystem, Ory Elements. ### Ory Elements -[Ory Elements](../../elements/index.mdx) is a component library designed to make building login, registration, and account -pages for Ory easy. It is modular and customizable, allowing you to use only the components you need while tailoring them to fit your -implementation's design. The UI created with Ory Elements changes dynamically to adapt to your Ory Network configuration. \ No newline at end of file +[Ory Elements](../../elements/index.mdx) is a component library designed to make building login, registration, and account pages +for Ory easy. It is modular and customizable, allowing you to use only the components you need while tailoring them to fit your +implementation's design. The UI created with Ory Elements changes dynamically to adapt to your Ory Network configuration. diff --git a/docs/migrate-to-ory/migrate/map-to-orycap.mdx b/docs/migrate-to-ory/migrate/map-to-orycap.mdx index 56de58f05..2b29c560b 100644 --- a/docs/migrate-to-ory/migrate/map-to-orycap.mdx +++ b/docs/migrate-to-ory/migrate/map-to-orycap.mdx @@ -5,18 +5,16 @@ sidebar_label: Map your existing functionality to Ory Network capabilities sidebar_position: 1 --- -Using your list of identity-related flows, create a side-by-side table to map existing functionality to Ory Network’s equivalent -capabilities. While most of the time you'll find your existing functionality neatly maps to Ory's capabilities, this is -the best time to identify when it does not. Some examples: +Using your list of identity-related flows, create a side-by-side table to map existing functionality to Ory Network’s equivalent +capabilities. While most of the time you'll find your existing functionality neatly maps to Ory's capabilities, this is the best +time to identify when it does not. Some examples: - - -- Not all vendors strictly comply with standards, whereas Ory does, so you might need to change how you implement functionality -to be compliant with standards +- Not all vendors strictly comply with standards, whereas Ory does, so you might need to change how you implement functionality to + be compliant with standards - You might have a unique use case that requires additional help from our support team Table 1: An example of mapping functionality -| Existing Functionality | Ory Capabilities | -|---|---| -| JSON Web Tokens | [Ory Session cookies/tokens](https://www.ory.sh/docs/identities/session-to-jwt-cors)| +| Existing Functionality | Ory Capabilities | +| ---------------------- | ------------------------------------------------------------------------------------ | +| JSON Web Tokens | [Ory Session cookies/tokens](https://www.ory.sh/docs/identities/session-to-jwt-cors) | diff --git a/docs/migrate-to-ory/migrate/migrate-identities.mdx b/docs/migrate-to-ory/migrate/migrate-identities.mdx index 0201128bc..b0a39b70d 100644 --- a/docs/migrate-to-ory/migrate/migrate-identities.mdx +++ b/docs/migrate-to-ory/migrate/migrate-identities.mdx @@ -4,34 +4,37 @@ title: Migrate your existing identities sidebar_label: Migrate your existing identities sidebar_position: 3 --- + With authentication now set up on your frontend and backend, the next step is to prepare your existing user identities for -migration. You should have already chosen your identity migration strategy in [Choose your IAM migration strategy](https://www.ory.com/docs/migrate-to-ory/migrate/migrate-strategies): -bulk or graceful identity migration. You will now implement your chosen identity migration strategy. +migration. You should have already chosen your identity migration strategy in +[Choose your IAM migration strategy](https://www.ory.com/docs/migrate-to-ory/migrate/migrate-strategies): bulk or graceful +identity migration. You will now implement your chosen identity migration strategy. -- For Auth0 migrations, see the dedicated [Migrate user identities from Auth0 to Ory](../migrate-from-auth0) guide. +- For Auth0 migrations, see the dedicated [Migrate user identities from Auth0 to Ory](../migrate-from-auth0) guide. ## Bulk identity migration -If you're using a managed identity solution, start the export of existing identities early, especially -if there's no straightforward way to export the identities and you might need to go through a support process. Identify the -hashing algorithm used for credentials. If your passwords aren't hashed, Ory handles hashing automatically during import. -If Ory supports your hashing algorithm, use the [`createIdentity`](https://www.ory.com/docs/reference/api#tag/identity/operation/createIdentity) API -to import users. If the hashing algorithm isn't supported or if you can't get the hashed passwords from your current -authentication system, you may want to do a graceful identity migration. +If you're using a managed identity solution, start the export of existing identities early, especially if there's no +straightforward way to export the identities and you might need to go through a support process. Identify the hashing algorithm +used for credentials. If your passwords aren't hashed, Ory handles hashing automatically during import. If Ory supports your +hashing algorithm, use the [`createIdentity`](https://www.ory.com/docs/reference/api#tag/identity/operation/createIdentity) API to +import users. If the hashing algorithm isn't supported or if you can't get the hashed passwords from your current authentication +system, you may want to do a graceful identity migration. -The `createIdentity` API supports bulk imports with a maximum of 1000 identities per request—larger imports require multiple -requests. The endpoint accepts a JSON array of identities, each of which must have a create property containing the identity -data. See the [Import identities ](../../kratos/manage-identities/25_import-user-accounts-identities.mdx) -documentation for complete details. +The `createIdentity` API supports bulk imports with a maximum of 1000 identities per request—larger imports require multiple +requests. The endpoint accepts a JSON array of identities, each of which must have a create property containing the identity data. +See the [Import identities](../../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation for complete +details. ## Graceful identity migration -Ory does not support the direct import of active sessions from your existing system. To ensure that users with active sessions -can continue accessing your services without needing to reauthenticate immediately, implement a transition -period where both systems operate concurrently. During this period, use the [password migration hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook) -to migrate existing users. All user authentication flows—including login, registration, password recovery, and settings management—should be -managed by Ory. However, your backend must be configured to recognize and accept sessions from both the old system and Ory. -As the transition progresses, gradually phase out the old system. Once most or all active sessions from the old system have -expired or been replaced by new sessions in Ory, you can complete the migration to Ory exclusively. This gradual approach -minimizes user disruption and provides your development team with time to resolve any issues that arise -during the migration. +Ory does not support the direct import of active sessions from your existing system. To ensure that users with active sessions can +continue accessing your services without needing to reauthenticate immediately, implement a transition period where both systems +operate concurrently. During this period, use the +[password migration hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook) +to migrate existing users. All user authentication flows—including login, registration, password recovery, and settings +management—should be managed by Ory. However, your backend must be configured to recognize and accept sessions from both the old +system and Ory. As the transition progresses, gradually phase out the old system. Once most or all active sessions from the old +system have expired or been replaced by new sessions in Ory, you can complete the migration to Ory exclusively. This gradual +approach minimizes user disruption and provides your development team with time to resolve any issues that arise during the +migration. diff --git a/docs/migrate-to-ory/migrate/migrate-strategies.mdx b/docs/migrate-to-ory/migrate/migrate-strategies.mdx index 0123c6b4c..2df392a31 100644 --- a/docs/migrate-to-ory/migrate/migrate-strategies.mdx +++ b/docs/migrate-to-ory/migrate/migrate-strategies.mdx @@ -4,24 +4,25 @@ title: Choose your IAM migration strategy sidebar_label: Choose your IAM migration strategy sidebar_position: 2 --- -This section covers two aspects of migrating to Ory: transferring user identities and rolling out IAM functionality to your -applications. User identity migration involves moving user accounts, credentials, and profile data from your existing system -to Ory. IAM rollout determines the sequence and timing for transitioning each application to use Ory for authentication and + +This section covers two aspects of migrating to Ory: transferring user identities and rolling out IAM functionality to your +applications. User identity migration involves moving user accounts, credentials, and profile data from your existing system to +Ory. IAM rollout determines the sequence and timing for transitioning each application to use Ory for authentication and authorization. -These two aspects are distinct but interconnected. You might choose different identity migration strategies for different +These two aspects are distinct but interconnected. You might choose different identity migration strategies for different applications or rollout phases. For example, you could use bulk identity migration for your first application in a single-phased -rollout, and later use graceful identity migration for subsequent multi-phased rollouts. Understanding both aspects helps you design a -transition plan that balances technical constraints, business priorities, and user impact. Your choice of approach should be -driven by several key factors: the complexity of your system, the size of your user base, and your organization's risk +rollout, and later use graceful identity migration for subsequent multi-phased rollouts. Understanding both aspects helps you +design a transition plan that balances technical constraints, business priorities, and user impact. Your choice of approach should +be driven by several key factors: the complexity of your system, the size of your user base, and your organization's risk tolerance and downtime constraints. ## Choose your IAM system rollout strategy -Simpler systems with homogeneous user segments and a single legacy authentication system can be implemented in a single-phase IAM system rollout. -However, complex systems or risk-averse organizations may benefit from a multi-phased, application-based, or phased rollout approach. This strategy -allows you to test processes in production by first rolling out to less critical user segments or applications, learning valuable lessons to -refine your approach before a broader rollout. +Simpler systems with homogeneous user segments and a single legacy authentication system can be implemented in a single-phase IAM +system rollout. However, complex systems or risk-averse organizations may benefit from a multi-phased, application-based, or +phased rollout approach. This strategy allows you to test processes in production by first rolling out to less critical user +segments or applications, learning valuable lessons to refine your approach before a broader rollout. +- Higher risk concentration: If something goes wrong during deployment, it impacts your entire organization simultaneously rather + than being contained to a smaller group, potentially causing widespread business disruption. +- Support burden: Your support team must handle issues from all users at once, which can lead to long resolution times, frustrated + users, and inability to provide quality assistance when everyone needs help simultaneously. +- Limited learning opportunity: You can't incorporate lessons learned from early adopters because there are no early adopters—any + design flaws, training gaps, or technical issues only become apparent when it's too late to adjust your approach. +- Difficult rollback: Reverting to the old system after a full cutover can be complex and potentially impossible if you've already + decommissioned infrastructure or migrated data irreversibly. + ### Multi-phase rollout -A multi-phase approach involves migrating user identities and rolling out applications/services in phases, focusing on specific applications, -services, or user segments. This approach results in multiple "go-lives", each affecting a defined group of users or applications/services. -A multi-phase approach can employ bulk or graceful identity imigration strategies, or a hybrid of both. The key difference is that a -specific segment of users or authenication for a specific application or service is rolled out separately, each in its own specific phase. +A multi-phase approach involves migrating user identities and rolling out applications/services in phases, focusing on specific +applications, services, or user segments. This approach results in multiple "go-lives", each affecting a defined group of users or +applications/services. A multi-phase approach can employ bulk or graceful identity imigration strategies, or a hybrid of both. The +key difference is that a specific segment of users or authenication for a specific application or service is rolled out +separately, each in its own specific phase. A multi-phase rollout is the best choice in most cases, especially when: -- You manage multiple enterpise-level applications with different underlying authentication systems +- You manage multiple enterpise-level applications with different underlying authentication systems - You manage diverse segments of users with different underlying authentication systems - You have a complex system, and you want to reduce the risk of unknown consequences and improve the process iteratively. @@ -83,26 +85,25 @@ A multi-phase rollout is the best choice in most cases, especially when: - Reduced risk: By rolling out in phases, issues are isolated to specific apps or user segments. - Flexibility: Allows for adjustments and optimizations between phases based on lessons learned. -- Minimized/no downtime: Since the rollout occurs in stages, downtime can be limited to smaller user groups or avoided - completely. +- Minimized/no downtime: Since the rollout occurs in stages, downtime can be limited to smaller user groups or avoided completely. #### Drawbacks of multi-phase rollout - Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. - Extended timeline: The time to roll out the whole system takes longer as it is broken down into phases. - Resource demands: Running both systems in parallel during the transition can strain resources. - + ## Choose your user identity migration strategy -Ory supports two primary identity migration strategies: +Ory supports two primary identity migration strategies: - **Bulk identity migration** - Migrate all users at once. -- **Graceful identity migration** - Migrate when a user authenticates, running old and new solutions in parallel. +- **Graceful identity migration** - Migrate when a user authenticates, running old and new solutions in parallel. -By understanding these methods and planning accordingly, you can ensure a smooth and secure transition for your users to -Ory Network. +By understanding these methods and planning accordingly, you can ensure a smooth and secure transition for your users to Ory +Network. + ### Graceful identity migration Graceful identity migration—also called automatic, trickle, just-in-time, or online migration—involves running both the old and -new systems in parallel, gradually migrating user identities as the user authenticates. This approach features two "go-lives": the initial -application "go-live" and subsequent user-specific cutovers during login. +new systems in parallel, gradually migrating user identities as the user authenticates. This approach features two "go-lives": the +initial application "go-live" and subsequent user-specific cutovers during login. #### When to use graceful identity migration + A graceful identity migration is a good choice when: - You don't have access to hashed credentials or they are hashed with a proprietary algorithm @@ -159,7 +162,8 @@ A graceful identity migration is a good choice when: ##### Advantages of graceful identity migration -- Low risk: The gradual identity migration reduces the risk of widespread issues, as only a few users are affected at any given time. +- Low risk: The gradual identity migration reduces the risk of widespread issues, as only a few users are affected at any given + time. - No hashed credentials needed: Users are migrated during their "normal" authentication process, so you don't need to import credentials. Great if you don't have access to the hashed credentials. - No downtime: Both authentication systems operate simultaneously until the identity migration is completed. @@ -167,10 +171,11 @@ A graceful identity migration is a good choice when: ##### Drawbacks of graceful identity migration - Extended migration period: The process takes longer as users are migrated individually over time. -- Increased complexity: Maintaining synchronization between two systems adds complexity to the migration process. (You need to -ensure identites are synchronized in both systems in case a rollback is required.) In addition, a graceful migration requires -additional coding effort to implement Ory's [password migration using a web hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook). -- Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the old and new systems during the - transition period. - - \ No newline at end of file +- Increased complexity: Maintaining synchronization between two systems adds complexity to the migration process. (You need to + ensure identites are synchronized in both systems in case a rollback is required.) In addition, a graceful migration requires + additional coding effort to implement Ory's + [password migration using a web hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook). +- Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the old and new systems during + the transition period. + + diff --git a/docs/migrate-to-ory/migrate/migrate-to-ory.mdx b/docs/migrate-to-ory/migrate/migrate-to-ory.mdx index d05884a97..9625362ed 100644 --- a/docs/migrate-to-ory/migrate/migrate-to-ory.mdx +++ b/docs/migrate-to-ory/migrate/migrate-to-ory.mdx @@ -4,24 +4,26 @@ title: Understand your current identity management system sidebar_label: Understand your current identity management system sidebar_position: 1 --- + # Understand your current IAM system -Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific IAM needs and -understand the unique requirements of each approach. Each scenario differs in complexity and implementation needs. Use these IAM -scenarios to map the identity flows for your application. +Below are example IAM scenarios supported by Ory Network. Use them to identify which scenario best fits your specific IAM needs +and understand the unique requirements of each approach. Each scenario differs in complexity and implementation needs. Use these +IAM scenarios to map the identity flows for your application. ## Identify your IAM scenario + - [CIAM](#ciam-customer-identity-and-access-management) - [B2B](#b2b-business-to-business) - [Workforce](#workforce-business-to-enterprise) - [Agentic AI](#agentic-ai) - ### CIAM (Customer Identity and Access Management) Your company sells products or services directly to individual consumers. #### Key IAM requirements + - Self-service registration, login, and profile management for end users - Social login, multi-factor passwordless options, and robust account recovery - Privacy compliance (GDPR, CCPA) @@ -29,8 +31,8 @@ Your company sells products or services directly to individual consumers. ### B2B (Business-to-Business) -Your company sells products or services directly to other businesses rather than individual consumers. Your customers are organizations -that use these products or services to run their own operations. +Your company sells products or services directly to other businesses rather than individual consumers. Your customers are +organizations that use these products or services to run their own operations. #### Key IAM requirements @@ -43,11 +45,13 @@ that use these products or services to run their own operations. ### Workforce (Business-to-Enterprise) -Your company provides products or services. Your company wants to manage access for a single organization's extended workforce. You want to -consolidate employee user accounts and identities across multi-tenant brands, applications and systems. You need to seamlessly connect with -existing enterprise identity providers and other 3rd party systems, and streamline user onboarding, offboarding, and permission management. +Your company provides products or services. Your company wants to manage access for a single organization's extended workforce. +You want to consolidate employee user accounts and identities across multi-tenant brands, applications and systems. You need to +seamlessly connect with existing enterprise identity providers and other 3rd party systems, and streamline user onboarding, +offboarding, and permission management. #### Key IAM requirements + - Streamline onboarding/offboarding of employee, contractor, and temporary workers - Role-based access aligned with organizational hierarchy - HR system integration with flexible identity schemas @@ -59,30 +63,33 @@ existing enterprise identity providers and other 3rd party systems, and streamli ### Agentic AI -Your company wants to enable AI applications to securely connect to data sources and tools. For example, servers that host resources and -clients (AI applications) that discover and use those resources. +Your company wants to enable AI applications to securely connect to data sources and tools. For example, servers that host +resources and clients (AI applications) that discover and use those resources. #### Key IAM requirements + - Standardized protocol that works across many tools and data sources - Built-in authentication and access control ## Map all identity flows in your application -Build a complete picture of every identity-related process in your system. Use your IAM scenario’s Key IAM requirements to identify -these flows. This ensures you don’t miss critical flows during migration. +Build a complete picture of every identity-related process in your system. Use your IAM scenario’s Key IAM requirements to +identify these flows. This ensures you don’t miss critical flows during migration. -1. Identify all entry points where an identity-related process occurs (e.g., web app login, mobile app sign-in, API tokens, social or enterprise sign-ins). +1. Identify all entry points where an identity-related process occurs (e.g., web app login, mobile app sign-in, API tokens, social + or enterprise sign-ins). 1. Create a comprehensive inventory of flows, for example: - - Registration/sign-up - - Sign-in/sign-out - - Multi-Factor Authentication (MFA) - - Password reset and account recovery - - Account linking (social, enterprise logins) - - User profile management - - Token refresh and session handling - - Recovery flows, consent screens, or partner-specific integrations + - Registration/sign-up + - Sign-in/sign-out + - Multi-Factor Authentication (MFA) + - Password reset and account recovery + - Account linking (social, enterprise logins) + - User profile management + - Token refresh and session handling + - Recovery flows, consent screens, or partner-specific integrations 1. Create flow diagrams (sequence diagrams or flow charts) to surface dependencies and hidden complexity. -1. Note where identity-related (authentication and authorization) processes interact with other systems (databases, CRMs, partner apps, or external APIs). +1. Note where identity-related (authentication and authorization) processes interact with other systems (databases, CRMs, partner + apps, or external APIs). At the end of this process you should have a living document with diagrams that capture: diff --git a/docs/migrate-to-ory/migrate/test-validate.mdx b/docs/migrate-to-ory/migrate/test-validate.mdx index a73e6a71e..1568cf003 100644 --- a/docs/migrate-to-ory/migrate/test-validate.mdx +++ b/docs/migrate-to-ory/migrate/test-validate.mdx @@ -4,12 +4,12 @@ title: Test and validate your integration sidebar_label: Test and validate your integration sidebar_position: 1 --- -Before going live with your Ory Network integration, thorough testing across multiple environments is essential -to ensure your authentication system works securely and reliably. You should test progressively through development, -staging, and production environments, validating that configurations, custom domains, and integrations work correctly -at each stage. -## Create a comprehensive list of testing requirements +Before going live with your Ory Network integration, thorough testing across multiple environments is essential to ensure your +authentication system works securely and reliably. You should test progressively through development, staging, and production +environments, validating that configurations, custom domains, and integrations work correctly at each stage. + +## Create a comprehensive list of testing requirements Your testing should cover: @@ -28,5 +28,5 @@ Your validation should cover: - Compare user counts between systems (should match within acceptable margin) - Validate authentication flows for representative sample users - Verify all business logic continues to function correctly -- Conduct user acceptance testing with representative user groups to ensure the migrated system meets usability -requirements and maintains expected functionality. \ No newline at end of file +- Conduct user acceptance testing with representative user groups to ensure the migrated system meets usability requirements and + maintains expected functionality. diff --git a/src/sidebar.ts b/src/sidebar.ts index 931d5c0e6..437b08253 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -209,11 +209,11 @@ const quickstart: SidebarItemsConfig = [ collapsed: true, collapsible: true, items: [ - "migrate-to-ory/migrate/index", - "migrate-to-ory/migrate/map-to-orycap", - "migrate-to-ory/migrate/migrate-strategies", - //"migrate-to-ory/migrate/faq-migrate", - ] + "migrate-to-ory/migrate/index", + "migrate-to-ory/migrate/map-to-orycap", + "migrate-to-ory/migrate/migrate-strategies", + //"migrate-to-ory/migrate/faq-migrate", + ], }, { type: "category", @@ -221,30 +221,26 @@ const quickstart: SidebarItemsConfig = [ collapsed: true, collapsible: true, items: [ - "migrate-to-ory/migrate/create-project", - "migrate-to-ory/migrate/design-id-schema", - "migrate-to-ory/migrate/integrate-frontend", - "migrate-to-ory/migrate/integrate-backend", - "migrate-to-ory/migrate/migrate-identities", - ] + "migrate-to-ory/migrate/create-project", + "migrate-to-ory/migrate/design-id-schema", + "migrate-to-ory/migrate/integrate-frontend", + "migrate-to-ory/migrate/integrate-backend", + "migrate-to-ory/migrate/migrate-identities", + ], }, { type: "category", label: "Phase 3: Test & validate", collapsed: true, collapsible: true, - items: [ - "migrate-to-ory/migrate/test-validate", - ] + items: ["migrate-to-ory/migrate/test-validate"], }, { type: "category", label: "Phase 4: Go live", collapsed: true, collapsible: true, - items: [ - "migrate-to-ory/migrate/go-live", - ] + items: ["migrate-to-ory/migrate/go-live"], }, ], }, From 364d7ccf4e81e97125e3cc652e79df0e6971a97b Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Mon, 10 Nov 2025 14:33:25 -0800 Subject: [PATCH 09/32] chore: formatting fixes --- docs/migrate-to-ory/migrate/index.mdx | 6 +++--- docs/migrate-to-ory/migrate/migrate-strategies.mdx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index 928189f52..14d972237 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -60,7 +60,7 @@ organizations that use these products or services to run their own operations. - Role-based permissions and API controls - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users - + ### Workforce (Business-to-Enterprise) @@ -80,7 +80,7 @@ offboarding, and permission management. - Zero-trust security, MFA, and SSO for enterprise applications - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users - + ### Agentic AI @@ -92,7 +92,7 @@ resources and clients (AI applications) that discover and use those resources. - Standardized protocol that works across many tools and data sources - Built-in authentication and access control - + ## Map all identity flows in your application diff --git a/docs/migrate-to-ory/migrate/migrate-strategies.mdx b/docs/migrate-to-ory/migrate/migrate-strategies.mdx index 2df392a31..df8d87b3d 100644 --- a/docs/migrate-to-ory/migrate/migrate-strategies.mdx +++ b/docs/migrate-to-ory/migrate/migrate-strategies.mdx @@ -92,7 +92,7 @@ A multi-phase rollout is the best choice in most cases, especially when: - Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. - Extended timeline: The time to roll out the whole system takes longer as it is broken down into phases. - Resource demands: Running both systems in parallel during the transition can strain resources. - + ## Choose your user identity migration strategy @@ -142,7 +142,7 @@ A bulk identity migration can involve some risk and downtime, but it is recommen - Downtime: This approach may require planned system downtime to ensure data consistency, which can disrupt users. - Increased preparation: Requires extensive planning and testing to mitigate risks, making it more resource-intensive during that phase. - + ### Graceful identity migration @@ -177,5 +177,5 @@ A graceful identity migration is a good choice when: [password migration using a web hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook). - Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the old and new systems during the transition period. - + From e1fba139ebf001cb8943fd56707b6062e9a81fa9 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Mon, 10 Nov 2025 14:42:42 -0800 Subject: [PATCH 10/32] chore: fixed tabs again --- docs/migrate-to-ory/migrate/index.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index 14d972237..bd44769f6 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -60,7 +60,7 @@ organizations that use these products or services to run their own operations. - Role-based permissions and API controls - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users - + ### Workforce (Business-to-Enterprise) @@ -80,7 +80,7 @@ offboarding, and permission management. - Zero-trust security, MFA, and SSO for enterprise applications - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users - + ### Agentic AI @@ -92,7 +92,7 @@ resources and clients (AI applications) that discover and use those resources. - Standardized protocol that works across many tools and data sources - Built-in authentication and access control - + ## Map all identity flows in your application From 1466d44e970e842f773741b4df3f25ae99941a01 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Wed, 12 Nov 2025 08:09:12 -0800 Subject: [PATCH 11/32] chore: formatting fixes --- docs/migrate-to-ory/migrate/index.mdx | 6 +++--- .../migrate-to-ory/migrate/migrate-strategies.mdx | 15 ++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index bd44769f6..928189f52 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -60,7 +60,7 @@ organizations that use these products or services to run their own operations. - Role-based permissions and API controls - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users - + ### Workforce (Business-to-Enterprise) @@ -80,7 +80,7 @@ offboarding, and permission management. - Zero-trust security, MFA, and SSO for enterprise applications - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users - + ### Agentic AI @@ -92,7 +92,7 @@ resources and clients (AI applications) that discover and use those resources. - Standardized protocol that works across many tools and data sources - Built-in authentication and access control - + ## Map all identity flows in your application diff --git a/docs/migrate-to-ory/migrate/migrate-strategies.mdx b/docs/migrate-to-ory/migrate/migrate-strategies.mdx index df8d87b3d..865da4728 100644 --- a/docs/migrate-to-ory/migrate/migrate-strategies.mdx +++ b/docs/migrate-to-ory/migrate/migrate-strategies.mdx @@ -91,9 +91,8 @@ A multi-phase rollout is the best choice in most cases, especially when: - Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. - Extended timeline: The time to roll out the whole system takes longer as it is broken down into phases. -- Resource demands: Running both systems in parallel during the transition can strain resources. - - +- Resource demands: Running both systems in parallel during the transition can strain resources. + ## Choose your user identity migration strategy @@ -141,9 +140,8 @@ A bulk identity migration can involve some risk and downtime, but it is recommen perform a rollback. - Downtime: This approach may require planned system downtime to ensure data consistency, which can disrupt users. - Increased preparation: Requires extensive planning and testing to mitigate risks, making it more resource-intensive during that - phase. - - + phase. + ### Graceful identity migration @@ -176,6 +174,5 @@ A graceful identity migration is a good choice when: additional coding effort to implement Ory's [password migration using a web hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook). - Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the old and new systems during - the transition period. - - + the transition period. + From da5ed2eee7af0c95b848e29183dde2832226a82c Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Wed, 12 Nov 2025 08:15:24 -0800 Subject: [PATCH 12/32] chore: fix formating --- docs/migrate-to-ory/migrate/index.mdx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index 928189f52..8203bfc3b 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -31,6 +31,7 @@ IAM scenarios to map the identity flows for your application. {label: 'Workforce', value: 'Workforce'}, {label: 'Agentic AI', value: 'Agentic AI'}, ]}> + ### CIAM (Customer Identity and Access Management) @@ -45,6 +46,7 @@ Your company sells products or services directly to individual consumers. - High-scale performance for millions of users + ### B2B (Business-to-Business) @@ -60,7 +62,9 @@ organizations that use these products or services to run their own operations. - Role-based permissions and API controls - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users - + + + ### Workforce (Business-to-Enterprise) @@ -80,7 +84,9 @@ offboarding, and permission management. - Zero-trust security, MFA, and SSO for enterprise applications - Privacy compliance (GDPR, CCPA) - High-scale performance for millions of users - + + + ### Agentic AI @@ -92,7 +98,9 @@ resources and clients (AI applications) that discover and use those resources. - Standardized protocol that works across many tools and data sources - Built-in authentication and access control - + + + ## Map all identity flows in your application From 3a281866bd9a401d545cebafaede144fc7f97344 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Wed, 12 Nov 2025 08:23:57 -0800 Subject: [PATCH 13/32] chore: fix formatting --- .../migrate/migrate-strategies.mdx | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/docs/migrate-to-ory/migrate/migrate-strategies.mdx b/docs/migrate-to-ory/migrate/migrate-strategies.mdx index 865da4728..308ba63d7 100644 --- a/docs/migrate-to-ory/migrate/migrate-strategies.mdx +++ b/docs/migrate-to-ory/migrate/migrate-strategies.mdx @@ -30,6 +30,7 @@ segments or applications, learning valuable lessons to refine your approach befo {label: 'Single-phase rollout', value: 'single'}, {label: 'Multi-phase rollout', value: 'multi'}, ]}> + ### Single-phase rollout @@ -64,9 +65,11 @@ A single-phase rollout approach is the best choice when: design flaws, training gaps, or technical issues only become apparent when it's too late to adjust your approach. - Difficult rollback: Reverting to the old system after a full cutover can be complex and potentially impossible if you've already decommissioned infrastructure or migrated data irreversibly. - + + + ### Multi-phase rollout A multi-phase approach involves migrating user identities and rolling out applications/services in phases, focusing on specific @@ -91,8 +94,11 @@ A multi-phase rollout is the best choice in most cases, especially when: - Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. - Extended timeline: The time to roll out the whole system takes longer as it is broken down into phases. -- Resource demands: Running both systems in parallel during the transition can strain resources. - +- Resource demands: Running both systems in parallel during the transition can strain resources. + + + + ## Choose your user identity migration strategy @@ -110,6 +116,7 @@ Network. {label: 'Bulk identity migration', value: 'bulk'}, {label: 'Graceful identity migration', value: 'graceful'}, ]}> + ### Bulk identity migration @@ -140,7 +147,10 @@ A bulk identity migration can involve some risk and downtime, but it is recommen perform a rollback. - Downtime: This approach may require planned system downtime to ensure data consistency, which can disrupt users. - Increased preparation: Requires extensive planning and testing to mitigate risks, making it more resource-intensive during that - phase. + phase. + + + ### Graceful identity migration @@ -174,5 +184,8 @@ A graceful identity migration is a good choice when: additional coding effort to implement Ory's [password migration using a web hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook). - Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the old and new systems during - the transition period. + the transition period. + + + From 52a48744df4e42503e0ef8623af0c51724f14fe5 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Wed, 12 Nov 2025 08:28:54 -0800 Subject: [PATCH 14/32] chore: fix formating again --- .../migrate/migrate-strategies.mdx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/migrate-to-ory/migrate/migrate-strategies.mdx b/docs/migrate-to-ory/migrate/migrate-strategies.mdx index 308ba63d7..714367e12 100644 --- a/docs/migrate-to-ory/migrate/migrate-strategies.mdx +++ b/docs/migrate-to-ory/migrate/migrate-strategies.mdx @@ -94,7 +94,7 @@ A multi-phase rollout is the best choice in most cases, especially when: - Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. - Extended timeline: The time to roll out the whole system takes longer as it is broken down into phases. -- Resource demands: Running both systems in parallel during the transition can strain resources. +- Resource demands: Running both systems in parallel during the transition can strain resources. @@ -147,11 +147,11 @@ A bulk identity migration can involve some risk and downtime, but it is recommen perform a rollback. - Downtime: This approach may require planned system downtime to ensure data consistency, which can disrupt users. - Increased preparation: Requires extensive planning and testing to mitigate risks, making it more resource-intensive during that - phase. - - + phase. - + + + ### Graceful identity migration @@ -184,8 +184,8 @@ A graceful identity migration is a good choice when: additional coding effort to implement Ory's [password migration using a web hook](https://www.ory.com/docs/kratos/manage-identities/import-user-accounts-identities#password-migration-using-a-web-hook). - Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the old and new systems during - the transition period. - - - - + the transition period. + + + + From 103a3137346afc57a54a27df3a20e5339d848788 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Wed, 12 Nov 2025 09:39:20 -0800 Subject: [PATCH 15/32] Update docs/migrate-to-ory/migrate/migrate-strategies.mdx Co-authored-by: Jonas Hungershausen --- docs/migrate-to-ory/migrate/migrate-strategies.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/migrate-to-ory/migrate/migrate-strategies.mdx b/docs/migrate-to-ory/migrate/migrate-strategies.mdx index 714367e12..2a6a77a66 100644 --- a/docs/migrate-to-ory/migrate/migrate-strategies.mdx +++ b/docs/migrate-to-ory/migrate/migrate-strategies.mdx @@ -74,7 +74,7 @@ A single-phase rollout approach is the best choice when: A multi-phase approach involves migrating user identities and rolling out applications/services in phases, focusing on specific applications, services, or user segments. This approach results in multiple "go-lives", each affecting a defined group of users or -applications/services. A multi-phase approach can employ bulk or graceful identity imigration strategies, or a hybrid of both. The +applications/services. A multi-phase approach can employ bulk or graceful identity migration strategies, or a hybrid of both. The key difference is that a specific segment of users or authenication for a specific application or service is rolled out separately, each in its own specific phase. From c690685569deca13aad4e1518710574c4edb4708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Wa=C5=82ach?= Date: Mon, 10 Nov 2025 15:17:57 +0100 Subject: [PATCH 16/32] docs: switch to calendar-based unified versioning (#2326) * docs: switch to calendar-based unified versioning * Update docs/ecosystem/upgrading.mdx * Update docs/ecosystem/upgrading.mdx * Update docs/ecosystem/upgrading.mdx * Update docs/ecosystem/upgrading.mdx * Update docs/ecosystem/upgrading.mdx * Update docs/ecosystem/upgrading.mdx * chore: format --------- Co-authored-by: unatasha8 Co-authored-by: vinckr --- docs/ecosystem/upgrading.mdx | 43 ++++++------------------------------ 1 file changed, 7 insertions(+), 36 deletions(-) diff --git a/docs/ecosystem/upgrading.mdx b/docs/ecosystem/upgrading.mdx index 2ee422474..e437960dd 100644 --- a/docs/ecosystem/upgrading.mdx +++ b/docs/ecosystem/upgrading.mdx @@ -26,39 +26,10 @@ Before upgrading to a newer version, please make sure to check with these docume ## Versioning -The Ory ecosystem consists of multiple services versioned using [semantic versioning](https://semver.org). This section explains -how we define service versions and what they mean. - -## Development stages - -:::important - -**Ory only releases software that is stable and ready for production!** -The sandbox/incubating stage is an indicator of how much the API could change in the future, including backward incompatible -changes. - -Ory was founded in 2015, secures more than 50B requests monthly and is the most trusted open source ecosystem for authentication & -authorization. - -::: - -There are three main stages of development for services: - -- **Graduated:** Mature implementations of proven concepts. They rarely change in backwards incompatible ways. A software is - considered graduated if the major version is >= 1, for example `v1.0.1`, `v2.2.2`. Backwards incompatible changes are indicated - by a bump of the major version number. Most, if not all, REST APIs will provide backwards compatible transformations that make - it possible to interact with the server using older API versions. -- **Incubating:** Implements well defined but not fully matured concepts. Incubating software has a major version number of `0`, - for example `v0.10.0`. You may see a pre-release version such as `v0.10.0-beta.1`. Incubating software has a higher (but overall - moderate) probability for larger changes that can break backwards compatibility, for which there are upgrade guides. -- **Sandbox:** Implements concepts, APIs and CLIs at the experimental stage and may change in unpredictable ways. Sandbox software - has a major version number of `0` with a `alpha` or `beta` pre-release indicator, for example `v0.10.0-alpha.1`. It's more - likely that you'll encounter a version tag with a `alpha` pre-release version. We will provide upgrade guides wherever possible, - when they're used in production already. - -The following is a list of maturity level per project: - -- [Ory Hydra](https://github.com/ory/hydra) is a **graduated** project. -- [Ory Kratos](https://github.com/ory/kratos) is a **graduated** project. -- [Ory Oathkeeper](https://github.com/ory/oathkeeper) is an **incubating** project. -- [Ory Keto](https://github.com/ory/keto) is a **sandbox** project. +Ory uses a calendar-based product versioning system, where the version format is `YY.Q.N`, representing the year, quarter, and +release number within that quarter. For example, version `26.2.0` indicates a product release in the second quarter of 2026, with +`0` being the first release in that quarter. The release number increases with each subsequent release during the quarter. With +this system you can understand when a version was released and how recent it released. Starting from October 2025, all Ory +components use a common versioning number, meaning releases are synchronized across all components such as Ory Kratos, Ory Hydra, +Ory Oathkeeper, and Ory Keto. This unified versioning simplifies upgrades and ensures compatibility across the entire Ory +ecosystem. From 329399e0b6c1d0d075b9e61defcc3b860cb6df31 Mon Sep 17 00:00:00 2001 From: Nikos Sklikas Date: Mon, 10 Nov 2025 18:06:50 +0200 Subject: [PATCH 17/32] docs: add device flow documentation (#2026) * docs: add device flow documentation * chore: Refine device verification UI section wording * chore: Update user code entropy configuration details Clarified the description of user code entropy options and their implications for user entry. * chore: Revise Device Authorization Grant documentation Updated the description and steps for the Device Authorization Grant to clarify the process and correct terminology. * chore: Update device authorization flow steps and formatting * chore: Added image for device authorization flow Added an image to illustrate the device authorization flow. * chore: Fix image path for device authorization flow * chore: Integrate Mermaid diagram for device authorization flow Added a sequence diagram to illustrate the device authorization flow using Mermaid. * Update docs/oauth2-oidc/device-authorization.mdx Co-authored-by: Patrik * Update docs/oauth2-oidc/device-authorization.mdx Co-authored-by: Patrik * Update docs/oauth2-oidc/device-authorization.mdx Co-authored-by: Patrik * Update docs/oauth2-oidc/device-authorization.mdx Co-authored-by: Patrik * Update docs/oauth2-oidc/device-authorization.mdx Co-authored-by: Patrik * Update docs/oauth2-oidc/device-authorization.mdx Co-authored-by: Patrik * chore: address review comments and format * chore: add to sidebar * chore: fix grammar and typos --------- Co-authored-by: unatasha8 Co-authored-by: Patrik Co-authored-by: vinckr --- docs/oauth2-oidc/device-authorization.mdx | 184 ++++++++++++++++++++++ src/sidebar.ts | 1 + 2 files changed, 185 insertions(+) create mode 100644 docs/oauth2-oidc/device-authorization.mdx diff --git a/docs/oauth2-oidc/device-authorization.mdx b/docs/oauth2-oidc/device-authorization.mdx new file mode 100644 index 000000000..fc8a90170 --- /dev/null +++ b/docs/oauth2-oidc/device-authorization.mdx @@ -0,0 +1,184 @@ +--- +id: device-authorization +title: Device Authorization +sidebar_label: Device authorization flow +--- + +The OAuth 2.0 Device Authorization Grant (RFC 8628) brings OAuth to devices with internet connectivity but limited input +capabilities. This flow is designed for smart TVs, streaming devices, IoT hardware, printers, remote terminal sessions, AI agents, +and other connected devices where typing credentials or opening a browser isn't practical or possible. Here's how it works: the +device to be authenticated displays a URL and a short code, prompting you to open that URL on your phone or computer to authorize +access. After successful authorization, the device will get an access and (optionally) a refresh token. The two devices don't need +to communicate directly; the authorization happens through the OAuth provider. + +This document provides an overview of the Ory's device authorization grant flow, with a step-by-step example of its +implementation, configuration options, and guidance on creating custom user interfaces for the verification screen. + +## Overview of the flow + +Here is the high-level overview for the device authorization grant flow: + +1. The user attempts to log in to the device. This initiates the device to request authorization from the authorization server. +1. When the authorization server responds, the user is instructed to visit a URL and enter the provided user code, which they do + on a different device. +1. On the different device the user visits the URL, enters the user code, logs in, and grants access to the device. +1. In the meantime, the device polls the authorization server. Once the user authenticates and grants access, the authentication + server sends an access token to the device, which is used to access the protected resource. + +```mdx-code-block +import Mermaid from "@site/src/theme/Mermaid"; + +>+AS: Start device code grant + AS-->>-D: Verification URI, Device-code, User-code + loop background poll before user authorized + D->>+AS: Request Token by device code + AS-->>-D: Error + end + D->>+U: Ask visit verification URI
Reveal User-code + U->>+AS: Request verification URI + AS-->>-U: Prompt for User-code + U->>+AS: Sumbit User-code + AS-->>-U: Prompt for login and consent + U->>+AS: Submit credentials and consent + AS-->>-U: Show success + deactivate U + loop background poll after user authorized + D->>+AS: Request Token by device code + end + AS-->>-D: Token + D-->>D: Process and store token + deactivate D +`} /> +``` + +### Step 1: Device requests authorization + +The user attempts to log in through the limited input device. The device sends a POST request to the authorization server to +initiate the flow with the following parameters: + +- `client_id`: The ID of the client (device) that's making the request +- `scope` (optional): The scope of the access request, which specifies which resources the requesting device can access + +The authorization server responds with the following information: + +- `device_code`: A unique code to identify the authorization request +- `user_code`: A code the user enters at the verification URL +- `verification_uri`: The URL where the user authorizes the device +- `verification_uri_complete`: The URL where the user authorizes the device, with the user_code already filled in +- `expires_in`: The lifespan of the device code (in seconds) +- `interval`: The polling interval (in seconds) for the client to check if the user has authorized the device yet + +### Step 2: Display user code and verification URI + +The device shows the user the `user_code` and `verification_uri` it received from the authorization server. Depending on the +device, this can be in the form of a URL, QR code, acoustically, or any other form that the device can communicate with the user. + +### Step 3: User grants permission + +The user visits the provided URI on a separate device, such as a phone, and enters the code. Once the user enters the code, the +user is prompted to log in, if not already authenticated, and grants or denies permission to the client (device). After granting +permission, the user is redirected to a page confirming they are successfully logged in. + +### Step 4: Device polls for the access token + +While the user is authorizing the device, the device polls the `token` endpoint of the authorization server to check whether the +user has completed the authorization process by making a POST request with the following parameters: + +- `client_id`: The ID of the client that's making the request +- `device_code`: The device code returned from the authorization request +- `grant_type`: This must always be `urn:ietf:params:oauth:grant-type:device_code` + +After the user grants their consent, the authentication server sends an access token to the device, which is used to access the +protected resource. + +## Configuration options + +### Configure the user interface + +To enable and configure the device authorization grant in Ory Hydra, adjust the following settings in your configuration file: + +```yaml +urls: + device: + # The verification UI is where the user inputs the user-code + verification: http://path/to/device/verification/ui + # The success UI is where the user is sent to after successful authorization + success: http://path/to/device/success +``` + +### Configure user code entropy + +Depending on your security needs and your traffic load, you should choose the appropriate `user_code` entropy. The +`oauth2.device_authorization.user_code.entropy_preset` configuration supports 3 values: + +- `high`: `user_code` is 8 characters long and consists of alphanumeric characters, excluding some ambiguous symbols +- `medium`: `user_code` is 8 characters long and consists of only upper case alphabetic characters +- `low`: `user_code` is 9 characters long and consists of only numeric characters + +It is also possible to configure the length and character set directly: + +```yaml +oauth2: + device_authorization: + user_code: + length: 8 + character_set: abcdefghijklmnopqrstuvwxyz0123456789 +``` + +It is important to strike the right balance between security and user experience here. Higher entropy enhances security and +protects against an attacker randomly guessing valid user-codes. This is especially important when more concurrent device flows +are being performed. As users will need to manually enter the user code, the higher the entropy, the more difficult it will be for +the user to enter the user code. For a better user experience, ambiguous characters should be avoided, for example `O` and `0` on +any display, or `1` and `7` on a 7-segment display. This isn't of any concern when the user doesn't need to input the user-code +manually, for example when scanning a QR code. + +## Device verification UI implementation + +Here is a sample UI implementation for device verification: + +```js +import { Configuration, OAuth2Api } from "@ory/client" +import { Request, Response } from "express" + +const ory = new OAuth2Api( + new Configuration({ + basePath: `https://${process.env.ORY_PROJECT_SLUG}.projects.oryapis.com`, + accessToken: process.env.ORY_API_KEY, + }), +) + +// Please note that this is an example implementation. +// In a production app, please add proper error handling. +export async function handleLogin(request: Request, response: Response) { + const challenge = request.query.device_challenge.toString() + const userCode = request.query.user_code.toString() + + // Show the login form if the form was not submitted. + if (request.method === "GET") { + response.render("device", { + challenge, + userCode, + }) + return + } + + // User was authenticated successfully, + return await ory + .acceptUserCodeRequest({ + deviceChallenge: challenge, + acceptDeviceUserCodeRequest: { + user_code: userCode, + }, + }) + .then(({ redirect_to }) => { + response.redirect(String(redirect_to)) + }) +} +``` diff --git a/src/sidebar.ts b/src/sidebar.ts index adb5aa4c8..67d150835 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -701,6 +701,7 @@ const hydra: SidebarItemsConfig = [ items: [ "oauth2-oidc/authorization-code-flow", "oauth2-oidc/client-credentials", + "oauth2-oidc/device-authorization", "oauth2-oidc/resource-owner-password-grant", "oauth2-oidc/refresh-token-grant", "oauth2-oidc/userinfo-oidc", From be44bcdf1651c4c8267ac87d4c6128c37b6f01bd Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 10 Nov 2025 18:53:37 -0300 Subject: [PATCH 18/32] fix: high-perf pooling doc (#2332) * fix: high-perf pooling doc * chore: apply suggestion from code review Co-authored-by: hackerman <3372410+aeneasr@users.noreply.github.com> * chore: apply suggestion from code review Co-authored-by: hackerman <3372410+aeneasr@users.noreply.github.com> * chore: apply suggestions from code review Co-authored-by: Arne Luenser * chore: omit slop --------- Co-authored-by: hackerman <3372410+aeneasr@users.noreply.github.com> Co-authored-by: Arne Luenser --- docs/self-hosted/deployment.md | 26 +---------- .../oel/oel-high-performance-pooling.mdx | 44 +++++++++++++++++++ src/sidebar.ts | 1 + 3 files changed, 47 insertions(+), 24 deletions(-) create mode 100644 docs/self-hosted/oel/oel-high-performance-pooling.mdx diff --git a/docs/self-hosted/deployment.md b/docs/self-hosted/deployment.md index 9a3063381..51cbb5fb2 100644 --- a/docs/self-hosted/deployment.md +++ b/docs/self-hosted/deployment.md @@ -70,30 +70,8 @@ DSN=postgres://user:password@host:123/database?sslmode=verify-full ##### High-performance pooling -:::note - -High-performance pooling is supported in Ory Enterprise License (OEL) images. - -::: - -High-performance pooling is built using [pgxpool](https://pkg.go.dev/github.com/jackc/pgx/v5/pgxpool) and provides additional -configuration options to the ones listed under "Standard pooling". - -To activate high-performance pooling, you must set at least the `pool_min_conns` parameter; otherwise, high-performance pooling -will not be enabled. - -- `pool_min_conns` (number): The minimum number of total (in-use and idle) database connections to keep open at all times. After a - connection closes, the pool may dip below `pool_min_conns` momentarily. Defaults to 0. -- `pool_max_conns` (number): Sets the maximum number of open connections to the database. Overrides `max_conns`. -- `pool_max_conn_idle_time` (duration: for example "500ms", "5s", "30m", "1h"): Database connections will be closed after idling - for this duration. Overrides `max_conn_idle_time`. -- `pool_max_conn_lifetime` (duration: for example "500ms", "5s", "30m", "1h"): Sets the time after which a connection will be - closed, irrespective of how long it has been idle. Overrides `max_conn_lifetime`. -- `pool_max_conn_lifetime_jitter` (duration: for example "500ms", "5s", "30m", "1h"): Jitter to add to the - `pool_max_conn_lifetime` value. This is useful to avoid thundering herd problems when many connections are closed and re-opened - at the same time. -- `pool_health_check_period` (duration: for example "500ms", "5s", "30m", "1h"): Sets the period for health checks to potentially - kill stale connections. Defaults to "1m". +High-performance pooling is supported in Ory Enterprise License (OEL) images. Read more about it in the +[high-performance pooling](./oel/high-performance-pooling) documentation. ### CockroachDB diff --git a/docs/self-hosted/oel/oel-high-performance-pooling.mdx b/docs/self-hosted/oel/oel-high-performance-pooling.mdx new file mode 100644 index 000000000..a948b9881 --- /dev/null +++ b/docs/self-hosted/oel/oel-high-performance-pooling.mdx @@ -0,0 +1,44 @@ +--- +id: high-performance-pooling +title: High-performance database connection pooling +sidebar_label: High-performance pooling +--- + +High-performance pooling is available for CockroachDB and PostgreSQL with an Ory Enterprise License (OEL). It provides additional +configuration options for managing database connections under variable load. + +To activate high-performance pooling, you must set the `pool_min_conns` parameter, otherwise high-performance pooling will not be +enabled. + +- `pool_min_conns` (number): The minimum number of total (in-use and idle) database connections to keep open at all times. After a + connection closes, the pool may dip below `pool_min_conns` momentarily. Defaults to 0. +- `pool_max_conns` (number): Sets the maximum number of open connections to the database. Overrides `max_conns`. +- `pool_max_conn_idle_time` (duration: for example "500ms", "5s", "30m", "1h"): Database connections will be closed after idling + for this duration. Overrides `max_conn_idle_time`. +- `pool_max_conn_lifetime` (duration: for example "500ms", "5s", "30m", "1h"): Sets the time after which a connection will be + closed, irrespective of how long it has been idle. Overrides `max_conn_lifetime`. +- `pool_max_conn_lifetime_jitter` (duration: for example "500ms", "5s", "30m", "1h"): Jitter to add to the + `pool_max_conn_lifetime` value. This is useful to avoid thundering herd problems when many connections are closed and re-opened + at the same time. +- `pool_health_check_period` (duration: for example "500ms", "5s", "30m", "1h"): Sets the period for health checks to potentially + kill stale connections. Defaults to "1m". + +## When to use high-performance pooling + +Standard pooling opens connections on demand and closes them after idle timeout, which can cause connection storms during sudden +traffic spikes. High-performance pooling maintains `min_pool` persistent connections and includes refresh jitter to prevent +synchronized resets, without initialization overhead and reducing the risk of database overload during demand surges. + +Consider high-performance pooling when your workload exhibits: + +- Large, unpredictable traffic spikes +- Sudden transitions from low to high request volume +- Time-sensitive operations where connection initialization latency is problematic + +For steady-state traffic or gradual load changes, standard pooling may be enough. + +## When not to use high-performance pooling + +High-performance pooling does not reload TLS certificates while the process is running. If database TLS certificates change, you +must restart the Ory service to establish connections using the new certificates. Standard pooling supports hot reloading of TLS +certificates because connections close after idle timeout and reconnect with refreshed credentials. diff --git a/src/sidebar.ts b/src/sidebar.ts index 67d150835..b13e419c0 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -1261,6 +1261,7 @@ const oel: SidebarItemsConfig = [ items: ["self-hosted/oel/polis/changelog"], }, "self-hosted/oel/monitoring/monitoring", + "self-hosted/oel/high-performance-pooling", ] const security: SidebarItemsConfig = [ From bcd03b39a02e2a6bbb9452b068a55c56c0d04fcc Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Mon, 10 Nov 2025 23:04:03 +0100 Subject: [PATCH 19/32] chore(docs): update of OEL images (#2337) chore(docs): update OEL image tag --- docs/self-hosted/oel/keto/changelog/v25.4.0.md | 1 + docs/self-hosted/oel/kratos/changelog/v25.4.0.md | 12 ++++++++++++ docs/self-hosted/oel/oathkeeper/changelog/v25.4.0.md | 1 + docs/self-hosted/oel/oauth2/changelog/v25.4.0.md | 1 + docs/self-hosted/oel/oel-hydra-image-tags.md | 1 + docs/self-hosted/oel/oel-keto-image-tags.md | 1 + docs/self-hosted/oel/oel-kratos-image-tags.md | 1 + docs/self-hosted/oel/oel-oathkeeper-image-tags.md | 1 + docs/self-hosted/oel/oel-polis-image-tags.md | 1 + docs/self-hosted/oel/polis/changelog/v25.4.0.md | 1 + 10 files changed, 21 insertions(+) create mode 100644 docs/self-hosted/oel/keto/changelog/v25.4.0.md create mode 100644 docs/self-hosted/oel/kratos/changelog/v25.4.0.md create mode 100644 docs/self-hosted/oel/oathkeeper/changelog/v25.4.0.md create mode 100644 docs/self-hosted/oel/oauth2/changelog/v25.4.0.md create mode 100644 docs/self-hosted/oel/polis/changelog/v25.4.0.md diff --git a/docs/self-hosted/oel/keto/changelog/v25.4.0.md b/docs/self-hosted/oel/keto/changelog/v25.4.0.md new file mode 100644 index 000000000..b366cfca4 --- /dev/null +++ b/docs/self-hosted/oel/keto/changelog/v25.4.0.md @@ -0,0 +1 @@ +No changelog entries found for keto/oel in versions v25.4.0 diff --git a/docs/self-hosted/oel/kratos/changelog/v25.4.0.md b/docs/self-hosted/oel/kratos/changelog/v25.4.0.md new file mode 100644 index 000000000..7ca826419 --- /dev/null +++ b/docs/self-hosted/oel/kratos/changelog/v25.4.0.md @@ -0,0 +1,12 @@ +## v25.4.0 + +### Session now available in settings after hook + +When using the `After Hook` feature in Ory Kratos, the session is now accessible in the webhook's body context. This enhancement +allows developers to access session information directly within the after hook, enabling more dynamic and context-aware settings +webhooks. + +### Significantly faster identity updates + +This patch improves the performance of identity updates by optimizing database queries and reducing redundant operations. Users +will experience faster response times when signing up, updating their profiles, or changing credentials. diff --git a/docs/self-hosted/oel/oathkeeper/changelog/v25.4.0.md b/docs/self-hosted/oel/oathkeeper/changelog/v25.4.0.md new file mode 100644 index 000000000..e0e926e85 --- /dev/null +++ b/docs/self-hosted/oel/oathkeeper/changelog/v25.4.0.md @@ -0,0 +1 @@ +No changelog entries found for oathkeeper/oel in versions v25.4.0 diff --git a/docs/self-hosted/oel/oauth2/changelog/v25.4.0.md b/docs/self-hosted/oel/oauth2/changelog/v25.4.0.md new file mode 100644 index 000000000..935c45169 --- /dev/null +++ b/docs/self-hosted/oel/oauth2/changelog/v25.4.0.md @@ -0,0 +1 @@ +No changelog entries found for hydra/oel in versions v25.4.0 diff --git a/docs/self-hosted/oel/oel-hydra-image-tags.md b/docs/self-hosted/oel/oel-hydra-image-tags.md index feffa34d4..d559414b6 100644 --- a/docs/self-hosted/oel/oel-hydra-image-tags.md +++ b/docs/self-hosted/oel/oel-hydra-image-tags.md @@ -1,5 +1,6 @@ | Image Tag | Release Date | | ---------------------------------------- | ------------ | +| 25.4.0 | 2025-11-07 | | 25.3.9 | 2025-10-30 | | 25.3.8 | 2025-10-23 | | 25.3.6 | 2025-10-10 | diff --git a/docs/self-hosted/oel/oel-keto-image-tags.md b/docs/self-hosted/oel/oel-keto-image-tags.md index ead0b1ece..72e4bdc24 100644 --- a/docs/self-hosted/oel/oel-keto-image-tags.md +++ b/docs/self-hosted/oel/oel-keto-image-tags.md @@ -1,5 +1,6 @@ | Image Tag | Release Date | | ---------------------------------------- | ------------ | +| 25.4.0 | 2025-11-07 | | 25.3.9 | 2025-10-30 | | 25.3.8 | 2025-10-23 | | 25.3.6 | 2025-10-10 | diff --git a/docs/self-hosted/oel/oel-kratos-image-tags.md b/docs/self-hosted/oel/oel-kratos-image-tags.md index d438cbcb3..3368a0d52 100644 --- a/docs/self-hosted/oel/oel-kratos-image-tags.md +++ b/docs/self-hosted/oel/oel-kratos-image-tags.md @@ -1,5 +1,6 @@ | Image Tag | Release Date | | ---------------------------------------- | ------------ | +| 25.4.0 | 2025-11-07 | | 25.3.9 | 2025-10-30 | | 25.3.8 | 2025-10-23 | | 25.3.6 | 2025-10-10 | diff --git a/docs/self-hosted/oel/oel-oathkeeper-image-tags.md b/docs/self-hosted/oel/oel-oathkeeper-image-tags.md index f36f2a40a..3b68e09dc 100644 --- a/docs/self-hosted/oel/oel-oathkeeper-image-tags.md +++ b/docs/self-hosted/oel/oel-oathkeeper-image-tags.md @@ -1,5 +1,6 @@ | Image Tag | Release Date | | ---------------------------------------- | ------------ | +| 25.4.0 | 2025-11-07 | | 25.3.9 | 2025-10-30 | | 25.3.8 | 2025-10-23 | | 25.3.6 | 2025-10-10 | diff --git a/docs/self-hosted/oel/oel-polis-image-tags.md b/docs/self-hosted/oel/oel-polis-image-tags.md index 6c4f9efdb..9c913f0b2 100644 --- a/docs/self-hosted/oel/oel-polis-image-tags.md +++ b/docs/self-hosted/oel/oel-polis-image-tags.md @@ -1,5 +1,6 @@ | Image Tag | Release Date | | ---------------------------------------- | ------------ | +| 25.4.0 | 2025-11-07 | | 25.3.9 | 2025-10-30 | | 25.3.8 | 2025-10-23 | | 25.3.6 | 2025-10-10 | diff --git a/docs/self-hosted/oel/polis/changelog/v25.4.0.md b/docs/self-hosted/oel/polis/changelog/v25.4.0.md new file mode 100644 index 000000000..bd4a94c72 --- /dev/null +++ b/docs/self-hosted/oel/polis/changelog/v25.4.0.md @@ -0,0 +1 @@ +No changelog entries found for polis/oel in versions v25.4.0 From ed75f81617acf03600ee807de5a7f5f75586ef82 Mon Sep 17 00:00:00 2001 From: ory-bot <60093411+ory-bot@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:46:51 +0000 Subject: [PATCH 20/32] autogen(docs): generate cli docs --- docs/keto/cli/keto-check.md | 6 +++--- docs/keto/cli/keto-expand.md | 6 +++--- docs/keto/cli/keto-migrate-down.md | 6 +++--- docs/keto/cli/keto-migrate-status.md | 6 +++--- docs/keto/cli/keto-migrate-up.md | 6 +++--- docs/keto/cli/keto-migrate.md | 6 +++--- docs/keto/cli/keto-relation-tuple-create.md | 6 +++--- docs/keto/cli/keto-relation-tuple-delete-all.md | 6 +++--- docs/keto/cli/keto-relation-tuple-delete.md | 6 +++--- docs/keto/cli/keto-relation-tuple-get.md | 6 +++--- docs/keto/cli/keto-relation-tuple-parse.md | 6 +++--- docs/keto/cli/keto-relation-tuple.md | 6 +++--- docs/keto/cli/keto-serve.md | 6 +++--- docs/keto/cli/keto-status.md | 6 +++--- docs/keto/cli/keto-version.md | 6 +++--- docs/keto/cli/keto.md | 4 ++-- 16 files changed, 47 insertions(+), 47 deletions(-) diff --git a/docs/keto/cli/keto-check.md b/docs/keto/cli/keto-check.md index 4674de8cd..df46486c3 100644 --- a/docs/keto/cli/keto-check.md +++ b/docs/keto/cli/keto-check.md @@ -1,7 +1,7 @@ --- id: keto-check title: keto check -description: keto check Check whether a subject has a relation on an object +description: keto check --- diff --git a/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/AndroidManifest.xml b/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/AndroidManifest.xml index c951df04f..afa52c821 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/AndroidManifest.xml +++ b/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/AndroidManifest.xml @@ -1,13 +1,13 @@ - - + + + + + + + + diff --git a/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/kotlin/com/example/flutter_web/MainActivity.kt b/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/kotlin/com/example/flutter_web/MainActivity.kt deleted file mode 100644 index e4168aece..000000000 --- a/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/kotlin/com/example/flutter_web/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.flutter_web - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/kotlin/com/example/flutter_web_redirect/MainActivity.kt b/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/kotlin/com/example/flutter_web_redirect/MainActivity.kt new file mode 100644 index 000000000..08e3a4101 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/kotlin/com/example/flutter_web_redirect/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.flutter_web_redirect + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/res/values-night/styles.xml b/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/res/values-night/styles.xml index 3db14bb53..06952be74 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/res/values-night/styles.xml +++ b/code-examples/protect-page-login/flutter_web_redirect/android/app/src/main/res/values-night/styles.xml @@ -3,7 +3,7 @@ diff --git a/code-examples/protect-page-login/flutter_web_redirect/android/build.gradle b/code-examples/protect-page-login/flutter_web_redirect/android/build.gradle deleted file mode 100644 index 24047dce5..000000000 --- a/code-examples/protect-page-login/flutter_web_redirect/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.3.50' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/code-examples/protect-page-login/flutter_web_redirect/android/build.gradle.kts b/code-examples/protect-page-login/flutter_web_redirect/android/build.gradle.kts new file mode 100644 index 000000000..89176ef44 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/android/build.gradle.kts @@ -0,0 +1,21 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/android/gradle.properties b/code-examples/protect-page-login/flutter_web_redirect/android/gradle.properties index 94adc3a3f..f018a6181 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/android/gradle.properties +++ b/code-examples/protect-page-login/flutter_web_redirect/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true diff --git a/code-examples/protect-page-login/flutter_web_redirect/android/gradle/wrapper/gradle-wrapper.properties b/code-examples/protect-page-login/flutter_web_redirect/android/gradle/wrapper/gradle-wrapper.properties index bc6a58afd..ac3b47926 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/android/gradle/wrapper/gradle-wrapper.properties +++ b/code-examples/protect-page-login/flutter_web_redirect/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip diff --git a/code-examples/protect-page-login/flutter_web_redirect/android/settings.gradle b/code-examples/protect-page-login/flutter_web_redirect/android/settings.gradle deleted file mode 100644 index 44e62bcf0..000000000 --- a/code-examples/protect-page-login/flutter_web_redirect/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/code-examples/protect-page-login/flutter_web_redirect/android/settings.gradle.kts b/code-examples/protect-page-login/flutter_web_redirect/android/settings.gradle.kts new file mode 100644 index 000000000..ab39a10a2 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/android/settings.gradle.kts @@ -0,0 +1,25 @@ +pluginManagement { + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.7.3" apply false + id("org.jetbrains.kotlin.android") version "2.1.0" apply false +} + +include(":app") diff --git a/code-examples/protect-page-login/flutter_web_redirect/env b/code-examples/protect-page-login/flutter_web_redirect/env deleted file mode 100644 index 709adc1f6..000000000 --- a/code-examples/protect-page-login/flutter_web_redirect/env +++ /dev/null @@ -1 +0,0 @@ -ORY_BASE_URL=http://localhost:3005 \ No newline at end of file diff --git a/code-examples/protect-page-login/flutter_web_redirect/ios/Flutter/AppFrameworkInfo.plist b/code-examples/protect-page-login/flutter_web_redirect/ios/Flutter/AppFrameworkInfo.plist index 8d4492f97..7c5696400 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/ios/Flutter/AppFrameworkInfo.plist +++ b/code-examples/protect-page-login/flutter_web_redirect/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 12.0 diff --git a/code-examples/protect-page-login/flutter_web_redirect/ios/Runner.xcodeproj/project.pbxproj b/code-examples/protect-page-login/flutter_web_redirect/ios/Runner.xcodeproj/project.pbxproj index 057631095..98a1b2be8 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/ios/Runner.xcodeproj/project.pbxproj +++ b/code-examples/protect-page-login/flutter_web_redirect/ios/Runner.xcodeproj/project.pbxproj @@ -3,11 +3,12 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -15,6 +16,16 @@ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -31,6 +42,8 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -55,6 +68,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -72,6 +93,7 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, ); sourceTree = ""; }; @@ -79,6 +101,7 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -101,6 +124,23 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; @@ -127,9 +167,14 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 1100; @@ -150,11 +195,19 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -171,10 +224,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -185,6 +240,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -200,6 +256,14 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -211,6 +275,14 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -235,6 +307,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -264,6 +337,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -272,7 +346,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -294,7 +368,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWeb; + PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWebRedirect; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -302,10 +376,58 @@ }; name = Profile; }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWebRedirect.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWebRedirect.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWebRedirect.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -335,6 +457,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -349,7 +472,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -361,6 +484,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -390,6 +514,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -398,7 +523,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -422,7 +547,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWeb; + PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWebRedirect; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -444,7 +569,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWeb; + PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWebRedirect; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -455,6 +580,16 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/code-examples/protect-page-login/flutter_web_redirect/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/code-examples/protect-page-login/flutter_web_redirect/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a33..e3773d42e 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/code-examples/protect-page-login/flutter_web_redirect/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + + + + diff --git a/code-examples/protect-page-login/flutter_web_redirect/ios/Runner/AppDelegate.swift b/code-examples/protect-page-login/flutter_web_redirect/ios/Runner/AppDelegate.swift index 70693e4a8..626664468 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/ios/Runner/AppDelegate.swift +++ b/code-examples/protect-page-login/flutter_web_redirect/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ -import UIKit import Flutter +import UIKit -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/code-examples/protect-page-login/flutter_web_redirect/ios/Runner/Info.plist b/code-examples/protect-page-login/flutter_web_redirect/ios/Runner/Info.plist index d6b38772f..2e7f3e64a 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/ios/Runner/Info.plist +++ b/code-examples/protect-page-login/flutter_web_redirect/ios/Runner/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Flutter Web + Flutter Web Redirect CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -13,7 +13,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - flutter_web + flutter_web_redirect CFBundlePackageType APPL CFBundleShortVersionString @@ -41,7 +41,9 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents diff --git a/code-examples/protect-page-login/flutter_web_redirect/ios/RunnerTests/RunnerTests.swift b/code-examples/protect-page-login/flutter_web_redirect/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000..86a7c3b1b --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/lib/main.dart b/code-examples/protect-page-login/flutter_web_redirect/lib/main.dart index 02e75d611..62b533dd0 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/lib/main.dart +++ b/code-examples/protect-page-login/flutter_web_redirect/lib/main.dart @@ -1,6 +1,3 @@ -// Copyright © 2022 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - import 'package:universal_html/html.dart'; import 'package:dio/browser.dart'; import 'package:dio/dio.dart'; @@ -10,7 +7,7 @@ import 'package:flutter_web_redirect/services/auth.dart'; Future main() async { // load the env file - await dotenv.load(fileName: "env"); + await dotenv.load(fileName: ".env"); final baseUrl = dotenv.get("ORY_BASE_URL").toString(); @@ -19,9 +16,7 @@ Future main() async { baseUrl: baseUrl, connectTimeout: const Duration(seconds: 10000), receiveTimeout: const Duration(seconds: 5000), - headers: { - "Accept": "application/json", - }, + headers: {"Accept": "application/json"}, validateStatus: (status) { // here we prevent the request from throwing an error when the status code is less than 500 (internal server error) return status! < 500; @@ -53,16 +48,14 @@ class MyApp extends StatelessWidget { final AuthService auth; const MyApp({Key? key, required this.dio, required this.auth}) - : super(key: key); + : super(key: key); // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), + theme: ThemeData(primarySwatch: Colors.blue), home: MyHomePage(title: 'Ory ❤ Flutter Web', auth: auth), ); } @@ -70,7 +63,7 @@ class MyApp extends StatelessWidget { class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, required this.title, required this.auth}) - : super(key: key); + : super(key: key); // This widget is the home page of your application. It is stateful, meaning // that it has a State object (defined below) that contains fields that affect @@ -123,7 +116,9 @@ class _MyHomePageState extends State { children: [ Text('Session Information:${widget.auth.identity.toString()}'), TextButton( - onPressed: widget.auth.logout, child: const Text('Logout')), + onPressed: widget.auth.logout, + child: const Text('Logout'), + ), ], ), ), diff --git a/code-examples/protect-page-login/flutter_web_redirect/lib/services/auth.dart b/code-examples/protect-page-login/flutter_web_redirect/lib/services/auth.dart index 36a00be84..cb6dcf3f3 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/lib/services/auth.dart +++ b/code-examples/protect-page-login/flutter_web_redirect/lib/services/auth.dart @@ -1,5 +1,3 @@ -// Copyright © 2022 Ory Corp -// SPDX-License-Identifier: Apache-2.0 import 'package:universal_html/html.dart'; import 'package:dio/dio.dart'; @@ -12,15 +10,18 @@ class AuthService { AuthService(Dio dio) : _ory = OryClient(dio: dio).getFrontendApi(); Future isAuthenticated() async { - return _ory.toSession().then((resp) { - if (resp.statusCode == 200) { - _identity = resp.data; - return true; - } - return false; - }).catchError((error) { - return false; - }); + return _ory + .toSession() + .then((resp) { + if (resp.statusCode == 200) { + _identity = resp.data; + return true; + } + return false; + }) + .catchError((error) { + return false; + }); } Future logout() async { diff --git a/code-examples/protect-page-login/flutter_web_redirect/linux/.gitignore b/code-examples/protect-page-login/flutter_web_redirect/linux/.gitignore new file mode 100644 index 000000000..d3896c984 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/code-examples/protect-page-login/flutter_web_redirect/linux/CMakeLists.txt b/code-examples/protect-page-login/flutter_web_redirect/linux/CMakeLists.txt new file mode 100644 index 000000000..aecbcb56d --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/linux/CMakeLists.txt @@ -0,0 +1,128 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "flutter_web_redirect") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.flutter_web_redirect") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/code-examples/protect-page-login/flutter_web_redirect/linux/flutter/CMakeLists.txt b/code-examples/protect-page-login/flutter_web_redirect/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000..d5bd01648 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/code-examples/protect-page-login/flutter_web_redirect/linux/flutter/generated_plugin_registrant.cc b/code-examples/protect-page-login/flutter_web_redirect/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..e71a16d23 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/linux/flutter/generated_plugin_registrant.h b/code-examples/protect-page-login/flutter_web_redirect/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..e0f0a47bc --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/code-examples/protect-page-login/flutter_web_redirect/linux/flutter/generated_plugins.cmake b/code-examples/protect-page-login/flutter_web_redirect/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000..2e1de87a7 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/code-examples/protect-page-login/flutter_web_redirect/linux/runner/CMakeLists.txt b/code-examples/protect-page-login/flutter_web_redirect/linux/runner/CMakeLists.txt new file mode 100644 index 000000000..e97dabc70 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/linux/runner/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the application ID. +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") diff --git a/code-examples/protect-page-login/flutter_web_redirect/linux/runner/main.cc b/code-examples/protect-page-login/flutter_web_redirect/linux/runner/main.cc new file mode 100644 index 000000000..e7c5c5437 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/linux/runner/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/linux/runner/my_application.cc b/code-examples/protect-page-login/flutter_web_redirect/linux/runner/my_application.cc new file mode 100644 index 000000000..5e7496ce0 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/linux/runner/my_application.cc @@ -0,0 +1,130 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "flutter_web_redirect"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "flutter_web_redirect"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + // Set the program name to the application ID, which helps various systems + // like GTK and desktop environments map this running application to its + // corresponding .desktop file. This ensures better integration by allowing + // the application to be recognized beyond its binary name. + g_set_prgname(APPLICATION_ID); + + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/linux/runner/my_application.h b/code-examples/protect-page-login/flutter_web_redirect/linux/runner/my_application.h new file mode 100644 index 000000000..72271d5e4 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/linux/runner/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/.gitignore b/code-examples/protect-page-login/flutter_web_redirect/macos/.gitignore new file mode 100644 index 000000000..746adbb6b --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Flutter/Flutter-Debug.xcconfig b/code-examples/protect-page-login/flutter_web_redirect/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Flutter/Flutter-Release.xcconfig b/code-examples/protect-page-login/flutter_web_redirect/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Flutter/GeneratedPluginRegistrant.swift b/code-examples/protect-page-login/flutter_web_redirect/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 000000000..cccf817a5 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,10 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcodeproj/project.pbxproj b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..894a01012 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,705 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* flutter_web_redirect.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "flutter_web_redirect.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* flutter_web_redirect.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* flutter_web_redirect.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWebRedirect.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/flutter_web_redirect.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/flutter_web_redirect"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWebRedirect.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/flutter_web_redirect.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/flutter_web_redirect"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWebRedirect.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/flutter_web_redirect.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/flutter_web_redirect"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..4a9225d5d --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcworkspace/contents.xcworkspacedata b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/AppDelegate.swift b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000..b3c176141 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..60e60c947 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images": [ + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_16.png", + "scale": "1x" + }, + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "2x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "1x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_64.png", + "scale": "2x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_128.png", + "scale": "1x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "2x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "1x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "2x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "1x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_1024.png", + "scale": "2x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..82b6f9d9a33e198f5747104729e1fcef999772a5 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY literal 0 HcmV?d00001 diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..13b35eba55c6dabc3aac36f33d859266c18fa0d0 GIT binary patch literal 5680 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 literal 0 HcmV?d00001 diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f5fa40fb3d1e0710331a48de5d256da3f275d GIT binary patch literal 520 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 literal 0 HcmV?d00001 diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..2f1632cfddf3d9dade342351e627a0a75609fb46 GIT binary patch literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Configs/AppInfo.xcconfig b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000..0f17dec35 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = flutter_web_redirect + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterWebRedirect + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved. diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Configs/Debug.xcconfig b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000..36b0fd946 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Configs/Release.xcconfig b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000..dff4f4956 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Configs/Warnings.xcconfig b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000..42bcbf478 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/DebugProfile.entitlements b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000..dddb8a30c --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Info.plist b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Info.plist new file mode 100644 index 000000000..4789daa6a --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/MainFlutterWindow.swift b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000..3cc05eb23 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Release.entitlements b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Release.entitlements new file mode 100644 index 000000000..852fa1a47 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/code-examples/protect-page-login/flutter_web_redirect/macos/RunnerTests/RunnerTests.swift b/code-examples/protect-page-login/flutter_web_redirect/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000..61f3bd1fc --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/pubspec.lock b/code-examples/protect-page-login/flutter_web_redirect/pubspec.lock index 3d8839a35..0ff1779a5 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/pubspec.lock +++ b/code-examples/protect-page-login/flutter_web_redirect/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.13.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" built_collection: dependency: transitive description: @@ -29,82 +29,90 @@ packages: dependency: transitive description: name: built_value - sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166" + sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d url: "https://pub.dev" source: hosted - version: "8.6.1" + version: "8.12.0" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" charcode: dependency: transitive description: name: charcode - sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" csslib: dependency: transitive description: name: csslib - sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f" + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" url: "https://pub.dev" source: hosted - version: "0.17.3" + version: "1.0.2" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.8" dio: dependency: "direct main" description: name: dio - sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197 + sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9 url: "https://pub.dev" source: hosted - version: "5.3.2" + version: "5.9.0" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" fake_async: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" fixnum: dependency: transitive description: name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -114,18 +122,18 @@ packages: dependency: "direct main" description: name: flutter_dotenv - sha256: "9357883bdd153ab78cbf9ffa07656e336b8bbb2b5a3ca596b0b27e119f7c7d77" + sha256: d4130c4a43e0b13fefc593bc3961f2cb46e30cb79e253d4a526b1b5d24ae1ce4 url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "6.0.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "6.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -135,58 +143,58 @@ packages: dependency: transitive description: name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" url: "https://pub.dev" source: hosted - version: "0.15.4" + version: "0.15.6" http_parser: dependency: transitive description: name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.1.2" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "6.0.0" matcher: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -199,10 +207,18 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "2.0.0" one_of: dependency: transitive description: @@ -223,26 +239,26 @@ packages: dependency: "direct main" description: name: ory_client - sha256: "6cee3dc3ff90527e9b7e9897546201842660a2f5cef90828d75973d114bcac7a" + sha256: "2e061fd9daf40d0b60f09fa94968f85b9ec5ec12d771bfc64c6e30d903963488" url: "https://pub.dev" source: hosted - version: "1.1.41" + version: "1.22.8" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" quiver: dependency: transitive description: name: quiver - sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" sky_engine: dependency: transitive description: flutter @@ -252,66 +268,66 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.6" typed_data: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" universal_html: dependency: "direct main" description: name: universal_html - sha256: a5cc5a84188e5d3e58f3ed77fe3dd4575dc1f68aa7c89e51b5b4105b9aab3b9d + sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.4" universal_io: dependency: transitive description: @@ -324,18 +340,26 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "15.0.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.8.1 <4.0.0" + flutter: ">=3.35.7" diff --git a/code-examples/protect-page-login/flutter_web_redirect/pubspec.yaml b/code-examples/protect-page-login/flutter_web_redirect/pubspec.yaml index 6de2bb50d..c44840839 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/pubspec.yaml +++ b/code-examples/protect-page-login/flutter_web_redirect/pubspec.yaml @@ -1,28 +1,28 @@ name: flutter_web_redirect -description: A Flutter Web App integrated with Ory. +description: "A Flutter Web App integrated with Ory." publish_to: "none" - version: 1.0.0+1 environment: - sdk: ">=3.0.6 <4.0.0" + sdk: ">=3.8.1 <4.0.0" + flutter: 3.35.7 dependencies: flutter: sdk: flutter - cupertino_icons: ^1.0.5 - dio: ^5.3.2 - flutter_dotenv: ^5.1.0 - ory_client: ^1.1.41 - universal_html: ^2.2.3 + cupertino_icons: ^1.0.8 + dio: ^5.8.0+1 + flutter_dotenv: ^6.0.0 + ory_client: ^1.22.8 + universal_html: ^2.2.4 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.2 + flutter_lints: ^6.0.0 flutter: uses-material-design: true # Load our env file from the root directory assets: - - env + - .env diff --git a/code-examples/protect-page-login/flutter_web_redirect/test/widget_test.dart b/code-examples/protect-page-login/flutter_web_redirect/test/widget_test.dart new file mode 100644 index 000000000..642df8420 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/test/widget_test.dart @@ -0,0 +1,34 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:dio/browser.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:flutter_web_redirect/main.dart'; +import 'package:flutter_web_redirect/services/auth.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + final dio = DioForBrowser(); + final auth = AuthService(dio); + await tester.pumpWidget(MyApp(dio: dio, auth: auth)); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/web/index.html b/code-examples/protect-page-login/flutter_web_redirect/web/index.html index 3e34e07bd..97cd7f98a 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/web/index.html +++ b/code-examples/protect-page-login/flutter_web_redirect/web/index.html @@ -21,84 +21,18 @@ - + - + - flutter_web + flutter_web_redirect - - + diff --git a/code-examples/protect-page-login/flutter_web_redirect/web/manifest.json b/code-examples/protect-page-login/flutter_web_redirect/web/manifest.json index a918af94b..9583f2462 100644 --- a/code-examples/protect-page-login/flutter_web_redirect/web/manifest.json +++ b/code-examples/protect-page-login/flutter_web_redirect/web/manifest.json @@ -1,6 +1,6 @@ { - "name": "flutter_web", - "short_name": "flutter_web", + "name": "flutter_web_redirect", + "short_name": "flutter_web_redirect", "start_url": ".", "display": "standalone", "background_color": "#0175C2", diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/.gitignore b/code-examples/protect-page-login/flutter_web_redirect/windows/.gitignore new file mode 100644 index 000000000..d492d0d98 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/CMakeLists.txt b/code-examples/protect-page-login/flutter_web_redirect/windows/CMakeLists.txt new file mode 100644 index 000000000..63828cdbe --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(flutter_web_redirect LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "flutter_web_redirect") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/flutter/CMakeLists.txt b/code-examples/protect-page-login/flutter_web_redirect/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000..903f4899d --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/flutter/generated_plugin_registrant.cc b/code-examples/protect-page-login/flutter_web_redirect/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..8b6d4680a --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/flutter/generated_plugin_registrant.h b/code-examples/protect-page-login/flutter_web_redirect/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..dc139d85a --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/flutter/generated_plugins.cmake b/code-examples/protect-page-login/flutter_web_redirect/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000..b93c4c30c --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/runner/CMakeLists.txt b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/CMakeLists.txt new file mode 100644 index 000000000..394917c05 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/runner/Runner.rc b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/Runner.rc new file mode 100644 index 000000000..82ff2012a --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "flutter_web_redirect" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "flutter_web_redirect" "\0" + VALUE "LegalCopyright", "Copyright (C) 2025 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "flutter_web_redirect.exe" "\0" + VALUE "ProductName", "flutter_web_redirect" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/runner/flutter_window.cpp b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/flutter_window.cpp new file mode 100644 index 000000000..955ee3038 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/runner/flutter_window.h b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/flutter_window.h new file mode 100644 index 000000000..6da0652f0 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/runner/main.cpp b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/main.cpp new file mode 100644 index 000000000..0406eaa44 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"flutter_web_redirect", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/runner/resource.h b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/resource.h new file mode 100644 index 000000000..66a65d1e4 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/runner/resources/app_icon.ico b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/runner/runner.exe.manifest b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/runner.exe.manifest new file mode 100644 index 000000000..153653e8d --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/runner/utils.cpp b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/utils.cpp new file mode 100644 index 000000000..3a0b46511 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/runner/utils.h b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/utils.h new file mode 100644 index 000000000..3879d5475 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/runner/win32_window.cpp b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/win32_window.cpp new file mode 100644 index 000000000..60608d0fe --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/code-examples/protect-page-login/flutter_web_redirect/windows/runner/win32_window.h b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/win32_window.h new file mode 100644 index 000000000..e901dde68 --- /dev/null +++ b/code-examples/protect-page-login/flutter_web_redirect/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/docs/getting-started/integrate-auth/20_flutter-web-redirect.mdx b/docs/getting-started/integrate-auth/20_flutter-web-redirect.mdx index 084de25bd..b623467e5 100644 --- a/docs/getting-started/integrate-auth/20_flutter-web-redirect.mdx +++ b/docs/getting-started/integrate-auth/20_flutter-web-redirect.mdx @@ -76,14 +76,15 @@ import auth from '!!raw-loader!../../../code-examples/protect-page-login/flutter ## Add environment variables Create a `.env` file in the root of the project to hold the `ORY_BASE_URL` variable. The value of the variable is the Ory Tunnel -URL, for example `http://localhost:3005`. +URL, for example `http://localhost:3000`. ```shell-session touch .env ``` ```mdx-code-block -import env from '!!raw-loader!../../../code-examples/protect-page-login/flutter_web_redirect/env' +import env from '!!raw-loader!../../../code-examples/protect-page-login/flutter_web_redirect/.env.example'; + {env} ``` From 40b9b3fe3a3cc9a9ebea6f513f4c5367623fe7a3 Mon Sep 17 00:00:00 2001 From: Mia Date: Wed, 12 Nov 2025 09:59:56 -0500 Subject: [PATCH 28/32] feat: add kratos webhook header allowlist config details (#2100) * feat: add kratos webhook header allowlist config details Signed-off-by: Mia * fix: wording Signed-off-by: Mia --------- Signed-off-by: Mia --- docs/guides/integrate-with-ory-cloud-through-webhooks.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/guides/integrate-with-ory-cloud-through-webhooks.mdx b/docs/guides/integrate-with-ory-cloud-through-webhooks.mdx index 94f42fac1..15a81f594 100644 --- a/docs/guides/integrate-with-ory-cloud-through-webhooks.mdx +++ b/docs/guides/integrate-with-ory-cloud-through-webhooks.mdx @@ -91,7 +91,7 @@ template as well. These objects are available through a `ctx` object. #### Accessing request headers -The following request headers are available via `ctx.request_headers`: +Request headers are available via `ctx.request_headers`. By default, only the following headers are exposed: ``` Accept @@ -113,6 +113,8 @@ True-Client-Ip User-Agent ``` +To customize allowed headers, use the `actions.web_hook.header_allowlist` configuration option. + #### Jsonnet templating To send `{ user_id: {some-id} }` in the request body, create the following the [Jsonnet](https://jsonnet.org) template: From 10ad7efe1f839bbca099e935a6d5dc0cc31069e6 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Tue, 7 Oct 2025 21:37:17 -0700 Subject: [PATCH 29/32] chore: migration guide updates --- docs/migrate-to-ory/migrate/create-project | 18 +++ docs/migrate-to-ory/migrate/define-id-schema | 21 ++++ docs/migrate-to-ory/migrate/go-live | 27 +++++ docs/migrate-to-ory/migrate/index.mdx | 1 - docs/migrate-to-ory/migrate/integrate-backend | 30 +++++ .../migrate-to-ory/migrate/integrate-frontend | 33 ++++++ .../migrate-to-ory/migrate/migrate-strategies | 106 ++++++++++++++++++ docs/migrate-to-ory/migrate/user-identities | 40 +++++++ 8 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 docs/migrate-to-ory/migrate/create-project create mode 100644 docs/migrate-to-ory/migrate/define-id-schema create mode 100644 docs/migrate-to-ory/migrate/go-live create mode 100644 docs/migrate-to-ory/migrate/integrate-backend create mode 100644 docs/migrate-to-ory/migrate/integrate-frontend create mode 100644 docs/migrate-to-ory/migrate/migrate-strategies create mode 100644 docs/migrate-to-ory/migrate/user-identities diff --git a/docs/migrate-to-ory/migrate/create-project b/docs/migrate-to-ory/migrate/create-project new file mode 100644 index 000000000..a0643ee97 --- /dev/null +++ b/docs/migrate-to-ory/migrate/create-project @@ -0,0 +1,18 @@ +--- +id: create-project +title: Create project +sidebar_label: Create project +sidebar_position: 3 +--- + +## Create Ory Network projects + +Now that you have chosen your migration strategy, you can begin the actual migration process by setting up your Ory Network +projects. This involves creating a new project environment where the migration will take place. + +You can create a new Ory Network project using the Ory CLI. The command ory create project allows you to specify the environment +of the project, the output format, the name of the project, and the workspace to use. More details about creating a project can be +found [here](../cli/ory-create-project). + +Before migrating your production environment, perform the migration in a development or staging environment. This allows you to +test and refine the process without affecting your live data or users. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/define-id-schema b/docs/migrate-to-ory/migrate/define-id-schema new file mode 100644 index 000000000..3e92a070b --- /dev/null +++ b/docs/migrate-to-ory/migrate/define-id-schema @@ -0,0 +1,21 @@ +--- +id: define-id-schema +title: Define identity schema +sidebar_label: Define identity schema +sidebar_position: 3 +--- + + +## Define identity schema + +To match identities from your current system with the new Ory system, you can customize the identity schema in Ory to meet your +specific requirements. This schema defines the types of data that the system can store for users, including names, email +addresses, phone numbers, and other authentication-related information. Additionally, you can specify extra metadata fields to be +included in user profiles. Ory offers default presets to assist you in creating and managing identity schemas. More details about +identity schemas can be found [here](../kratos/manage-identities/identity-schema). + +- Do store profile data in your identity that is used across your system. This includes the usernames, email addresses, phone + numbers, first names, and last names. +- Do not store business logic in your identity, store this information in other systems, such as your application database. This + includes for example credit card information, shipping addresses, shopping cart items, or user preferences. See also the + following section on backend integration. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/go-live b/docs/migrate-to-ory/migrate/go-live new file mode 100644 index 000000000..8ccc24868 --- /dev/null +++ b/docs/migrate-to-ory/migrate/go-live @@ -0,0 +1,27 @@ +--- +id: go-live +title: Go live +sidebar_label: Go live +sidebar_position: 2 +--- + + +# Go live + +After successfully migrating your data and testing the integration in your development or staging environment, it's time to go +live with Ory in your production environment. + +1. Final testing: Before switching over to Ory in production, conduct thorough testing of all user authentication flows, identity + management features, and access controls. This includes testing edge cases, error handling, and load testing to ensure the + system can handle your user base. +1. Prepare a rollback plan: In case any issues arise during the go-live process, have a rollback plan in place. This may involve + reverting to your previous authentication system or restoring data from backups. +1. Schedule the go-live: Choose a time for the go-live that minimizes the impact on your users, such as during low-traffic + periods. Communicate the planned migration to your users in advance, including any expected downtime or changes they should be + aware of. +1. Monitor the transition: As you switch over to Ory, closely monitor the system for any issues, such as failed authentications, + performance bottlenecks, or user complaints. Use live events to monitor the system under + . +1. Optimize and refine: After the go-live, continue to monitor the system and gather user feedback. + +Once your Ory integration is stable and users are successfully authenticating with the new system, your migration is complete. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index 8203bfc3b..b9ffcd686 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -39,7 +39,6 @@ IAM scenarios to map the identity flows for your application. Your company sells products or services directly to individual consumers. #### Key IAM requirements - - Self-service registration, login, and profile management for end users - Social login, multi-factor passwordless options, and robust account recovery - Privacy compliance (GDPR, CCPA) diff --git a/docs/migrate-to-ory/migrate/integrate-backend b/docs/migrate-to-ory/migrate/integrate-backend new file mode 100644 index 000000000..d870a8c20 --- /dev/null +++ b/docs/migrate-to-ory/migrate/integrate-backend @@ -0,0 +1,30 @@ +--- +id: integrate-backend +title: Integrate backend +sidebar_label: Integrate backend +sidebar_position: 1 +--- + +# Integrate backend + +When the frontend makes an API call to your backend, it will include the necessary cookies. Your backend must then forward these +cookies when calling the Ory API to validate the session. For example in a Go backend, you could use a +[middleware](../getting-started/integrate-auth/go#validate-and-login) to intercept API requests and validate the session by +calling Ory’s toSession() method. Ensure that the cookies received from the front end are forwarded in this call. Since backend +calls to Ory’s API won’t automatically include cookies, you must manually attach the relevant cookies to these requests. This is +important for the backend to be able to check the session. + +When using Ory to manage identities, it is best practice to store business logic in your application database and keep only +authentication-relevant data in Ory. Here’s a general approach: + +1. Configure [Ory Actions](../kratos/hooks/configure-hooks) to send webhooks to your server after user registration or other + identity-related events. The webhook payload will include the data of the newly created identity. +1. Upon receiving the webhook, your server can create a corresponding user record in your database. This allows your system to + link Ory-managed identities with your business logic. +1. Establish a connection between the Ory identity and the user record in your database by storing the `user.id` in + `identity.metadata_public.id`. This ensures that subsequent API calls can easily map the Ory identity to the correct internal + user. More about metadata in the [Identity metadata & traits ](../kratos/manage-identities/managing-users-identities-metadata) + documentation. +1. Now when the frontend makes API calls containing the Ory cookie or token, the backend should verify the session using the + whoami API endpoint. This endpoint returns the session details, including the identity, allowing the backend to authenticate + the request and link it to the internal user record. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/integrate-frontend b/docs/migrate-to-ory/migrate/integrate-frontend new file mode 100644 index 000000000..29abd4091 --- /dev/null +++ b/docs/migrate-to-ory/migrate/integrate-frontend @@ -0,0 +1,33 @@ +--- +id: integrate-frontend +title: Integrate frontend +sidebar_label: Integrate frontend +sidebar_position: 1 +--- + +# Integrate frontend + +To make authenticated API calls using Ory, start by properly configuring your domain and subdomains. By default the cookie domain +is set to the root domain (e.g., example.org) when you add a custom domain. This ensures that cookies can be shared across all +subdomains. + +- Example subdomain structure: + - Run Ory at auth.example.org. + - Host your backend API at api.example.org. + - Serve your frontend UI at www.example.org or another designated subdomain. + +This setup allows both your front end and back end to access the authentication session cookies managed by Ory. + +To begin integrating Ory into your frontend, it's helpful to start with the +["protect a page with login" guides](../getting-started/overview) that cover the basics of developing with Ory for various +programming languages and frameworks, including SDK usage and essential setup steps. + +Ory Network has two types of user interfaces. We recommend starting with the built-in +[Account Experience](../account-experience/index.mdx), which offers a standard user interface, covering all self-service flows +with the option to style branding to get you up and running. If you prefer a custom user interface that matches your current +design 1:1, Ory allows you to create and style a custom UI that integrates seamlessly with your existing setup. You can do this +using the API directly, the SDK for your language, or - if you are working in the React ecosystem - Ory Elements. Ory Elements is +a component library designed to make building login, registration, and account pages for Ory easy. It is modular and customizable, +allowing you to use only the components you need while tailoring them to fit your implementation's design. The UI created with Ory +Elements changes dynamically to adapt to your Ory Network configuration. More details about customizing the user interface with +Ory Elements can be found [here](../elements/index.mdx). \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/migrate-strategies b/docs/migrate-to-ory/migrate/migrate-strategies new file mode 100644 index 000000000..364e5363a --- /dev/null +++ b/docs/migrate-to-ory/migrate/migrate-strategies @@ -0,0 +1,106 @@ +--- +id: migrate-strategies +title: Migration strategies +sidebar_label: Migration strategies +sidebar_position: 2 +--- + +## Migration strategy + +When migrating user data from an old system to a new one, the process involves two main steps: transferring existing data and +"go-live" when users start authenticating with the new system. The choice of migration strategy depends on your specific use case, +the shape of existing data, and the number of "go-lives" you need to manage. + +- **Big bang** - Migrate everyone at once. +- **Stepped** - Migrate your applications or user segments individually. _This is the most common choice_. +- **Graceful** - Migrate when a user authenticates, running both solutions in parallel. + +Each migration strategy has its strengths and challenges. The ideal choice depends on factors such as the complexity of your +system, the number of users, and your organization's tolerance for risk and downtime. + +### Big bang migration + +In a big bang migration, also known as "offline migration", all user data is transferred at once, and a single "go-live" event is +scheduled, where all users start using the new system simultaneously. + +#### Advantages of big bang migration + +- Simplicity: Since there is only one "go-live", the migration process is easier to manage and plan. +- Time efficiency: The migration process happens in one go, reducing the time needed for the transition. +- Less complexity: There is no need to run two systems in parallel and you can retire the previous solution immediately. + +#### Drawbacks of big bang migration + +- High risk: If any issues occur during the cutover, the impact can be significant, affecting all users. +- Downtime: This approach may require planned system downtime to ensure data consistency, which can disrupt users. +- Increased preparation: Requires extensive planning and testing to mitigate risks, making it more resource-intensive during that + phase. + +A big bang migration is often not the best choice due to the risk and downtime, but it is recommended when + +- the number of users is low / app is simple +- downtime isn't a problem +- you need to retire the current solution yesterday + +### Stepwise migration + +Stepwise migration, also called "application-based migration", involves transferring user data in phases, focusing on specific +applications, services, or user segments at a time. This approach results in multiple "go-lives", each affecting a defined group +of users. + +#### Advantages of stepwise migration + +- Reduced risk: By migrating in phases, issues are isolated to specific apps or user segments. +- Flexibility: Allows for adjustments and optimizations between phases based on lessons learned. +- Minimized/no downtime: Since the migration occurs in stages, downtime can be limited to smaller user groups or avoided + completely. + +#### Drawbacks of stepwise migration + +- Complex management: Multiple "go-lives" require more coordination and detailed planning, increasing operational complexity. +- Extended timeline: The migration process takes longer as it is broken down into phases. +- Resource demands: Running both systems in parallel during the transition can strain resources. + +A stepwise migration is the best choice in most cases, especially when + +- you manage multiple apps/segments of users with different underlying auth systems +- downtime should be mostly avoided +- you have some time to migrate + +### Graceful migration + +Graceful migration - also called "slow migration", "rolling migration", or "online migration" - involves running both the old and +new systems in parallel, gradually migrating users as they authenticate. This approach features two "go-lives": the initial +application "go-live" and subsequent user-specific cutovers during login. + +#### Advantages of graceful migration + +- Low risk: The gradual transition reduces the risk of widespread issues, as only a few users are affected at any given time. +- No hashed credentials needed: Users are migrated during their "normal" authentication process, so you don't need to import + credentials. Great if you don't have access to the hashed credentials. +- No downtime: Both systems operate simultaneously until the migration is completed. + +#### Drawbacks of graceful migration + +- Extended migration period: The process takes longer as users are migrated individually over time. +- Increased complexity: Maintaining synchronization between two systems adds complexity to the migration process. +- Potential for data inconsistencies: If not carefully managed, there may be discrepancies between the two systems during the + transition period. + +A graceful migration is a good choice when + +- you don't have access to hashed credentials or they are hashed with a proprietary algorithm +- absolutely no downtime is acceptable +- running the current solution until the end of migration isn't a problem + +## Create Ory Network projects + +Now that you have chosen your migration strategy, you can begin the actual migration process by setting up your Ory Network +projects. This involves creating a new project environment where the migration will take place. + +You can create a new Ory Network project using the Ory CLI. The command ory create project allows you to specify the environment +of the project, the output format, the name of the project, and the workspace to use. More details about creating a project can be +found [here](../cli/ory-create-project). + +Before migrating your production environment, perform the migration in a development or staging environment. This allows you to +test and refine the process without affecting your live data or users. \ No newline at end of file diff --git a/docs/migrate-to-ory/migrate/user-identities b/docs/migrate-to-ory/migrate/user-identities new file mode 100644 index 000000000..dbc8801c6 --- /dev/null +++ b/docs/migrate-to-ory/migrate/user-identities @@ -0,0 +1,40 @@ +--- +id: user-identities +title: Migrate user identities +sidebar_label: Migrate user identities +sidebar_position: 3 +--- + +## Get existing user identities ready for migration +With authentication now set up on your front end and back end, the next step is to prepare your existing user identities for +migration. If you're using a managed identity solution, it’s a good idea to start the export process early, especially if there's +no straightforward way to export the identities and you might need to go through a support process. + +Export user data from your existing authentication solution or database and find out the hashing algorithm used to hash their +credentials. If your passwords are not hashed, you can bypass this step, as Ory will handle password hashing automatically during +the import process. Ory supports a range of hashing algorithms; if yours is supported, use the +[create identity API](../reference/api#tag/identity/operation/createIdentity) to import your users. If the hashing algorithm isn't +supported or if you can't get the hashed passwords from your current authentication system, you may want to do a "graceful" +migration and use the password migration hook to migrate your existing users. You can find more details in the +[Import identities ](../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. + +## Import user identities + +You can use the [create identity API](../reference/api#tag/identity/operation/batchPatchIdentities) to bulk import identities into +Ory. A maximum of 2000 identities can be created in a single request. If you need to import more identities, you need to split the +import into multiple requests. The endpoint accepts a JSON array of identities, each of which must have a create property that +holds the identity that should be created. You can find more details in the +[Import identities ](../kratos/manage-identities/25_import-user-accounts-identities.mdx) documentation. + +### Phased migration for active sessions + +Ory does not support the direct import of active sessions from your existing system. To ensure that users with active sessions +from the old system can continue accessing your services without needing to reauthenticate immediately, implement a transition +period during which both the old and new systems operate concurrently. During this period, all user authentication flows—including +login, registration, password recovery, and settings management—should be managed by Ory. However, your backend must be configured +to recognize and accept sessions from both the old system and Ory. + +As the transition progresses, gradually phase out the old system. Once most or all active sessions from the old system have +expired or been replaced by new sessions in Ory, you can complete the migration to Ory exclusively. This gradual approach +minimizes user disruption and provides your development team with the time needed to resolve any potential issues that may arise +during the migration. \ No newline at end of file From 41044fc90c050c3644eea050f5361fbbdff6563d Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Wed, 12 Nov 2025 11:43:52 -0800 Subject: [PATCH 30/32] chore: migration updates --- docs/migrate-to-ory/migrate/index.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index b9ffcd686..8203bfc3b 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -39,6 +39,7 @@ IAM scenarios to map the identity flows for your application. Your company sells products or services directly to individual consumers. #### Key IAM requirements + - Self-service registration, login, and profile management for end users - Social login, multi-factor passwordless options, and robust account recovery - Privacy compliance (GDPR, CCPA) From 6c55a3651109f6ddfde8b7419e1b0dd0bfe14e05 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Tue, 7 Oct 2025 21:37:17 -0700 Subject: [PATCH 31/32] chore: migration guide updates --- docs/migrate-to-ory/migrate/index.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index 8203bfc3b..b9ffcd686 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -39,7 +39,6 @@ IAM scenarios to map the identity flows for your application. Your company sells products or services directly to individual consumers. #### Key IAM requirements - - Self-service registration, login, and profile management for end users - Social login, multi-factor passwordless options, and robust account recovery - Privacy compliance (GDPR, CCPA) From 206cd772fa38854ec683d876ef26cc46d25f7288 Mon Sep 17 00:00:00 2001 From: unatasha8 Date: Wed, 12 Nov 2025 12:45:50 -0800 Subject: [PATCH 32/32] chore: update migration guide --- docs/migrate-to-ory/migrate/index.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/migrate-to-ory/migrate/index.mdx b/docs/migrate-to-ory/migrate/index.mdx index b9ffcd686..8203bfc3b 100644 --- a/docs/migrate-to-ory/migrate/index.mdx +++ b/docs/migrate-to-ory/migrate/index.mdx @@ -39,6 +39,7 @@ IAM scenarios to map the identity flows for your application. Your company sells products or services directly to individual consumers. #### Key IAM requirements + - Self-service registration, login, and profile management for end users - Social login, multi-factor passwordless options, and robust account recovery - Privacy compliance (GDPR, CCPA)